2 * linux/arch/arm/mm/small_page.c
4 * Copyright (C) 1996 Russell King
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 * 26/01/1996 RMK Cleaned up various areas to make little more generic
12 * 07/02/1999 RMK Support added for 16K and 32K page sizes
13 * containing 8K blocks
15 #include <linux/signal.h>
16 #include <linux/sched.h>
17 #include <linux/kernel.h>
18 #include <linux/errno.h>
19 #include <linux/string.h>
20 #include <linux/types.h>
21 #include <linux/ptrace.h>
22 #include <linux/mman.h>
24 #include <linux/swap.h>
25 #include <linux/smp.h>
27 #include <asm/bitops.h>
28 #include <asm/pgtable.h>
34 * We need to be able to allocate naturally aligned memory of finer
35 * granularity than the page size. This is typically used for the
36 * second level page tables on 32-bit ARMs.
39 * We "misuse" the Linux memory management system. We use alloc_page
40 * to allocate a page and then mark it as reserved. The Linux memory
41 * management system will then ignore the "offset", "next_hash" and
42 * "pprev_hash" entries in the mem_map for this page.
44 * We then use a bitstring in the "offset" field to mark which segments
45 * of the page are in use, and manipulate this as required during the
46 * allocation and freeing of these small pages.
48 * We also maintain a queue of pages being used for this purpose using
49 * the "next_hash" and "pprev_hash" entries of mem_map;
54 unsigned int mask; /* (1 << shift) - 1 */
55 unsigned int shift; /* (1 << shift) size of page */
56 unsigned int block_mask; /* nr_blocks - 1 */
57 unsigned int all_used; /* (1 << nr_blocks) - 1 */
61 static struct order orders[] = {
63 { NULL, 2047, 11, 1, 0x00000003 }
64 #elif PAGE_SIZE == 32768
65 { NULL, 2047, 11, 15, 0x0000ffff },
66 { NULL, 8191, 13, 3, 0x0000000f }
68 #error unsupported page size
72 #define USED_MAP(pg) ((pg)->index)
73 #define TEST_AND_CLEAR_USED(pg,off) (test_and_clear_bit(off, &USED_MAP(pg)))
74 #define SET_USED(pg,off) (set_bit(off, &USED_MAP(pg)))
76 static spinlock_t small_page_lock = SPIN_LOCK_UNLOCKED;
78 static void add_page_to_queue(struct page *page, struct page **p)
86 (*p)->pprev_hash = &page->next_hash;
91 static void remove_page_from_queue(struct page *page)
93 if (page->pprev_hash) {
95 page->next_hash->pprev_hash = page->pprev_hash;
96 *page->pprev_hash = page->next_hash;
97 page->pprev_hash = NULL;
101 static unsigned long __get_small_page(int priority, struct order *order)
110 spin_lock_irqsave(&small_page_lock, flags);
114 if (USED_MAP(page) & ~order->all_used)
117 offset = ffz(USED_MAP(page));
118 SET_USED(page, offset);
119 if (USED_MAP(page) == order->all_used)
120 remove_page_from_queue(page);
121 spin_unlock_irqrestore(&small_page_lock, flags);
123 return (unsigned long) page_address(page) + (offset << order->shift);
126 page = alloc_page(priority);
128 spin_lock_irqsave(&small_page_lock, flags);
132 SetPageReserved(page);
135 add_page_to_queue(page, &order->queue);
144 spin_unlock_irqrestore(&small_page_lock, flags);
148 static void __free_small_page(unsigned long spage, struct order *order)
153 if (virt_addr_valid(spage)) {
154 page = virt_to_page(spage);
157 * The container-page must be marked Reserved
159 if (!PageReserved(page) || spage & order->mask)
163 if (USED_MAP(page) & ~order->all_used)
167 spage = spage >> order->shift;
168 spage &= order->block_mask;
171 * the following must be atomic wrt get_page
173 spin_lock_irqsave(&small_page_lock, flags);
175 if (USED_MAP(page) == order->all_used)
176 add_page_to_queue(page, &order->queue);
178 if (!TEST_AND_CLEAR_USED(page, spage))
181 if (USED_MAP(page) == 0)
184 spin_unlock_irqrestore(&small_page_lock, flags);
190 * unlink the page from the small page queue and free it
192 remove_page_from_queue(page);
193 spin_unlock_irqrestore(&small_page_lock, flags);
194 ClearPageReserved(page);
199 printk("Trying to free non-small page from %p\n", __builtin_return_address(0));
202 printk("Trying to free free small page from %p\n", __builtin_return_address(0));
205 unsigned long get_page_8k(int priority)
207 return __get_small_page(priority, orders+1);
210 void free_page_8k(unsigned long spage)
212 __free_small_page(spage, orders+1);