Merge branch 'asprintf' of https://github.com/bvanassche/fio
[fio.git] / smalloc.c
CommitLineData
d24c33a4
JA
1/*
2 * simple memory allocator, backed by mmap() so that it hands out memory
3 * that can be shared across processes and threads
4 */
5#include <sys/mman.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <assert.h>
9#include <string.h>
10#include <unistd.h>
e43606c2 11#include <inttypes.h>
d24c33a4
JA
12#include <sys/types.h>
13#include <limits.h>
3a8600b4 14#include <fcntl.h>
0ffccc21
BVA
15#ifdef CONFIG_VALGRIND_DEV
16#include <valgrind/valgrind.h>
17#else
18#define RUNNING_ON_VALGRIND 0
19#define VALGRIND_MALLOCLIKE_BLOCK(addr, size, rzB, is_zeroed) do { } while (0)
20#define VALGRIND_FREELIKE_BLOCK(addr, rzB) do { } while (0)
21#endif
d24c33a4 22
248c9436 23#include "fio.h"
971caeb1 24#include "fio_sem.h"
b3268b92 25#include "arch/arch.h"
3a8600b4 26#include "os/os.h"
10aa136b 27#include "smalloc.h"
b0f0326a 28#include "log.h"
d24c33a4 29
55f6491d 30#define SMALLOC_REDZONE /* define to detect memory corruption */
d24c33a4 31
ec996e9c
JA
32#define SMALLOC_BPB 32 /* block size, bytes-per-bit in bitmap */
33#define SMALLOC_BPI (sizeof(unsigned int) * 8)
34#define SMALLOC_BPL (SMALLOC_BPB * SMALLOC_BPI)
35
23bd40f9 36#define INITIAL_SIZE 16*1024*1024 /* new pool size */
5f9454a2
JA
37#define INITIAL_POOLS 8 /* maximum number of pools to setup */
38
39#define MAX_POOLS 16
d24c33a4 40
55f6491d
JA
41#define SMALLOC_PRE_RED 0xdeadbeefU
42#define SMALLOC_POST_RED 0x5aa55aa5U
55f6491d 43
2b386d25 44unsigned int smalloc_pool_size = INITIAL_SIZE;
aa1af5fd 45#ifdef SMALLOC_REDZONE
10aa136b 46static const int int_mask = sizeof(int) - 1;
aa1af5fd 47#endif
2b386d25 48
d24c33a4 49struct pool {
971caeb1 50 struct fio_sem *lock; /* protects this pool */
d24c33a4 51 void *map; /* map of blocks */
ec996e9c 52 unsigned int *bitmap; /* blocks free/busy map */
a3ebe7e0
JA
53 size_t free_blocks; /* free blocks */
54 size_t nr_blocks; /* total blocks */
55 size_t next_non_full;
56 size_t mmap_size;
ec996e9c
JA
57};
58
0ffccc21
BVA
59#ifdef SMALLOC_REDZONE
60#define REDZONE_SIZE sizeof(unsigned int)
61#else
62#define REDZONE_SIZE 0
63#endif
64
ec996e9c 65struct block_hdr {
a3ebe7e0 66 size_t size;
ec996e9c
JA
67#ifdef SMALLOC_REDZONE
68 unsigned int prered;
69#endif
d24c33a4
JA
70};
71
72static struct pool mp[MAX_POOLS];
73static unsigned int nr_pools;
74static unsigned int last_pool;
d24c33a4 75
d24c33a4
JA
76static inline int ptr_valid(struct pool *pool, void *ptr)
77{
dcb69098 78 unsigned int pool_size = pool->nr_blocks * SMALLOC_BPL;
ec996e9c
JA
79
80 return (ptr >= pool->map) && (ptr < pool->map + pool_size);
d24c33a4
JA
81}
82
a3ebe7e0 83static inline size_t size_to_blocks(size_t size)
808e9ea8
JA
84{
85 return (size + SMALLOC_BPB - 1) / SMALLOC_BPB;
86}
87
dcb69098 88static int blocks_iter(struct pool *pool, unsigned int pool_idx,
a3ebe7e0 89 unsigned int idx, size_t nr_blocks,
ec996e9c 90 int (*func)(unsigned int *map, unsigned int mask))
d24c33a4 91{
dcb69098 92
ec996e9c
JA
93 while (nr_blocks) {
94 unsigned int this_blocks, mask;
dcb69098
JA
95 unsigned int *map;
96
97 if (pool_idx >= pool->nr_blocks)
98 return 0;
99
100 map = &pool->bitmap[pool_idx];
ec996e9c
JA
101
102 this_blocks = nr_blocks;
103 if (this_blocks + idx > SMALLOC_BPI) {
104 this_blocks = SMALLOC_BPI - idx;
105 idx = SMALLOC_BPI - this_blocks;
106 }
107
108 if (this_blocks == SMALLOC_BPI)
109 mask = -1U;
110 else
111 mask = ((1U << this_blocks) - 1) << idx;
112
113 if (!func(map, mask))
114 return 0;
115
116 nr_blocks -= this_blocks;
117 idx = 0;
dcb69098 118 pool_idx++;
ec996e9c
JA
119 }
120
121 return 1;
d24c33a4
JA
122}
123
ec996e9c 124static int mask_cmp(unsigned int *map, unsigned int mask)
d24c33a4 125{
ec996e9c 126 return !(*map & mask);
d24c33a4
JA
127}
128
ec996e9c 129static int mask_clear(unsigned int *map, unsigned int mask)
d24c33a4 130{
dcb69098 131 assert((*map & mask) == mask);
ec996e9c
JA
132 *map &= ~mask;
133 return 1;
d24c33a4
JA
134}
135
ec996e9c 136static int mask_set(unsigned int *map, unsigned int mask)
d24c33a4 137{
dcb69098 138 assert(!(*map & mask));
ec996e9c
JA
139 *map |= mask;
140 return 1;
d24c33a4
JA
141}
142
dcb69098 143static int blocks_free(struct pool *pool, unsigned int pool_idx,
a3ebe7e0 144 unsigned int idx, size_t nr_blocks)
d24c33a4 145{
dcb69098 146 return blocks_iter(pool, pool_idx, idx, nr_blocks, mask_cmp);
d24c33a4
JA
147}
148
dcb69098 149static void set_blocks(struct pool *pool, unsigned int pool_idx,
a3ebe7e0 150 unsigned int idx, size_t nr_blocks)
d24c33a4 151{
dcb69098 152 blocks_iter(pool, pool_idx, idx, nr_blocks, mask_set);
d24c33a4
JA
153}
154
dcb69098 155static void clear_blocks(struct pool *pool, unsigned int pool_idx,
a3ebe7e0 156 unsigned int idx, size_t nr_blocks)
d24c33a4 157{
dcb69098 158 blocks_iter(pool, pool_idx, idx, nr_blocks, mask_clear);
d24c33a4
JA
159}
160
ec996e9c
JA
161static int find_next_zero(int word, int start)
162{
163 assert(word != -1U);
271067a6
JH
164 word >>= start;
165 return ffz(word) + start;
d24c33a4
JA
166}
167
5f9454a2 168static bool add_pool(struct pool *pool, unsigned int alloc_size)
d24c33a4 169{
8d5844e9 170 int bitmap_blocks;
c8931876 171 int mmap_flags;
b8a6582e 172 void *ptr;
ec996e9c 173
5f9454a2
JA
174 if (nr_pools == MAX_POOLS)
175 return false;
176
55f6491d 177#ifdef SMALLOC_REDZONE
ec996e9c 178 alloc_size += sizeof(unsigned int);
55f6491d 179#endif
ec996e9c
JA
180 alloc_size += sizeof(struct block_hdr);
181 if (alloc_size < INITIAL_SIZE)
182 alloc_size = INITIAL_SIZE;
183
184 /* round up to nearest full number of blocks */
185 alloc_size = (alloc_size + SMALLOC_BPL - 1) & ~(SMALLOC_BPL - 1);
186 bitmap_blocks = alloc_size / SMALLOC_BPL;
187 alloc_size += bitmap_blocks * sizeof(unsigned int);
188 pool->mmap_size = alloc_size;
0b9d69ec 189
ec996e9c
JA
190 pool->nr_blocks = bitmap_blocks;
191 pool->free_blocks = bitmap_blocks * SMALLOC_BPB;
adf57099 192
c8931876
JA
193 mmap_flags = OS_MAP_ANON;
194#ifdef CONFIG_ESX
195 mmap_flags |= MAP_PRIVATE;
196#else
197 mmap_flags |= MAP_SHARED;
198#endif
199 ptr = mmap(NULL, alloc_size, PROT_READ|PROT_WRITE, mmap_flags, -1, 0);
200
d24c33a4 201 if (ptr == MAP_FAILED)
8d5844e9 202 goto out_fail;
d24c33a4 203
ec996e9c 204 pool->map = ptr;
17f7fcd0 205 pool->bitmap = (unsigned int *)((char *) ptr + (pool->nr_blocks * SMALLOC_BPL));
9c3e13e3 206 memset(pool->bitmap, 0, bitmap_blocks * sizeof(unsigned int));
d24c33a4 207
971caeb1 208 pool->lock = fio_sem_init(FIO_SEM_UNLOCKED);
d24c33a4 209 if (!pool->lock)
8d5844e9 210 goto out_fail;
d24c33a4 211
d24c33a4 212 nr_pools++;
5f9454a2 213 return true;
8d5844e9 214out_fail:
b0f0326a 215 log_err("smalloc: failed adding pool\n");
d24c33a4 216 if (pool->map)
ec996e9c 217 munmap(pool->map, pool->mmap_size);
5f9454a2 218 return false;
d24c33a4
JA
219}
220
221void sinit(void)
222{
5f9454a2
JA
223 bool ret;
224 int i;
d24c33a4 225
5f9454a2
JA
226 for (i = 0; i < INITIAL_POOLS; i++) {
227 ret = add_pool(&mp[nr_pools], smalloc_pool_size);
228 if (!ret)
85492cb8
JA
229 break;
230 }
231
232 /*
233 * If we added at least one pool, we should be OK for most
234 * cases.
235 */
236 assert(i);
d24c33a4
JA
237}
238
239static void cleanup_pool(struct pool *pool)
240{
443bb114
JA
241 /*
242 * This will also remove the temporary file we used as a backing
243 * store, it was already unlinked
244 */
ec996e9c 245 munmap(pool->map, pool->mmap_size);
6548f47f
JA
246
247 if (pool->lock)
971caeb1 248 fio_sem_remove(pool->lock);
d24c33a4
JA
249}
250
251void scleanup(void)
252{
253 unsigned int i;
254
255 for (i = 0; i < nr_pools; i++)
256 cleanup_pool(&mp[i]);
d24c33a4
JA
257}
258
89da54e8 259#ifdef SMALLOC_REDZONE
cf98708d
JA
260static void *postred_ptr(struct block_hdr *hdr)
261{
e43606c2 262 uintptr_t ptr;
cf98708d 263
e43606c2 264 ptr = (uintptr_t) hdr + hdr->size - sizeof(unsigned int);
248c9436 265 ptr = (uintptr_t) PTR_ALIGN(ptr, int_mask);
cf98708d
JA
266
267 return (void *) ptr;
268}
269
ec996e9c 270static void fill_redzone(struct block_hdr *hdr)
55f6491d 271{
cf98708d 272 unsigned int *postred = postred_ptr(hdr);
55f6491d 273
0ffccc21
BVA
274 /* Let Valgrind fill the red zones. */
275 if (RUNNING_ON_VALGRIND)
276 return;
277
ec996e9c
JA
278 hdr->prered = SMALLOC_PRE_RED;
279 *postred = SMALLOC_POST_RED;
ec996e9c 280}
55f6491d 281
ec996e9c
JA
282static void sfree_check_redzone(struct block_hdr *hdr)
283{
cf98708d 284 unsigned int *postred = postred_ptr(hdr);
ec996e9c 285
0ffccc21
BVA
286 /* Let Valgrind check the red zones. */
287 if (RUNNING_ON_VALGRIND)
288 return;
289
ec996e9c 290 if (hdr->prered != SMALLOC_PRE_RED) {
b0f0326a
JA
291 log_err("smalloc pre redzone destroyed!\n"
292 " ptr=%p, prered=%x, expected %x\n",
ec996e9c 293 hdr, hdr->prered, SMALLOC_PRE_RED);
55f6491d
JA
294 assert(0);
295 }
296 if (*postred != SMALLOC_POST_RED) {
b0f0326a
JA
297 log_err("smalloc post redzone destroyed!\n"
298 " ptr=%p, postred=%x, expected %x\n",
ec996e9c 299 hdr, *postred, SMALLOC_POST_RED);
55f6491d
JA
300 assert(0);
301 }
89da54e8
JA
302}
303#else
304static void fill_redzone(struct block_hdr *hdr)
305{
55f6491d
JA
306}
307
89da54e8
JA
308static void sfree_check_redzone(struct block_hdr *hdr)
309{
310}
311#endif
312
d24c33a4
JA
313static void sfree_pool(struct pool *pool, void *ptr)
314{
ec996e9c 315 struct block_hdr *hdr;
179446e0 316 unsigned int i, idx;
ec996e9c 317 unsigned long offset;
d24c33a4
JA
318
319 if (!ptr)
320 return;
321
ec996e9c
JA
322 ptr -= sizeof(*hdr);
323 hdr = ptr;
55f6491d 324
d24c33a4
JA
325 assert(ptr_valid(pool, ptr));
326
ec996e9c 327 sfree_check_redzone(hdr);
d24c33a4 328
ec996e9c
JA
329 offset = ptr - pool->map;
330 i = offset / SMALLOC_BPL;
331 idx = (offset % SMALLOC_BPL) / SMALLOC_BPB;
d24c33a4 332
971caeb1 333 fio_sem_down(pool->lock);
dcb69098 334 clear_blocks(pool, i, idx, size_to_blocks(hdr->size));
ec996e9c
JA
335 if (i < pool->next_non_full)
336 pool->next_non_full = i;
179446e0 337 pool->free_blocks += size_to_blocks(hdr->size);
971caeb1 338 fio_sem_up(pool->lock);
d24c33a4
JA
339}
340
341void sfree(void *ptr)
342{
343 struct pool *pool = NULL;
344 unsigned int i;
345
8e5732e5
JA
346 if (!ptr)
347 return;
348
d24c33a4
JA
349 for (i = 0; i < nr_pools; i++) {
350 if (ptr_valid(&mp[i], ptr)) {
351 pool = &mp[i];
352 break;
353 }
354 }
355
45a65144 356 if (pool) {
0ffccc21 357 VALGRIND_FREELIKE_BLOCK(ptr, REDZONE_SIZE);
45a65144
JA
358 sfree_pool(pool, ptr);
359 return;
360 }
361
362 log_err("smalloc: ptr %p not from smalloc pool\n", ptr);
d24c33a4
JA
363}
364
a3ebe7e0 365static void *__smalloc_pool(struct pool *pool, size_t size)
d24c33a4 366{
a3ebe7e0 367 size_t nr_blocks;
ec996e9c
JA
368 unsigned int i;
369 unsigned int offset;
370 unsigned int last_idx;
371 void *ret = NULL;
d24c33a4 372
971caeb1 373 fio_sem_down(pool->lock);
179446e0
JA
374
375 nr_blocks = size_to_blocks(size);
ec996e9c 376 if (nr_blocks > pool->free_blocks)
8e5732e5 377 goto fail;
5ec10eaa 378
ec996e9c
JA
379 i = pool->next_non_full;
380 last_idx = 0;
381 offset = -1U;
382 while (i < pool->nr_blocks) {
383 unsigned int idx;
d24c33a4 384
ec996e9c
JA
385 if (pool->bitmap[i] == -1U) {
386 i++;
387 pool->next_non_full = i;
388 last_idx = 0;
389 continue;
390 }
d24c33a4 391
ec996e9c 392 idx = find_next_zero(pool->bitmap[i], last_idx);
dcb69098 393 if (!blocks_free(pool, i, idx, nr_blocks)) {
ec996e9c
JA
394 idx += nr_blocks;
395 if (idx < SMALLOC_BPI)
396 last_idx = idx;
397 else {
398 last_idx = 0;
399 while (idx >= SMALLOC_BPI) {
400 i++;
401 idx -= SMALLOC_BPI;
402 }
403 }
404 continue;
d24c33a4 405 }
dcb69098 406 set_blocks(pool, i, idx, nr_blocks);
ec996e9c
JA
407 offset = i * SMALLOC_BPL + idx * SMALLOC_BPB;
408 break;
409 }
410
411 if (i < pool->nr_blocks) {
412 pool->free_blocks -= nr_blocks;
413 ret = pool->map + offset;
d24c33a4 414 }
ec996e9c 415fail:
971caeb1 416 fio_sem_up(pool->lock);
ec996e9c 417 return ret;
d24c33a4
JA
418}
419
a3ebe7e0 420static void *smalloc_pool(struct pool *pool, size_t size)
55f6491d 421{
a3ebe7e0 422 size_t alloc_size = size + sizeof(struct block_hdr);
55f6491d
JA
423 void *ptr;
424
cf98708d 425 /*
122426da
JA
426 * Round to int alignment, so that the postred pointer will
427 * be naturally aligned as well.
cf98708d 428 */
ec996e9c 429#ifdef SMALLOC_REDZONE
122426da
JA
430 alloc_size += sizeof(unsigned int);
431 alloc_size = (alloc_size + int_mask) & ~int_mask;
ec996e9c
JA
432#endif
433
434 ptr = __smalloc_pool(pool, alloc_size);
89da54e8
JA
435 if (ptr) {
436 struct block_hdr *hdr = ptr;
55f6491d 437
89da54e8
JA
438 hdr->size = alloc_size;
439 fill_redzone(hdr);
55f6491d 440
89da54e8
JA
441 ptr += sizeof(*hdr);
442 memset(ptr, 0, size);
443 }
ec996e9c 444
55f6491d 445 return ptr;
55f6491d
JA
446}
447
0ffccc21 448static void *__smalloc(size_t size, bool is_zeroed)
d24c33a4 449{
85492cb8 450 unsigned int i, end_pool;
d24c33a4 451
7982aa7d
JA
452 if (size != (unsigned int) size)
453 return NULL;
454
d24c33a4 455 i = last_pool;
85492cb8 456 end_pool = nr_pools;
d24c33a4
JA
457
458 do {
85492cb8 459 for (; i < end_pool; i++) {
d24c33a4
JA
460 void *ptr = smalloc_pool(&mp[i], size);
461
462 if (ptr) {
463 last_pool = i;
0ffccc21
BVA
464 VALGRIND_MALLOCLIKE_BLOCK(ptr, size,
465 REDZONE_SIZE,
466 is_zeroed);
d24c33a4
JA
467 return ptr;
468 }
469 }
470 if (last_pool) {
85492cb8
JA
471 end_pool = last_pool;
472 last_pool = i = 0;
d24c33a4
JA
473 continue;
474 }
475
85492cb8 476 break;
d24c33a4
JA
477 } while (1);
478
81b3c86f
JA
479 log_err("smalloc: OOM. Consider using --alloc-size to increase the "
480 "shared memory available.\n");
d24c33a4
JA
481 return NULL;
482}
483
0ffccc21
BVA
484void *smalloc(size_t size)
485{
486 return __smalloc(size, false);
487}
488
544992f7
JA
489void *scalloc(size_t nmemb, size_t size)
490{
0ffccc21 491 return __smalloc(nmemb * size, true);
544992f7
JA
492}
493
d24c33a4
JA
494char *smalloc_strdup(const char *str)
495{
2894a2d4 496 char *ptr = NULL;
d24c33a4
JA
497
498 ptr = smalloc(strlen(str) + 1);
2894a2d4
CE
499 if (ptr)
500 strcpy(ptr, str);
d24c33a4
JA
501 return ptr;
502}