1
2
3
4
5
6
7
8
9
10
11#include <linux/mm.h>
12#include <linux/slab.h>
13#include <linux/module.h>
14#include <linux/mempool.h>
15#include <linux/buffer_head.h>
16
17static void add_element(mempool_t *pool, void *element)
18{
19 BUG_ON(pool->curr_nr >= pool->min_nr);
20 pool->elements[pool->curr_nr++] = element;
21}
22
23static void *remove_element(mempool_t *pool)
24{
25 BUG_ON(pool->curr_nr <= 0);
26 return pool->elements[--pool->curr_nr];
27}
28
29static void free_pool(mempool_t *pool)
30{
31 while (pool->curr_nr) {
32 void *element = remove_element(pool);
33 pool->free(element, pool->pool_data);
34 }
35 kfree(pool->elements);
36 kfree(pool);
37}
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53mempool_t * mempool_create(int min_nr, mempool_alloc_t *alloc_fn,
54 mempool_free_t *free_fn, void *pool_data)
55{
56 mempool_t *pool;
57
58 pool = kmalloc(sizeof(*pool), GFP_KERNEL);
59 if (!pool)
60 return NULL;
61 memset(pool, 0, sizeof(*pool));
62 pool->elements = kmalloc(min_nr * sizeof(void *), GFP_KERNEL);
63 if (!pool->elements) {
64 kfree(pool);
65 return NULL;
66 }
67 spin_lock_init(&pool->lock);
68 pool->min_nr = min_nr;
69 pool->pool_data = pool_data;
70 init_waitqueue_head(&pool->wait);
71 pool->alloc = alloc_fn;
72 pool->free = free_fn;
73
74
75
76
77 while (pool->curr_nr < pool->min_nr) {
78 void *element;
79
80 element = pool->alloc(GFP_KERNEL, pool->pool_data);
81 if (unlikely(!element)) {
82 free_pool(pool);
83 return NULL;
84 }
85 add_element(pool, element);
86 }
87 return pool;
88}
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106int mempool_resize(mempool_t *pool, int new_min_nr, int gfp_mask)
107{
108 void *element;
109 void **new_elements;
110 unsigned long flags;
111
112 BUG_ON(new_min_nr <= 0);
113
114 spin_lock_irqsave(&pool->lock, flags);
115 if (new_min_nr < pool->min_nr) {
116 while (pool->curr_nr > new_min_nr) {
117 element = remove_element(pool);
118 spin_unlock_irqrestore(&pool->lock, flags);
119 pool->free(element, pool->pool_data);
120 spin_lock_irqsave(&pool->lock, flags);
121 }
122 pool->min_nr = new_min_nr;
123 goto out_unlock;
124 }
125 spin_unlock_irqrestore(&pool->lock, flags);
126
127
128 new_elements = kmalloc(new_min_nr * sizeof(*new_elements), gfp_mask);
129 if (!new_elements)
130 return -ENOMEM;
131
132 spin_lock_irqsave(&pool->lock, flags);
133 memcpy(new_elements, pool->elements,
134 pool->curr_nr * sizeof(*new_elements));
135 kfree(pool->elements);
136 pool->elements = new_elements;
137 pool->min_nr = new_min_nr;
138
139 while (pool->curr_nr < pool->min_nr) {
140 spin_unlock_irqrestore(&pool->lock, flags);
141 element = pool->alloc(gfp_mask, pool->pool_data);
142 if (!element)
143 goto out;
144 spin_lock_irqsave(&pool->lock, flags);
145 if (pool->curr_nr < pool->min_nr)
146 add_element(pool, element);
147 else
148 kfree(element);
149 }
150out_unlock:
151 spin_unlock_irqrestore(&pool->lock, flags);
152out:
153 return 0;
154}
155
156
157
158
159
160
161
162
163
164
165void mempool_destroy(mempool_t *pool)
166{
167 if (pool->curr_nr != pool->min_nr)
168 BUG();
169 free_pool(pool);
170}
171
172
173
174
175
176
177
178
179
180
181
182
183void * mempool_alloc(mempool_t *pool, int gfp_mask)
184{
185 void *element;
186 unsigned long flags;
187 int curr_nr;
188 DECLARE_WAITQUEUE(wait, current);
189 int gfp_nowait = gfp_mask & ~(__GFP_WAIT | __GFP_IO);
190 int pf_flags = current->flags;
191
192repeat_alloc:
193 current->flags |= PF_NOWARN;
194 element = pool->alloc(gfp_nowait, pool->pool_data);
195 current->flags = pf_flags;
196 if (likely(element != NULL))
197 return element;
198
199
200
201
202
203 if ((gfp_mask & __GFP_FS) && (gfp_mask != gfp_nowait) &&
204 (pool->curr_nr <= pool->min_nr/2)) {
205 element = pool->alloc(gfp_mask, pool->pool_data);
206 if (likely(element != NULL))
207 return element;
208 }
209
210
211
212
213 wakeup_bdflush();
214
215 spin_lock_irqsave(&pool->lock, flags);
216 if (likely(pool->curr_nr)) {
217 element = remove_element(pool);
218 spin_unlock_irqrestore(&pool->lock, flags);
219 return element;
220 }
221 spin_unlock_irqrestore(&pool->lock, flags);
222
223
224 if (gfp_mask == gfp_nowait)
225 return NULL;
226
227 blk_run_queues();
228
229 add_wait_queue_exclusive(&pool->wait, &wait);
230 set_task_state(current, TASK_UNINTERRUPTIBLE);
231
232 spin_lock_irqsave(&pool->lock, flags);
233 curr_nr = pool->curr_nr;
234 spin_unlock_irqrestore(&pool->lock, flags);
235
236 if (!curr_nr)
237 schedule();
238
239 current->state = TASK_RUNNING;
240 remove_wait_queue(&pool->wait, &wait);
241
242 goto repeat_alloc;
243}
244
245
246
247
248
249
250
251
252
253void mempool_free(void *element, mempool_t *pool)
254{
255 unsigned long flags;
256
257 if (pool->curr_nr < pool->min_nr) {
258 spin_lock_irqsave(&pool->lock, flags);
259 if (pool->curr_nr < pool->min_nr) {
260 add_element(pool, element);
261 spin_unlock_irqrestore(&pool->lock, flags);
262 wake_up(&pool->wait);
263 return;
264 }
265 spin_unlock_irqrestore(&pool->lock, flags);
266 }
267 pool->free(element, pool->pool_data);
268}
269
270EXPORT_SYMBOL(mempool_create);
271EXPORT_SYMBOL(mempool_resize);
272EXPORT_SYMBOL(mempool_destroy);
273EXPORT_SYMBOL(mempool_alloc);
274EXPORT_SYMBOL(mempool_free);
275