1/* 2 * linux/arch/arm26/mm/small_page.c 3 * 4 * Copyright (C) 1996 Russell King 5 * Copyright (C) 2003 Ian Molton 6 * 7 * This program is free software; you can redistribute it and/or modify 8 * it under the terms of the GNU General Public License version 2 as 9 * published by the Free Software Foundation. 10 * 11 * Changelog: 12 * 26/01/1996 RMK Cleaned up various areas to make little more generic 13 * 07/02/1999 RMK Support added for 16K and 32K page sizes 14 * containing 8K blocks 15 * 23/05/2004 IM Fixed to use struct page->lru (thanks wli) 16 * 17 */ 18#include <linux/signal.h> 19#include <linux/sched.h> 20#include <linux/kernel.h> 21#include <linux/errno.h> 22#include <linux/string.h> 23#include <linux/types.h> 24#include <linux/ptrace.h> 25#include <linux/mman.h> 26#include <linux/mm.h> 27#include <linux/swap.h> 28#include <linux/smp.h> 29#include <linux/bitops.h> 30 31#include <asm/pgtable.h> 32 33#define PEDANTIC 34 35 36struct order { 37 struct list_head queue; 38 unsigned int mask; /* (1 << shift) - 1 */ 39 unsigned int shift; /* (1 << shift) size of page */ 40 unsigned int block_mask; /* nr_blocks - 1 */ 41 unsigned int all_used; /* (1 << nr_blocks) - 1 */ 42}; 43 44 45static struct order orders[] = { 46#if PAGE_SIZE == 32768 47 { LIST_HEAD_INIT(orders[0].queue), 2047, 11, 15, 0x0000ffff }, 48 { LIST_HEAD_INIT(orders[1].queue), 8191, 13, 3, 0x0000000f } 49#else 50#error unsupported page size (ARGH!) 51#endif 52}; 53 54#define USED_MAP(pg) ((pg)->index) 55#define TEST_AND_CLEAR_USED(pg,off) (test_and_clear_bit(off, &USED_MAP(pg))) 56#define SET_USED(pg,off) (set_bit(off, &USED_MAP(pg))) 57 58static DEFINE_SPINLOCK(small_page_lock); 59 60static unsigned long __get_small_page(int priority, struct order *order) 61{ 62 unsigned long flags; 63 struct page *page; 64 int offset; 65 66 do { 67 spin_lock_irqsave(&small_page_lock, flags); 68 69 if (list_empty(&order->queue)) 70 goto need_new_page; 71 72 page = list_entry(order->queue.next, struct page, lru); 73again: 74#ifdef PEDANTIC 75 BUG_ON(USED_MAP(page) & ~order->all_used); 76#endif 77 offset = ffz(USED_MAP(page)); 78 SET_USED(page, offset); 79 if (USED_MAP(page) == order->all_used) 80 list_del_init(&page->lru); 81 spin_unlock_irqrestore(&small_page_lock, flags); 82 83 return (unsigned long) page_address(page) + (offset << order->shift); 84 85need_new_page: 86 spin_unlock_irqrestore(&small_page_lock, flags); 87 page = alloc_page(priority); 88 spin_lock_irqsave(&small_page_lock, flags); 89 90 if (list_empty(&order->queue)) { 91 if (!page) 92 goto no_page; 93 SetPageReserved(page); 94 USED_MAP(page) = 0; 95 list_add(&page->lru, &order->queue); 96 goto again; 97 } 98 99 spin_unlock_irqrestore(&small_page_lock, flags); 100 __free_page(page); 101 } while (1); 102 103no_page: 104 spin_unlock_irqrestore(&small_page_lock, flags); 105 return 0; 106} 107 108static void __free_small_page(unsigned long spage, struct order *order) 109{ 110 unsigned long flags; 111 struct page *page; 112 113 if (virt_addr_valid(spage)) { 114 page = virt_to_page(spage); 115 116 /* 117 * The container-page must be marked Reserved 118 */ 119 if (!PageReserved(page) || spage & order->mask) 120 goto non_small; 121 122#ifdef PEDANTIC 123 BUG_ON(USED_MAP(page) & ~order->all_used); 124#endif 125 126 spage = spage >> order->shift; 127 spage &= order->block_mask; 128 129 /* 130 * the following must be atomic wrt get_page 131 */ 132 spin_lock_irqsave(&small_page_lock, flags); 133 134 if (USED_MAP(page) == order->all_used) 135 list_add(&page->lru, &order->queue); 136 137 if (!TEST_AND_CLEAR_USED(page, spage)) 138 goto already_free; 139 140 if (USED_MAP(page) == 0) 141 goto free_page; 142 143 spin_unlock_irqrestore(&small_page_lock, flags); 144 } 145 return; 146 147free_page: 148 /* 149 * unlink the page from the small page queue and free it 150 */ 151 list_del_init(&page->lru); 152 spin_unlock_irqrestore(&small_page_lock, flags); 153 ClearPageReserved(page); 154 __free_page(page); 155 return; 156 157non_small: 158 printk("Trying to free non-small page from %p\n", __builtin_return_address(0)); 159 return; 160already_free: 161 printk("Trying to free free small page from %p\n", __builtin_return_address(0)); 162} 163 164unsigned long get_page_8k(int priority) 165{ 166 return __get_small_page(priority, orders+1); 167} 168 169void free_page_8k(unsigned long spage) 170{ 171 __free_small_page(spage, orders+1); 172} 173