Commit 929f97276bcf7f4a95272ed08a85339b98ba210d

Authored by Dean Nelson
Committed by Linus Torvalds
1 parent 833423143c

[PATCH] change gen_pool allocator to not touch managed memory

Modify the gen_pool allocator (lib/genalloc.c) to utilize a bitmap scheme
instead of the buddy scheme.  The purpose of this change is to eliminate
the touching of the actual memory being allocated.

Since the change modifies the interface, a change to the uncached allocator
(arch/ia64/kernel/uncached.c) is also required.

Both Andrey Volkov and Jes Sorenson have expressed a desire that the
gen_pool allocator not write to the memory being managed. See the
following:

  http://marc.theaimsgroup.com/?l=linux-kernel&m=113518602713125&w=2
  http://marc.theaimsgroup.com/?l=linux-kernel&m=113533568827916&w=2

Signed-off-by: Dean Nelson <dcn@sgi.com>
Cc: Andrey Volkov <avolkov@varma-el.com>
Acked-by: Jes Sorensen <jes@trained-monkey.org>
Cc: "Luck, Tony" <tony.luck@intel.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

Showing 4 changed files with 252 additions and 261 deletions Side-by-side Diff

arch/ia64/kernel/uncached.c
1 1 /*
2   - * Copyright (C) 2001-2005 Silicon Graphics, Inc. All rights reserved.
  2 + * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights reserved.
3 3 *
4 4 * This program is free software; you can redistribute it and/or modify it
5 5 * under the terms of version 2 of the GNU General Public License
6 6  
7 7  
... ... @@ -29,16 +29,9 @@
29 29 #include <asm/tlbflush.h>
30 30 #include <asm/sn/arch.h>
31 31  
32   -#define DEBUG 0
33 32  
34   -#if DEBUG
35   -#define dprintk printk
36   -#else
37   -#define dprintk(x...) do { } while (0)
38   -#endif
  33 +extern void __init efi_memmap_walk_uc(efi_freemem_callback_t, void *);
39 34  
40   -void __init efi_memmap_walk_uc (efi_freemem_callback_t callback);
41   -
42 35 #define MAX_UNCACHED_GRANULES 5
43 36 static int allocated_granules;
44 37  
... ... @@ -60,6 +53,7 @@
60 53 static void uncached_ipi_mc_drain(void *data)
61 54 {
62 55 int status;
  56 +
63 57 status = ia64_pal_mc_drain();
64 58 if (status)
65 59 printk(KERN_WARNING "ia64_pal_mc_drain() failed with %i on "
66 60  
67 61  
68 62  
69 63  
70 64  
71 65  
72 66  
... ... @@ -67,30 +61,35 @@
67 61 }
68 62  
69 63  
70   -static unsigned long
71   -uncached_get_new_chunk(struct gen_pool *poolp)
  64 +/*
  65 + * Add a new chunk of uncached memory pages to the specified pool.
  66 + *
  67 + * @pool: pool to add new chunk of uncached memory to
  68 + * @nid: node id of node to allocate memory from, or -1
  69 + *
  70 + * This is accomplished by first allocating a granule of cached memory pages
  71 + * and then converting them to uncached memory pages.
  72 + */
  73 +static int uncached_add_chunk(struct gen_pool *pool, int nid)
72 74 {
73 75 struct page *page;
74   - void *tmp;
75 76 int status, i;
76   - unsigned long addr, node;
  77 + unsigned long c_addr, uc_addr;
77 78  
78 79 if (allocated_granules >= MAX_UNCACHED_GRANULES)
79   - return 0;
  80 + return -1;
80 81  
81   - node = poolp->private;
82   - page = alloc_pages_node(node, GFP_KERNEL | __GFP_ZERO,
  82 + /* attempt to allocate a granule's worth of cached memory pages */
  83 +
  84 + page = alloc_pages_node(nid, GFP_KERNEL | __GFP_ZERO,
83 85 IA64_GRANULE_SHIFT-PAGE_SHIFT);
  86 + if (!page)
  87 + return -1;
84 88  
85   - dprintk(KERN_INFO "get_new_chunk page %p, addr %lx\n",
86   - page, (unsigned long)(page-vmem_map) << PAGE_SHIFT);
  89 + /* convert the memory pages from cached to uncached */
87 90  
88   - /*
89   - * Do magic if no mem on local node! XXX
90   - */
91   - if (!page)
92   - return 0;
93   - tmp = page_address(page);
  91 + c_addr = (unsigned long)page_address(page);
  92 + uc_addr = c_addr - PAGE_OFFSET + __IA64_UNCACHED_OFFSET;
94 93  
95 94 /*
96 95 * There's a small race here where it's possible for someone to
97 96  
98 97  
99 98  
100 99  
101 100  
102 101  
103 102  
104 103  
105 104  
106 105  
107 106  
108 107  
109 108  
110 109  
111 110  
112 111  
113 112  
114 113  
... ... @@ -100,76 +99,90 @@
100 99 for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++)
101 100 SetPageUncached(&page[i]);
102 101  
103   - flush_tlb_kernel_range(tmp, tmp + IA64_GRANULE_SIZE);
  102 + flush_tlb_kernel_range(uc_addr, uc_adddr + IA64_GRANULE_SIZE);
104 103  
105 104 status = ia64_pal_prefetch_visibility(PAL_VISIBILITY_PHYSICAL);
106   -
107   - dprintk(KERN_INFO "pal_prefetch_visibility() returns %i on cpu %i\n",
108   - status, raw_smp_processor_id());
109   -
110 105 if (!status) {
111 106 status = smp_call_function(uncached_ipi_visibility, NULL, 0, 1);
112 107 if (status)
113   - printk(KERN_WARNING "smp_call_function failed for "
114   - "uncached_ipi_visibility! (%i)\n", status);
  108 + goto failed;
115 109 }
116 110  
  111 + preempt_disable();
  112 +
117 113 if (ia64_platform_is("sn2"))
118   - sn_flush_all_caches((unsigned long)tmp, IA64_GRANULE_SIZE);
  114 + sn_flush_all_caches(uc_addr, IA64_GRANULE_SIZE);
119 115 else
120   - flush_icache_range((unsigned long)tmp,
121   - (unsigned long)tmp+IA64_GRANULE_SIZE);
  116 + flush_icache_range(uc_addr, uc_addr + IA64_GRANULE_SIZE);
122 117  
  118 + /* flush the just introduced uncached translation from the TLB */
  119 + local_flush_tlb_all();
  120 +
  121 + preempt_enable();
  122 +
123 123 ia64_pal_mc_drain();
124 124 status = smp_call_function(uncached_ipi_mc_drain, NULL, 0, 1);
125 125 if (status)
126   - printk(KERN_WARNING "smp_call_function failed for "
127   - "uncached_ipi_mc_drain! (%i)\n", status);
  126 + goto failed;
128 127  
129   - addr = (unsigned long)tmp - PAGE_OFFSET + __IA64_UNCACHED_OFFSET;
  128 + /*
  129 + * The chunk of memory pages has been converted to uncached so now we
  130 + * can add it to the pool.
  131 + */
  132 + status = gen_pool_add(pool, uc_addr, IA64_GRANULE_SIZE, nid);
  133 + if (status)
  134 + goto failed;
130 135  
131 136 allocated_granules++;
132   - return addr;
  137 + return 0;
  138 +
  139 + /* failed to convert or add the chunk so give it back to the kernel */
  140 +failed:
  141 + for (i = 0; i < (IA64_GRANULE_SIZE / PAGE_SIZE); i++)
  142 + ClearPageUncached(&page[i]);
  143 +
  144 + free_pages(c_addr, IA64_GRANULE_SHIFT-PAGE_SHIFT);
  145 + return -1;
133 146 }
134 147  
135 148  
136 149 /*
137 150 * uncached_alloc_page
138 151 *
  152 + * @starting_nid: node id of node to start with, or -1
  153 + *
139 154 * Allocate 1 uncached page. Allocates on the requested node. If no
140 155 * uncached pages are available on the requested node, roundrobin starting
141   - * with higher nodes.
  156 + * with the next higher node.
142 157 */
143   -unsigned long
144   -uncached_alloc_page(int nid)
  158 +unsigned long uncached_alloc_page(int starting_nid)
145 159 {
146   - unsigned long maddr;
  160 + unsigned long uc_addr;
  161 + struct gen_pool *pool;
  162 + int nid;
147 163  
148   - maddr = gen_pool_alloc(uncached_pool[nid], PAGE_SIZE);
  164 + if (unlikely(starting_nid >= MAX_NUMNODES))
  165 + return 0;
149 166  
150   - dprintk(KERN_DEBUG "uncached_alloc_page returns %lx on node %i\n",
151   - maddr, nid);
  167 + if (starting_nid < 0)
  168 + starting_nid = numa_node_id();
  169 + nid = starting_nid;
152 170  
153   - /*
154   - * If no memory is availble on our local node, try the
155   - * remaining nodes in the system.
156   - */
157   - if (!maddr) {
158   - int i;
  171 + do {
  172 + if (!node_online(nid))
  173 + continue;
  174 + pool = uncached_pool[nid];
  175 + if (pool == NULL)
  176 + continue;
  177 + do {
  178 + uc_addr = gen_pool_alloc(pool, PAGE_SIZE);
  179 + if (uc_addr != 0)
  180 + return uc_addr;
  181 + } while (uncached_add_chunk(pool, nid) == 0);
159 182  
160   - for (i = MAX_NUMNODES - 1; i >= 0; i--) {
161   - if (i == nid || !node_online(i))
162   - continue;
163   - maddr = gen_pool_alloc(uncached_pool[i], PAGE_SIZE);
164   - dprintk(KERN_DEBUG "uncached_alloc_page alternate search "
165   - "returns %lx on node %i\n", maddr, i);
166   - if (maddr) {
167   - break;
168   - }
169   - }
170   - }
  183 + } while ((nid = (nid + 1) % MAX_NUMNODES) != starting_nid);
171 184  
172   - return maddr;
  185 + return 0;
173 186 }
174 187 EXPORT_SYMBOL(uncached_alloc_page);
175 188  
176 189  
177 190  
178 191  
179 192  
180 193  
... ... @@ -177,21 +190,22 @@
177 190 /*
178 191 * uncached_free_page
179 192 *
  193 + * @uc_addr: uncached address of page to free
  194 + *
180 195 * Free a single uncached page.
181 196 */
182   -void
183   -uncached_free_page(unsigned long maddr)
  197 +void uncached_free_page(unsigned long uc_addr)
184 198 {
185   - int node;
  199 + int nid = paddr_to_nid(uc_addr - __IA64_UNCACHED_OFFSET);
  200 + struct gen_pool *pool = uncached_pool[nid];
186 201  
187   - node = paddr_to_nid(maddr - __IA64_UNCACHED_OFFSET);
  202 + if (unlikely(pool == NULL))
  203 + return;
188 204  
189   - dprintk(KERN_DEBUG "uncached_free_page(%lx) on node %i\n", maddr, node);
  205 + if ((uc_addr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET)
  206 + panic("uncached_free_page invalid address %lx\n", uc_addr);
190 207  
191   - if ((maddr & (0XFUL << 60)) != __IA64_UNCACHED_OFFSET)
192   - panic("uncached_free_page invalid address %lx\n", maddr);
193   -
194   - gen_pool_free(uncached_pool[node], maddr, PAGE_SIZE);
  208 + gen_pool_free(pool, uc_addr, PAGE_SIZE);
195 209 }
196 210 EXPORT_SYMBOL(uncached_free_page);
197 211  
198 212  
199 213  
200 214  
201 215  
202 216  
203 217  
204 218  
205 219  
206 220  
... ... @@ -199,43 +213,39 @@
199 213 /*
200 214 * uncached_build_memmap,
201 215 *
  216 + * @uc_start: uncached starting address of a chunk of uncached memory
  217 + * @uc_end: uncached ending address of a chunk of uncached memory
  218 + * @arg: ignored, (NULL argument passed in on call to efi_memmap_walk_uc())
  219 + *
202 220 * Called at boot time to build a map of pages that can be used for
203 221 * memory special operations.
204 222 */
205   -static int __init
206   -uncached_build_memmap(unsigned long start, unsigned long end, void *arg)
  223 +static int __init uncached_build_memmap(unsigned long uc_start,
  224 + unsigned long uc_end, void *arg)
207 225 {
208   - long length = end - start;
209   - int node;
  226 + int nid = paddr_to_nid(uc_start - __IA64_UNCACHED_OFFSET);
  227 + struct gen_pool *pool = uncached_pool[nid];
  228 + size_t size = uc_end - uc_start;
210 229  
211   - dprintk(KERN_ERR "uncached_build_memmap(%lx %lx)\n", start, end);
212   -
213 230 touch_softlockup_watchdog();
214   - memset((char *)start, 0, length);
215 231  
216   - node = paddr_to_nid(start - __IA64_UNCACHED_OFFSET);
217   -
218   - for (; start < end ; start += PAGE_SIZE) {
219   - dprintk(KERN_INFO "sticking %lx into the pool!\n", start);
220   - gen_pool_free(uncached_pool[node], start, PAGE_SIZE);
  232 + if (pool != NULL) {
  233 + memset((char *)uc_start, 0, size);
  234 + (void) gen_pool_add(pool, uc_start, size, nid);
221 235 }
222   -
223 236 return 0;
224 237 }
225 238  
226 239  
227   -static int __init uncached_init(void) {
228   - int i;
  240 +static int __init uncached_init(void)
  241 +{
  242 + int nid;
229 243  
230   - for (i = 0; i < MAX_NUMNODES; i++) {
231   - if (!node_online(i))
232   - continue;
233   - uncached_pool[i] = gen_pool_create(0, IA64_GRANULE_SHIFT,
234   - &uncached_get_new_chunk, i);
  244 + for_each_online_node(nid) {
  245 + uncached_pool[nid] = gen_pool_create(PAGE_SHIFT, nid);
235 246 }
236 247  
237   - efi_memmap_walk_uc(uncached_build_memmap);
238   -
  248 + efi_memmap_walk_uc(uncached_build_memmap, NULL);
239 249 return 0;
240 250 }
241 251  
arch/ia64/sn/kernel/sn2/cache.c
... ... @@ -3,11 +3,12 @@
3 3 * License. See the file "COPYING" in the main directory of this archive
4 4 * for more details.
5 5 *
6   - * Copyright (C) 2001-2003 Silicon Graphics, Inc. All rights reserved.
  6 + * Copyright (C) 2001-2003, 2006 Silicon Graphics, Inc. All rights reserved.
7 7 *
8 8 */
9 9 #include <linux/module.h>
10 10 #include <asm/pgalloc.h>
  11 +#include <asm/sn/arch.h>
11 12  
12 13 /**
13 14 * sn_flush_all_caches - flush a range of address from all caches (incl. L4)
14 15  
15 16  
... ... @@ -17,18 +18,24 @@
17 18 * Flush a range of addresses from all caches including L4.
18 19 * All addresses fully or partially contained within
19 20 * @flush_addr to @flush_addr + @bytes are flushed
20   - * from the all caches.
  21 + * from all caches.
21 22 */
22 23 void
23 24 sn_flush_all_caches(long flush_addr, long bytes)
24 25 {
25   - flush_icache_range(flush_addr, flush_addr+bytes);
  26 + unsigned long addr = flush_addr;
  27 +
  28 + /* SHub1 requires a cached address */
  29 + if (is_shub1() && (addr & RGN_BITS) == RGN_BASE(RGN_UNCACHED))
  30 + addr = (addr - RGN_BASE(RGN_UNCACHED)) + RGN_BASE(RGN_KERNEL);
  31 +
  32 + flush_icache_range(addr, addr + bytes);
26 33 /*
27 34 * The last call may have returned before the caches
28 35 * were actually flushed, so we call it again to make
29 36 * sure.
30 37 */
31   - flush_icache_range(flush_addr, flush_addr+bytes);
  38 + flush_icache_range(addr, addr + bytes);
32 39 mb();
33 40 }
34 41 EXPORT_SYMBOL(sn_flush_all_caches);
include/linux/genalloc.h
... ... @@ -4,38 +4,33 @@
4 4 * Uses for this includes on-device special memory, uncached memory
5 5 * etc.
6 6 *
7   - * This code is based on the buddy allocator found in the sym53c8xx_2
8   - * driver, adapted for general purpose use.
9   - *
10 7 * This source code is licensed under the GNU General Public License,
11 8 * Version 2. See the file COPYING for more details.
12 9 */
13 10  
14   -#include <linux/spinlock.h>
15 11  
16   -#define ALLOC_MIN_SHIFT 5 /* 32 bytes minimum */
17 12 /*
18   - * Link between free memory chunks of a given size.
  13 + * General purpose special memory pool descriptor.
19 14 */
20   -struct gen_pool_link {
21   - struct gen_pool_link *next;
  15 +struct gen_pool {
  16 + rwlock_t lock;
  17 + struct list_head chunks; /* list of chunks in this pool */
  18 + int min_alloc_order; /* minimum allocation order */
22 19 };
23 20  
24 21 /*
25   - * Memory pool descriptor.
  22 + * General purpose special memory pool chunk descriptor.
26 23 */
27   -struct gen_pool {
  24 +struct gen_pool_chunk {
28 25 spinlock_t lock;
29   - unsigned long (*get_new_chunk)(struct gen_pool *);
30   - struct gen_pool *next;
31   - struct gen_pool_link *h;
32   - unsigned long private;
33   - int max_chunk_shift;
  26 + struct list_head next_chunk; /* next chunk in pool */
  27 + unsigned long start_addr; /* starting address of memory chunk */
  28 + unsigned long end_addr; /* ending address of memory chunk */
  29 + unsigned long bits[0]; /* bitmap for allocating memory chunk */
34 30 };
35 31  
36   -unsigned long gen_pool_alloc(struct gen_pool *poolp, int size);
37   -void gen_pool_free(struct gen_pool *mp, unsigned long ptr, int size);
38   -struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift,
39   - unsigned long (*fp)(struct gen_pool *),
40   - unsigned long data);
  32 +extern struct gen_pool *gen_pool_create(int, int);
  33 +extern int gen_pool_add(struct gen_pool *, unsigned long, size_t, int);
  34 +extern unsigned long gen_pool_alloc(struct gen_pool *, size_t);
  35 +extern void gen_pool_free(struct gen_pool *, unsigned long, size_t);
... ... @@ -4,10 +4,6 @@
4 4 * Uses for this includes on-device special memory, uncached memory
5 5 * etc.
6 6 *
7   - * This code is based on the buddy allocator found in the sym53c8xx_2
8   - * driver Copyright (C) 1999-2001 Gerard Roudier <groudier@free.fr>,
9   - * and adapted for general purpose use.
10   - *
11 7 * Copyright 2005 (C) Jes Sorensen <jes@trained-monkey.org>
12 8 *
13 9 * This source code is licensed under the GNU General Public License,
14 10  
15 11  
16 12  
17 13  
18 14  
19 15  
20 16  
21 17  
22 18  
23 19  
24 20  
25 21  
26 22  
27 23  
28 24  
29 25  
30 26  
31 27  
32 28  
33 29  
34 30  
35 31  
36 32  
37 33  
38 34  
39 35  
... ... @@ -15,173 +11,156 @@
15 11 */
16 12  
17 13 #include <linux/module.h>
18   -#include <linux/stddef.h>
19   -#include <linux/kernel.h>
20   -#include <linux/string.h>
21   -#include <linux/slab.h>
22   -#include <linux/init.h>
23   -#include <linux/mm.h>
24   -#include <linux/spinlock.h>
25 14 #include <linux/genalloc.h>
26 15  
27   -#include <asm/page.h>
28 16  
29   -
30   -struct gen_pool *gen_pool_create(int nr_chunks, int max_chunk_shift,
31   - unsigned long (*fp)(struct gen_pool *),
32   - unsigned long data)
  17 +/*
  18 + * Create a new special memory pool.
  19 + *
  20 + * @min_alloc_order: log base 2 of number of bytes each bitmap bit represents
  21 + * @nid: node id of the node the pool structure should be allocated on, or -1
  22 + */
  23 +struct gen_pool *gen_pool_create(int min_alloc_order, int nid)
33 24 {
34   - struct gen_pool *poolp;
35   - unsigned long tmp;
36   - int i;
  25 + struct gen_pool *pool;
37 26  
38   - /*
39   - * This is really an arbitrary limit, +10 is enough for
40   - * IA64_GRANULE_SHIFT, aka 16MB. If anyone needs a large limit
41   - * this can be increased without problems.
42   - */
43   - if ((max_chunk_shift > (PAGE_SHIFT + 10)) ||
44   - ((max_chunk_shift < ALLOC_MIN_SHIFT) && max_chunk_shift))
45   - return NULL;
  27 + pool = kmalloc_node(sizeof(struct gen_pool), GFP_KERNEL, nid);
  28 + if (pool != NULL) {
  29 + rwlock_init(&pool->lock);
  30 + INIT_LIST_HEAD(&pool->chunks);
  31 + pool->min_alloc_order = min_alloc_order;
  32 + }
  33 + return pool;
  34 +}
  35 +EXPORT_SYMBOL(gen_pool_create);
46 36  
47   - if (!max_chunk_shift)
48   - max_chunk_shift = PAGE_SHIFT;
49 37  
50   - poolp = kmalloc(sizeof(struct gen_pool), GFP_KERNEL);
51   - if (!poolp)
52   - return NULL;
53   - memset(poolp, 0, sizeof(struct gen_pool));
54   - poolp->h = kmalloc(sizeof(struct gen_pool_link) *
55   - (max_chunk_shift - ALLOC_MIN_SHIFT + 1),
56   - GFP_KERNEL);
57   - if (!poolp->h) {
58   - printk(KERN_WARNING "gen_pool_alloc() failed to allocate\n");
59   - kfree(poolp);
60   - return NULL;
61   - }
62   - memset(poolp->h, 0, sizeof(struct gen_pool_link) *
63   - (max_chunk_shift - ALLOC_MIN_SHIFT + 1));
  38 +/*
  39 + * Add a new chunk of memory to the specified pool.
  40 + *
  41 + * @pool: pool to add new memory chunk to
  42 + * @addr: starting address of memory chunk to add to pool
  43 + * @size: size in bytes of the memory chunk to add to pool
  44 + * @nid: node id of the node the chunk structure and bitmap should be
  45 + * allocated on, or -1
  46 + */
  47 +int gen_pool_add(struct gen_pool *pool, unsigned long addr, size_t size,
  48 + int nid)
  49 +{
  50 + struct gen_pool_chunk *chunk;
  51 + int nbits = size >> pool->min_alloc_order;
  52 + int nbytes = sizeof(struct gen_pool_chunk) +
  53 + (nbits + BITS_PER_BYTE - 1) / BITS_PER_BYTE;
64 54  
65   - spin_lock_init(&poolp->lock);
66   - poolp->get_new_chunk = fp;
67   - poolp->max_chunk_shift = max_chunk_shift;
68   - poolp->private = data;
  55 + chunk = kmalloc_node(nbytes, GFP_KERNEL, nid);
  56 + if (unlikely(chunk == NULL))
  57 + return -1;
69 58  
70   - for (i = 0; i < nr_chunks; i++) {
71   - tmp = poolp->get_new_chunk(poolp);
72   - printk(KERN_INFO "allocated %lx\n", tmp);
73   - if (!tmp)
74   - break;
75   - gen_pool_free(poolp, tmp, (1 << poolp->max_chunk_shift));
76   - }
  59 + memset(chunk, 0, nbytes);
  60 + spin_lock_init(&chunk->lock);
  61 + chunk->start_addr = addr;
  62 + chunk->end_addr = addr + size;
77 63  
78   - return poolp;
  64 + write_lock(&pool->lock);
  65 + list_add(&chunk->next_chunk, &pool->chunks);
  66 + write_unlock(&pool->lock);
  67 +
  68 + return 0;
79 69 }
80   -EXPORT_SYMBOL(gen_pool_create);
  70 +EXPORT_SYMBOL(gen_pool_add);
81 71  
82 72  
83 73 /*
84   - * Simple power of two buddy-like generic allocator.
85   - * Provides naturally aligned memory chunks.
  74 + * Allocate the requested number of bytes from the specified pool.
  75 + * Uses a first-fit algorithm.
  76 + *
  77 + * @pool: pool to allocate from
  78 + * @size: number of bytes to allocate from the pool
86 79 */
87   -unsigned long gen_pool_alloc(struct gen_pool *poolp, int size)
  80 +unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
88 81 {
89   - int j, i, s, max_chunk_size;
90   - unsigned long a, flags;
91   - struct gen_pool_link *h = poolp->h;
  82 + struct list_head *_chunk;
  83 + struct gen_pool_chunk *chunk;
  84 + unsigned long addr, flags;
  85 + int order = pool->min_alloc_order;
  86 + int nbits, bit, start_bit, end_bit;
92 87  
93   - max_chunk_size = 1 << poolp->max_chunk_shift;
94   -
95   - if (size > max_chunk_size)
  88 + if (size == 0)
96 89 return 0;
97 90  
98   - size = max(size, 1 << ALLOC_MIN_SHIFT);
99   - i = fls(size - 1);
100   - s = 1 << i;
101   - j = i -= ALLOC_MIN_SHIFT;
  91 + nbits = (size + (1UL << order) - 1) >> order;
102 92  
103   - spin_lock_irqsave(&poolp->lock, flags);
104   - while (!h[j].next) {
105   - if (s == max_chunk_size) {
106   - struct gen_pool_link *ptr;
107   - spin_unlock_irqrestore(&poolp->lock, flags);
108   - ptr = (struct gen_pool_link *)poolp->get_new_chunk(poolp);
109   - spin_lock_irqsave(&poolp->lock, flags);
110   - h[j].next = ptr;
111   - if (h[j].next)
112   - h[j].next->next = NULL;
113   - break;
  93 + read_lock(&pool->lock);
  94 + list_for_each(_chunk, &pool->chunks) {
  95 + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
  96 +
  97 + end_bit = (chunk->end_addr - chunk->start_addr) >> order;
  98 + end_bit -= nbits + 1;
  99 +
  100 + spin_lock_irqsave(&chunk->lock, flags);
  101 + bit = -1;
  102 + while (bit + 1 < end_bit) {
  103 + bit = find_next_zero_bit(chunk->bits, end_bit, bit + 1);
  104 + if (bit >= end_bit)
  105 + break;
  106 +
  107 + start_bit = bit;
  108 + if (nbits > 1) {
  109 + bit = find_next_bit(chunk->bits, bit + nbits,
  110 + bit + 1);
  111 + if (bit - start_bit < nbits)
  112 + continue;
  113 + }
  114 +
  115 + addr = chunk->start_addr +
  116 + ((unsigned long)start_bit << order);
  117 + while (nbits--)
  118 + __set_bit(start_bit++, &chunk->bits);
  119 + spin_unlock_irqrestore(&chunk->lock, flags);
  120 + read_unlock(&pool->lock);
  121 + return addr;
114 122 }
115   - j++;
116   - s <<= 1;
  123 + spin_unlock_irqrestore(&chunk->lock, flags);
117 124 }
118   - a = (unsigned long) h[j].next;
119   - if (a) {
120   - h[j].next = h[j].next->next;
121   - /*
122   - * This should be split into a seperate function doing
123   - * the chunk split in order to support custom
124   - * handling memory not physically accessible by host
125   - */
126   - while (j > i) {
127   - j -= 1;
128   - s >>= 1;
129   - h[j].next = (struct gen_pool_link *) (a + s);
130   - h[j].next->next = NULL;
131   - }
132   - }
133   - spin_unlock_irqrestore(&poolp->lock, flags);
134   - return a;
  125 + read_unlock(&pool->lock);
  126 + return 0;
135 127 }
136 128 EXPORT_SYMBOL(gen_pool_alloc);
137 129  
138 130  
139 131 /*
140   - * Counter-part of the generic allocator.
  132 + * Free the specified memory back to the specified pool.
  133 + *
  134 + * @pool: pool to free to
  135 + * @addr: starting address of memory to free back to pool
  136 + * @size: size in bytes of memory to free
141 137 */
142   -void gen_pool_free(struct gen_pool *poolp, unsigned long ptr, int size)
  138 +void gen_pool_free(struct gen_pool *pool, unsigned long addr, size_t size)
143 139 {
144   - struct gen_pool_link *q;
145   - struct gen_pool_link *h = poolp->h;
146   - unsigned long a, b, flags;
147   - int i, s, max_chunk_size;
  140 + struct list_head *_chunk;
  141 + struct gen_pool_chunk *chunk;
  142 + unsigned long flags;
  143 + int order = pool->min_alloc_order;
  144 + int bit, nbits;
148 145  
149   - max_chunk_size = 1 << poolp->max_chunk_shift;
  146 + nbits = (size + (1UL << order) - 1) >> order;
150 147  
151   - if (size > max_chunk_size)
152   - return;
  148 + read_lock(&pool->lock);
  149 + list_for_each(_chunk, &pool->chunks) {
  150 + chunk = list_entry(_chunk, struct gen_pool_chunk, next_chunk);
153 151  
154   - size = max(size, 1 << ALLOC_MIN_SHIFT);
155   - i = fls(size - 1);
156   - s = 1 << i;
157   - i -= ALLOC_MIN_SHIFT;
158   -
159   - a = ptr;
160   -
161   - spin_lock_irqsave(&poolp->lock, flags);
162   - while (1) {
163   - if (s == max_chunk_size) {
164   - ((struct gen_pool_link *)a)->next = h[i].next;
165   - h[i].next = (struct gen_pool_link *)a;
  152 + if (addr >= chunk->start_addr && addr < chunk->end_addr) {
  153 + BUG_ON(addr + size > chunk->end_addr);
  154 + spin_lock_irqsave(&chunk->lock, flags);
  155 + bit = (addr - chunk->start_addr) >> order;
  156 + while (nbits--)
  157 + __clear_bit(bit++, &chunk->bits);
  158 + spin_unlock_irqrestore(&chunk->lock, flags);
166 159 break;
167 160 }
168   - b = a ^ s;
169   - q = &h[i];
170   -
171   - while (q->next && q->next != (struct gen_pool_link *)b)
172   - q = q->next;
173   -
174   - if (!q->next) {
175   - ((struct gen_pool_link *)a)->next = h[i].next;
176   - h[i].next = (struct gen_pool_link *)a;
177   - break;
178   - }
179   - q->next = q->next->next;
180   - a = a & b;
181   - s <<= 1;
182   - i++;
183 161 }
184   - spin_unlock_irqrestore(&poolp->lock, flags);
  162 + BUG_ON(nbits > 0);
  163 + read_unlock(&pool->lock);
185 164 }
186 165 EXPORT_SYMBOL(gen_pool_free);