2 * UCW Library -- Memory Pools (One-Time Allocation)
4 * (c) 1997--2014 Martin Mares <mj@ucw.cz>
5 * (c) 2007--2017 Pavel Charvat <pchar@ucw.cz>
7 * This software may be freely distributed and used according to the terms
8 * of the GNU Lesser General Public License.
14 #include <ucw/alloc.h>
15 #include <ucw/mempool.h>
19 #define MP_CHUNK_TAIL ALIGN_TO(sizeof(struct mempool_chunk), CPU_STRUCT_ALIGN)
20 #define MP_SIZE_MAX (SIZE_MAX - MP_CHUNK_TAIL - CPU_PAGE_SIZE)
22 struct mempool_chunk {
24 struct mempool *pool; // Can be useful when analysing coredump for memory leaks
26 struct mempool_chunk *next;
31 mp_align_size(size_t size)
33 #ifdef CONFIG_UCW_POOL_IS_MMAP
34 size = MAX(size, 64 + MP_CHUNK_TAIL);
35 return ALIGN_TO(size, CPU_PAGE_SIZE) - MP_CHUNK_TAIL;
37 return ALIGN_TO(size, CPU_STRUCT_ALIGN);
41 static void *mp_allocator_alloc(struct ucw_allocator *a, size_t size)
43 struct mempool *mp = (struct mempool *) a;
44 return mp_alloc_fast(mp, size);
47 static void *mp_allocator_realloc(struct ucw_allocator *a, void *ptr, size_t old_size, size_t new_size)
49 if (new_size <= old_size)
53 * In the future, we might want to do something like mp_realloc(),
54 * but we have to check that it is indeed the last block in the pool.
56 struct mempool *mp = (struct mempool *) a;
57 void *new = mp_alloc_fast(mp, new_size);
58 memcpy(new, ptr, old_size);
62 static void mp_allocator_free(struct ucw_allocator *a UNUSED, void *ptr UNUSED)
68 mp_init(struct mempool *pool, size_t chunk_size)
70 chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
71 *pool = (struct mempool) {
73 .alloc = mp_allocator_alloc,
74 .realloc = mp_allocator_realloc,
75 .free = mp_allocator_free,
77 .chunk_size = chunk_size,
78 .threshold = chunk_size >> 1,
79 .last_big = &pool->last_big
84 mp_new_big_chunk(struct mempool *pool, size_t size)
86 struct mempool_chunk *chunk;
87 chunk = xmalloc(size + MP_CHUNK_TAIL) + size;
90 pool->total_size += size + MP_CHUNK_TAIL;
95 mp_free_big_chunk(struct mempool *pool, struct mempool_chunk *chunk)
97 pool->total_size -= chunk->size + MP_CHUNK_TAIL;
98 xfree((void *)chunk - chunk->size);
102 mp_new_chunk(struct mempool *pool, size_t size)
104 #ifdef CONFIG_UCW_POOL_IS_MMAP
105 struct mempool_chunk *chunk;
106 chunk = page_alloc(size + MP_CHUNK_TAIL) + size;
109 pool->total_size += size + MP_CHUNK_TAIL;
112 return mp_new_big_chunk(pool, size);
117 mp_free_chunk(struct mempool *pool, struct mempool_chunk *chunk)
119 #ifdef CONFIG_UCW_POOL_IS_MMAP
120 pool->total_size -= chunk->size + MP_CHUNK_TAIL;
121 page_free((void *)chunk - chunk->size, chunk->size + MP_CHUNK_TAIL);
123 mp_free_big_chunk(pool, chunk);
128 mp_new(size_t chunk_size)
130 chunk_size = mp_align_size(MAX(sizeof(struct mempool), chunk_size));
131 struct mempool_chunk *chunk = mp_new_chunk(NULL, chunk_size);
132 struct mempool *pool = (void *)chunk - chunk_size;
133 DBG("Creating mempool %p with %zu bytes long chunks", pool, chunk_size);
138 *pool = (struct mempool) {
140 .alloc = mp_allocator_alloc,
141 .realloc = mp_allocator_realloc,
142 .free = mp_allocator_free,
144 .state = { .free = { chunk_size - sizeof(*pool) }, .last = { chunk } },
145 .chunk_size = chunk_size,
146 .threshold = chunk_size >> 1,
147 .last_big = &pool->last_big,
148 .total_size = chunk->size + MP_CHUNK_TAIL,
154 mp_free_chain(struct mempool *pool, struct mempool_chunk *chunk)
158 struct mempool_chunk *next = chunk->next;
159 mp_free_chunk(pool, chunk);
165 mp_free_big_chain(struct mempool *pool, struct mempool_chunk *chunk)
169 struct mempool_chunk *next = chunk->next;
170 mp_free_big_chunk(pool, chunk);
176 mp_delete(struct mempool *pool)
178 DBG("Deleting mempool %p", pool);
179 mp_free_big_chain(pool, pool->state.last[1]);
180 mp_free_chain(pool, pool->unused);
181 mp_free_chain(pool, pool->state.last[0]); // can contain the mempool structure
185 mp_flush(struct mempool *pool)
187 mp_free_big_chain(pool, pool->state.last[1]);
188 struct mempool_chunk *chunk, *next;
189 for (chunk = pool->state.last[0]; chunk && (void *)chunk - chunk->size != pool; chunk = next)
192 chunk->next = pool->unused;
193 pool->unused = chunk;
195 pool->state.last[0] = chunk;
196 pool->state.free[0] = chunk ? chunk->size - sizeof(*pool) : 0;
197 pool->state.last[1] = NULL;
198 pool->state.free[1] = 0;
199 pool->state.next = NULL;
200 pool->last_big = &pool->last_big;
204 mp_stats_chain(struct mempool *pool, struct mempool_chunk *chunk, struct mempool_stats *stats, uint idx)
208 stats->chain_size[idx] += chunk->size + MP_CHUNK_TAIL;
209 stats->chain_count[idx]++;
212 stats->used_size += chunk->size;
213 if ((byte *)pool == (byte *)chunk - chunk->size)
214 stats->used_size -= sizeof(*pool);
218 stats->total_size += stats->chain_size[idx];
222 mp_stats(struct mempool *pool, struct mempool_stats *stats)
224 bzero(stats, sizeof(*stats));
225 mp_stats_chain(pool, pool->state.last[0], stats, 0);
226 mp_stats_chain(pool, pool->state.last[1], stats, 1);
227 mp_stats_chain(pool, pool->unused, stats, 2);
228 stats->used_size -= pool->state.free[0] + pool->state.free[1];
229 ASSERT(stats->total_size == pool->total_size);
230 ASSERT(stats->used_size <= stats->total_size);
234 mp_total_size(struct mempool *pool)
236 return pool->total_size;
240 mp_shrink(struct mempool *pool, u64 min_total_size)
244 struct mempool_chunk *chunk = pool->unused;
245 if (!chunk || pool->total_size - (chunk->size + MP_CHUNK_TAIL) < min_total_size)
247 pool->unused = chunk->next;
248 mp_free_chunk(pool, chunk);
253 mp_alloc_internal(struct mempool *pool, size_t size)
255 struct mempool_chunk *chunk;
256 if (size <= pool->threshold)
261 chunk = pool->unused;
262 pool->unused = chunk->next;
266 chunk = mp_new_chunk(pool, pool->chunk_size);
271 chunk->next = pool->state.last[0];
272 pool->state.last[0] = chunk;
273 pool->state.free[0] = pool->chunk_size - size;
274 return (void *)chunk - pool->chunk_size;
276 else if (likely(size <= MP_SIZE_MAX))
279 size_t aligned = ALIGN_TO(size, CPU_STRUCT_ALIGN);
280 chunk = mp_new_big_chunk(pool, aligned);
281 chunk->next = pool->state.last[1];
285 pool->state.last[1] = chunk;
286 pool->state.free[1] = aligned - size;
287 return pool->last_big = (void *)chunk - aligned;
290 die("Cannot allocate %zu bytes from a mempool", size);
294 mp_alloc(struct mempool *pool, size_t size)
296 return mp_alloc_fast(pool, size);
300 mp_alloc_noalign(struct mempool *pool, size_t size)
302 return mp_alloc_fast_noalign(pool, size);
306 mp_alloc_zero(struct mempool *pool, size_t size)
308 void *ptr = mp_alloc_fast(pool, size);
314 mp_start_internal(struct mempool *pool, size_t size)
316 void *ptr = mp_alloc_internal(pool, size);
317 pool->state.free[pool->idx] += size;
322 mp_start(struct mempool *pool, size_t size)
324 return mp_start_fast(pool, size);
328 mp_start_noalign(struct mempool *pool, size_t size)
330 return mp_start_fast_noalign(pool, size);
334 mp_grow_internal(struct mempool *pool, size_t size)
336 if (unlikely(size > MP_SIZE_MAX))
337 die("Cannot allocate %zu bytes of memory", size);
338 size_t avail = mp_avail(pool);
339 void *ptr = mp_ptr(pool);
342 size_t amortized = likely(avail <= MP_SIZE_MAX / 2) ? avail * 2 : MP_SIZE_MAX;
343 amortized = MAX(amortized, size);
344 amortized = ALIGN_TO(amortized, CPU_STRUCT_ALIGN);
345 struct mempool_chunk *chunk = pool->state.last[1], *next = chunk->next;
346 pool->total_size = pool->total_size - chunk->size + amortized;
347 ptr = xrealloc(ptr, amortized + MP_CHUNK_TAIL);
348 chunk = ptr + amortized;
350 chunk->size = amortized;
351 pool->state.last[1] = chunk;
352 pool->state.free[1] = amortized;
353 pool->last_big = ptr;
358 void *p = mp_start_internal(pool, size);
359 memcpy(p, ptr, avail);
365 mp_open(struct mempool *pool, void *ptr)
367 return mp_open_fast(pool, ptr);
371 mp_realloc(struct mempool *pool, void *ptr, size_t size)
373 return mp_realloc_fast(pool, ptr, size);
377 mp_realloc_zero(struct mempool *pool, void *ptr, size_t size)
379 size_t old_size = mp_open_fast(pool, ptr);
380 ptr = mp_grow(pool, size);
382 bzero(ptr + old_size, size - old_size);
383 mp_end(pool, ptr + size);
388 mp_spread_internal(struct mempool *pool, void *p, size_t size)
390 void *old = mp_ptr(pool);
391 void *new = mp_grow_internal(pool, p-old+size);
396 mp_restore(struct mempool *pool, struct mempool_state *state)
398 struct mempool_chunk *chunk, *next;
399 struct mempool_state s = *state;
400 for (chunk = pool->state.last[0]; chunk != s.last[0]; chunk = next)
403 chunk->next = pool->unused;
404 pool->unused = chunk;
406 for (chunk = pool->state.last[1]; chunk != s.last[1]; chunk = next)
409 mp_free_big_chunk(pool, chunk);
412 pool->last_big = &pool->last_big;
415 struct mempool_state *
416 mp_push(struct mempool *pool)
418 struct mempool_state state = pool->state;
419 struct mempool_state *p = mp_alloc_fast(pool, sizeof(*p));
421 pool->state.next = p;
426 mp_pop(struct mempool *pool)
428 ASSERT(pool->state.next);
429 mp_restore(pool, pool->state.next);
434 #include <ucw/getopt.h>
440 fill(byte *ptr, uint len, uint magic)
443 *ptr++ = (magic++ & 255);
447 check(byte *ptr, uint len, uint magic, uint align)
449 ASSERT(!((uintptr_t)ptr & (align - 1)));
451 if (*ptr++ != (magic++ & 255))
455 int main(int argc, char **argv)
460 if (cf_getopt(argc, argv, CF_SHORT_OPTS, CF_NO_LONG_OPTS, NULL) >= 0 || argc != optind)
461 die("Invalid usage");
463 uint max = 1000, n = 0, m = 0, can_realloc = 0;
465 struct mempool_state *state[max];
466 uint len[max], num[max], align[max];
467 struct mempool *mp = mp_new(128), mp_static;
469 for (uint i = 0; i < 5000; i++)
471 for (uint j = 0; j < n; j++)
472 check(ptr[j], len[j], j, align[j]);
474 DBG("free_small=%u free_big=%u idx=%u chunk_size=%u last_big=%p", mp->state.free[0], mp->state.free[1], mp->idx, mp->chunk_size, mp->last_big);
475 for (struct mempool_chunk *ch = mp->state.last[0]; ch; ch = ch->next)
476 DBG("small %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
477 for (struct mempool_chunk *ch = mp->state.last[1]; ch; ch = ch->next)
478 DBG("big %p %p %p %d", (byte *)ch - ch->size, ch, ch + 1, ch->size);
480 int r = random_max(100);
487 else if ((r -= 1) < 0)
492 mp = mp_new(random_max(0x1000) + 1);
494 mp = &mp_static, mp_init(mp, random_max(512) + 1);
497 else if (n < max && (r -= 30) < 0)
499 len[n] = random_max(0x2000);
500 DBG("alloc(%u)", len[n]);
501 align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
502 ptr[n] = (align[n] == 1) ? mp_alloc_fast_noalign(mp, len[n]) : mp_alloc_fast(mp, len[n]);
503 DBG(" -> (%p)", ptr[n]);
504 fill(ptr[n], len[n], n);
508 else if (n < max && (r -= 20) < 0)
510 len[n] = random_max(0x2000);
511 DBG("start(%u)", len[n]);
512 align[n] = random_max(2) ? CPU_STRUCT_ALIGN : 1;
513 ptr[n] = (align[n] == 1) ? mp_start_fast_noalign(mp, len[n]) : mp_start_fast(mp, len[n]);
514 DBG(" -> (%p)", ptr[n]);
515 fill(ptr[n], len[n], n);
520 else if (can_realloc && n && (r -= 10) < 0)
522 if (mp_open(mp, ptr[n - 1]) != len[n - 1])
527 for (uint i = random_max(4); i--; )
530 len[k] = random_max(0x2000);
531 DBG("grow(%u)", len[k]);
532 ptr[k] = mp_grow(mp, len[k]);
533 DBG(" -> (%p)", ptr[k]);
534 check(ptr[k], MIN(l, len[k]), k, align[k]);
535 fill(ptr[k], len[k], k);
537 mp_end(mp, ptr[k] + len[k]);
540 else if (can_realloc && n && (r -= 20) < 0)
542 uint i = n - 1, l = len[i];
543 DBG("realloc(%p, %u)", ptr[i], len[i]);
544 ptr[i] = mp_realloc(mp, ptr[i], len[i] = random_max(0x2000));
545 DBG(" -> (%p, %u)", ptr[i], len[i]);
546 check(ptr[i], MIN(len[i], l), i, align[i]);
547 fill(ptr[i], len[i], i);
549 else if (m < max && (r -= 5) < 0)
553 state[m++] = mp_push(mp);
556 else if (m && (r -= 2) < 0)
564 else if (m && (r -= 1) < 0)
566 uint i = random_max(m);
567 DBG("restore(%u)", i);
568 mp_restore(mp, state[i]);
572 else if (can_realloc && n && (r -= 5) < 0)
573 ASSERT(mp_size(mp, ptr[n - 1]) == len[n - 1]);
576 struct mempool_stats stats;
577 mp_stats(mp, &stats);