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