mirror of
				https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
				synced 2025-10-31 07:02:06 +00:00 
			
		
		
		
	Remove PAGE_BUG - repalce it with BUG and BUG_ON. Signed-off-by: Matt Mackall <mpm@selenic.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
		
			
				
	
	
		
			193 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			193 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  *  linux/arch/arm26/mm/small_page.c
 | |
|  *
 | |
|  *  Copyright (C) 1996  Russell King
 | |
|  *  Copyright (C) 2003  Ian Molton
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify
 | |
|  * it under the terms of the GNU General Public License version 2 as
 | |
|  * published by the Free Software Foundation.
 | |
|  *
 | |
|  *  Changelog:
 | |
|  *   26/01/1996	RMK	Cleaned up various areas to make little more generic
 | |
|  *   07/02/1999	RMK	Support added for 16K and 32K page sizes
 | |
|  *			containing 8K blocks
 | |
|  *   23/05/2004 IM	Fixed to use struct page->lru (thanks wli)
 | |
|  *
 | |
|  */
 | |
| #include <linux/signal.h>
 | |
| #include <linux/sched.h>
 | |
| #include <linux/kernel.h>
 | |
| #include <linux/errno.h>
 | |
| #include <linux/string.h>
 | |
| #include <linux/types.h>
 | |
| #include <linux/ptrace.h>
 | |
| #include <linux/mman.h>
 | |
| #include <linux/mm.h>
 | |
| #include <linux/swap.h>
 | |
| #include <linux/smp.h>
 | |
| #include <linux/bitops.h>
 | |
| 
 | |
| #include <asm/pgtable.h>
 | |
| 
 | |
| #define PEDANTIC
 | |
| 
 | |
| /*
 | |
|  * Requirement:
 | |
|  *  We need to be able to allocate naturally aligned memory of finer
 | |
|  *  granularity than the page size.  This is typically used for the
 | |
|  *  second level page tables on 32-bit ARMs.
 | |
|  *
 | |
|  * FIXME - this comment is *out of date*
 | |
|  * Theory:
 | |
|  *  We "misuse" the Linux memory management system.  We use alloc_page
 | |
|  *  to allocate a page and then mark it as reserved.  The Linux memory
 | |
|  *  management system will then ignore the "offset", "next_hash" and
 | |
|  *  "pprev_hash" entries in the mem_map for this page.
 | |
|  *
 | |
|  *  We then use a bitstring in the "offset" field to mark which segments
 | |
|  *  of the page are in use, and manipulate this as required during the
 | |
|  *  allocation and freeing of these small pages.
 | |
|  *
 | |
|  *  We also maintain a queue of pages being used for this purpose using
 | |
|  *  the "next_hash" and "pprev_hash" entries of mem_map;
 | |
|  */
 | |
| 
 | |
| struct order {
 | |
| 	struct list_head queue;
 | |
| 	unsigned int mask;		/* (1 << shift) - 1		*/
 | |
| 	unsigned int shift;		/* (1 << shift) size of page	*/
 | |
| 	unsigned int block_mask;	/* nr_blocks - 1		*/
 | |
| 	unsigned int all_used;		/* (1 << nr_blocks) - 1		*/
 | |
| };
 | |
| 
 | |
| 
 | |
| static struct order orders[] = {
 | |
| #if PAGE_SIZE == 32768
 | |
| 	{ LIST_HEAD_INIT(orders[0].queue), 2047, 11, 15, 0x0000ffff },
 | |
| 	{ LIST_HEAD_INIT(orders[1].queue), 8191, 13,  3, 0x0000000f }
 | |
| #else
 | |
| #error unsupported page size (ARGH!)
 | |
| #endif
 | |
| };
 | |
| 
 | |
| #define USED_MAP(pg)			((pg)->index)
 | |
| #define TEST_AND_CLEAR_USED(pg,off)	(test_and_clear_bit(off, &USED_MAP(pg)))
 | |
| #define SET_USED(pg,off)		(set_bit(off, &USED_MAP(pg)))
 | |
| 
 | |
| static DEFINE_SPINLOCK(small_page_lock);
 | |
| 
 | |
| static unsigned long __get_small_page(int priority, struct order *order)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct page *page;
 | |
| 	int offset;
 | |
| 
 | |
| 	do {
 | |
| 		spin_lock_irqsave(&small_page_lock, flags);
 | |
| 
 | |
| 		if (list_empty(&order->queue))
 | |
| 			goto need_new_page;
 | |
| 
 | |
| 		page = list_entry(order->queue.next, struct page, lru);
 | |
| again:
 | |
| #ifdef PEDANTIC
 | |
| 		BUG_ON(USED_MAP(page) & ~order->all_used);
 | |
| #endif
 | |
| 		offset = ffz(USED_MAP(page));
 | |
| 		SET_USED(page, offset);
 | |
| 		if (USED_MAP(page) == order->all_used)
 | |
| 			list_del_init(&page->lru);
 | |
| 		spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 
 | |
| 		return (unsigned long) page_address(page) + (offset << order->shift);
 | |
| 
 | |
| need_new_page:
 | |
| 		spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 		page = alloc_page(priority);
 | |
| 		spin_lock_irqsave(&small_page_lock, flags);
 | |
| 
 | |
| 		if (list_empty(&order->queue)) {
 | |
| 			if (!page)
 | |
| 				goto no_page;
 | |
| 			SetPageReserved(page);
 | |
| 			USED_MAP(page) = 0;
 | |
| 			list_add(&page->lru, &order->queue);
 | |
| 			goto again;
 | |
| 		}
 | |
| 
 | |
| 		spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 		__free_page(page);
 | |
| 	} while (1);
 | |
| 
 | |
| no_page:
 | |
| 	spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| static void __free_small_page(unsigned long spage, struct order *order)
 | |
| {
 | |
| 	unsigned long flags;
 | |
| 	struct page *page;
 | |
| 
 | |
| 	if (virt_addr_valid(spage)) {
 | |
| 		page = virt_to_page(spage);
 | |
| 
 | |
| 		/*
 | |
| 		 * The container-page must be marked Reserved
 | |
| 		 */
 | |
| 		if (!PageReserved(page) || spage & order->mask)
 | |
| 			goto non_small;
 | |
| 
 | |
| #ifdef PEDANTIC
 | |
| 		BUG_ON(USED_MAP(page) & ~order->all_used);
 | |
| #endif
 | |
| 
 | |
| 		spage = spage >> order->shift;
 | |
| 		spage &= order->block_mask;
 | |
| 
 | |
| 		/*
 | |
| 		 * the following must be atomic wrt get_page
 | |
| 		 */
 | |
| 		spin_lock_irqsave(&small_page_lock, flags);
 | |
| 
 | |
| 		if (USED_MAP(page) == order->all_used)
 | |
| 			list_add(&page->lru, &order->queue);
 | |
| 
 | |
| 		if (!TEST_AND_CLEAR_USED(page, spage))
 | |
| 			goto already_free;
 | |
| 
 | |
| 		if (USED_MAP(page) == 0)
 | |
| 			goto free_page;
 | |
| 
 | |
| 		spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 	}
 | |
| 	return;
 | |
| 
 | |
| free_page:
 | |
| 	/*
 | |
| 	 * unlink the page from the small page queue and free it
 | |
| 	 */
 | |
| 	list_del_init(&page->lru);
 | |
| 	spin_unlock_irqrestore(&small_page_lock, flags);
 | |
| 	ClearPageReserved(page);
 | |
| 	__free_page(page);
 | |
| 	return;
 | |
| 
 | |
| non_small:
 | |
| 	printk("Trying to free non-small page from %p\n", __builtin_return_address(0));
 | |
| 	return;
 | |
| already_free:
 | |
| 	printk("Trying to free free small page from %p\n", __builtin_return_address(0));
 | |
| }
 | |
| 
 | |
| unsigned long get_page_8k(int priority)
 | |
| {
 | |
| 	return __get_small_page(priority, orders+1);
 | |
| }
 | |
| 
 | |
| void free_page_8k(unsigned long spage)
 | |
| {
 | |
| 	__free_small_page(spage, orders+1);
 | |
| }
 |