Home | History | Annotate | Download | only in fio
      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>
     11 #include <inttypes.h>
     12 #include <sys/types.h>
     13 #include <limits.h>
     14 #include <fcntl.h>
     15 
     16 #include "mutex.h"
     17 #include "arch/arch.h"
     18 #include "os/os.h"
     19 #include "smalloc.h"
     20 
     21 #define SMALLOC_REDZONE		/* define to detect memory corruption */
     22 
     23 #define SMALLOC_BPB	32	/* block size, bytes-per-bit in bitmap */
     24 #define SMALLOC_BPI	(sizeof(unsigned int) * 8)
     25 #define SMALLOC_BPL	(SMALLOC_BPB * SMALLOC_BPI)
     26 
     27 #define INITIAL_SIZE	8192*1024	/* new pool size */
     28 #define MAX_POOLS	128		/* maximum number of pools to setup */
     29 
     30 #define SMALLOC_PRE_RED		0xdeadbeefU
     31 #define SMALLOC_POST_RED	0x5aa55aa5U
     32 
     33 unsigned int smalloc_pool_size = INITIAL_SIZE;
     34 static const int int_mask = sizeof(int) - 1;
     35 
     36 struct pool {
     37 	struct fio_mutex *lock;			/* protects this pool */
     38 	void *map;				/* map of blocks */
     39 	unsigned int *bitmap;			/* blocks free/busy map */
     40 	size_t free_blocks;		/* free blocks */
     41 	size_t nr_blocks;			/* total blocks */
     42 	size_t next_non_full;
     43 	size_t mmap_size;
     44 };
     45 
     46 struct block_hdr {
     47 	size_t size;
     48 #ifdef SMALLOC_REDZONE
     49 	unsigned int prered;
     50 #endif
     51 };
     52 
     53 static struct pool mp[MAX_POOLS];
     54 static unsigned int nr_pools;
     55 static unsigned int last_pool;
     56 static struct fio_rwlock *lock;
     57 
     58 static inline void pool_lock(struct pool *pool)
     59 {
     60 	fio_mutex_down(pool->lock);
     61 }
     62 
     63 static inline void pool_unlock(struct pool *pool)
     64 {
     65 	fio_mutex_up(pool->lock);
     66 }
     67 
     68 static inline void global_read_lock(void)
     69 {
     70 	fio_rwlock_read(lock);
     71 }
     72 
     73 static inline void global_read_unlock(void)
     74 {
     75 	fio_rwlock_unlock(lock);
     76 }
     77 
     78 static inline void global_write_lock(void)
     79 {
     80 	fio_rwlock_write(lock);
     81 }
     82 
     83 static inline void global_write_unlock(void)
     84 {
     85 	fio_rwlock_unlock(lock);
     86 }
     87 
     88 static inline int ptr_valid(struct pool *pool, void *ptr)
     89 {
     90 	unsigned int pool_size = pool->nr_blocks * SMALLOC_BPL;
     91 
     92 	return (ptr >= pool->map) && (ptr < pool->map + pool_size);
     93 }
     94 
     95 static inline size_t size_to_blocks(size_t size)
     96 {
     97 	return (size + SMALLOC_BPB - 1) / SMALLOC_BPB;
     98 }
     99 
    100 static int blocks_iter(struct pool *pool, unsigned int pool_idx,
    101 		       unsigned int idx, size_t nr_blocks,
    102 		       int (*func)(unsigned int *map, unsigned int mask))
    103 {
    104 
    105 	while (nr_blocks) {
    106 		unsigned int this_blocks, mask;
    107 		unsigned int *map;
    108 
    109 		if (pool_idx >= pool->nr_blocks)
    110 			return 0;
    111 
    112 		map = &pool->bitmap[pool_idx];
    113 
    114 		this_blocks = nr_blocks;
    115 		if (this_blocks + idx > SMALLOC_BPI) {
    116 			this_blocks = SMALLOC_BPI - idx;
    117 			idx = SMALLOC_BPI - this_blocks;
    118 		}
    119 
    120 		if (this_blocks == SMALLOC_BPI)
    121 			mask = -1U;
    122 		else
    123 			mask = ((1U << this_blocks) - 1) << idx;
    124 
    125 		if (!func(map, mask))
    126 			return 0;
    127 
    128 		nr_blocks -= this_blocks;
    129 		idx = 0;
    130 		pool_idx++;
    131 	}
    132 
    133 	return 1;
    134 }
    135 
    136 static int mask_cmp(unsigned int *map, unsigned int mask)
    137 {
    138 	return !(*map & mask);
    139 }
    140 
    141 static int mask_clear(unsigned int *map, unsigned int mask)
    142 {
    143 	assert((*map & mask) == mask);
    144 	*map &= ~mask;
    145 	return 1;
    146 }
    147 
    148 static int mask_set(unsigned int *map, unsigned int mask)
    149 {
    150 	assert(!(*map & mask));
    151 	*map |= mask;
    152 	return 1;
    153 }
    154 
    155 static int blocks_free(struct pool *pool, unsigned int pool_idx,
    156 		       unsigned int idx, size_t nr_blocks)
    157 {
    158 	return blocks_iter(pool, pool_idx, idx, nr_blocks, mask_cmp);
    159 }
    160 
    161 static void set_blocks(struct pool *pool, unsigned int pool_idx,
    162 		       unsigned int idx, size_t nr_blocks)
    163 {
    164 	blocks_iter(pool, pool_idx, idx, nr_blocks, mask_set);
    165 }
    166 
    167 static void clear_blocks(struct pool *pool, unsigned int pool_idx,
    168 			 unsigned int idx, size_t nr_blocks)
    169 {
    170 	blocks_iter(pool, pool_idx, idx, nr_blocks, mask_clear);
    171 }
    172 
    173 static int find_next_zero(int word, int start)
    174 {
    175 	assert(word != -1U);
    176 	word >>= start;
    177 	return ffz(word) + start;
    178 }
    179 
    180 static int add_pool(struct pool *pool, unsigned int alloc_size)
    181 {
    182 	int bitmap_blocks;
    183 	void *ptr;
    184 
    185 #ifdef SMALLOC_REDZONE
    186 	alloc_size += sizeof(unsigned int);
    187 #endif
    188 	alloc_size += sizeof(struct block_hdr);
    189 	if (alloc_size < INITIAL_SIZE)
    190 		alloc_size = INITIAL_SIZE;
    191 
    192 	/* round up to nearest full number of blocks */
    193 	alloc_size = (alloc_size + SMALLOC_BPL - 1) & ~(SMALLOC_BPL - 1);
    194 	bitmap_blocks = alloc_size / SMALLOC_BPL;
    195 	alloc_size += bitmap_blocks * sizeof(unsigned int);
    196 	pool->mmap_size = alloc_size;
    197 
    198 	pool->nr_blocks = bitmap_blocks;
    199 	pool->free_blocks = bitmap_blocks * SMALLOC_BPB;
    200 
    201 	ptr = mmap(NULL, alloc_size, PROT_READ|PROT_WRITE,
    202 			MAP_SHARED | OS_MAP_ANON, -1, 0);
    203 	if (ptr == MAP_FAILED)
    204 		goto out_fail;
    205 
    206 	memset(ptr, 0, alloc_size);
    207 	pool->map = ptr;
    208 	pool->bitmap = (void *) ptr + (pool->nr_blocks * SMALLOC_BPL);
    209 
    210 	pool->lock = fio_mutex_init(FIO_MUTEX_UNLOCKED);
    211 	if (!pool->lock)
    212 		goto out_fail;
    213 
    214 	nr_pools++;
    215 	return 0;
    216 out_fail:
    217 	fprintf(stderr, "smalloc: failed adding pool\n");
    218 	if (pool->map)
    219 		munmap(pool->map, pool->mmap_size);
    220 	return 1;
    221 }
    222 
    223 void sinit(void)
    224 {
    225 	int ret;
    226 
    227 	lock = fio_rwlock_init();
    228 	ret = add_pool(&mp[0], INITIAL_SIZE);
    229 	assert(!ret);
    230 }
    231 
    232 static void cleanup_pool(struct pool *pool)
    233 {
    234 	/*
    235 	 * This will also remove the temporary file we used as a backing
    236 	 * store, it was already unlinked
    237 	 */
    238 	munmap(pool->map, pool->mmap_size);
    239 
    240 	if (pool->lock)
    241 		fio_mutex_remove(pool->lock);
    242 }
    243 
    244 void scleanup(void)
    245 {
    246 	unsigned int i;
    247 
    248 	for (i = 0; i < nr_pools; i++)
    249 		cleanup_pool(&mp[i]);
    250 
    251 	if (lock)
    252 		fio_rwlock_remove(lock);
    253 }
    254 
    255 #ifdef SMALLOC_REDZONE
    256 static void *postred_ptr(struct block_hdr *hdr)
    257 {
    258 	uintptr_t ptr;
    259 
    260 	ptr = (uintptr_t) hdr + hdr->size - sizeof(unsigned int);
    261 	ptr = (ptr + int_mask) & ~int_mask;
    262 
    263 	return (void *) ptr;
    264 }
    265 
    266 static void fill_redzone(struct block_hdr *hdr)
    267 {
    268 	unsigned int *postred = postred_ptr(hdr);
    269 
    270 	hdr->prered = SMALLOC_PRE_RED;
    271 	*postred = SMALLOC_POST_RED;
    272 }
    273 
    274 static void sfree_check_redzone(struct block_hdr *hdr)
    275 {
    276 	unsigned int *postred = postred_ptr(hdr);
    277 
    278 	if (hdr->prered != SMALLOC_PRE_RED) {
    279 		fprintf(stderr, "smalloc pre redzone destroyed!\n");
    280 		fprintf(stderr, "  ptr=%p, prered=%x, expected %x\n",
    281 				hdr, hdr->prered, SMALLOC_PRE_RED);
    282 		assert(0);
    283 	}
    284 	if (*postred != SMALLOC_POST_RED) {
    285 		fprintf(stderr, "smalloc post redzone destroyed!\n");
    286 		fprintf(stderr, "  ptr=%p, postred=%x, expected %x\n",
    287 				hdr, *postred, SMALLOC_POST_RED);
    288 		assert(0);
    289 	}
    290 }
    291 #else
    292 static void fill_redzone(struct block_hdr *hdr)
    293 {
    294 }
    295 
    296 static void sfree_check_redzone(struct block_hdr *hdr)
    297 {
    298 }
    299 #endif
    300 
    301 static void sfree_pool(struct pool *pool, void *ptr)
    302 {
    303 	struct block_hdr *hdr;
    304 	unsigned int i, idx;
    305 	unsigned long offset;
    306 
    307 	if (!ptr)
    308 		return;
    309 
    310 	ptr -= sizeof(*hdr);
    311 	hdr = ptr;
    312 
    313 	assert(ptr_valid(pool, ptr));
    314 
    315 	sfree_check_redzone(hdr);
    316 
    317 	offset = ptr - pool->map;
    318 	i = offset / SMALLOC_BPL;
    319 	idx = (offset % SMALLOC_BPL) / SMALLOC_BPB;
    320 
    321 	pool_lock(pool);
    322 	clear_blocks(pool, i, idx, size_to_blocks(hdr->size));
    323 	if (i < pool->next_non_full)
    324 		pool->next_non_full = i;
    325 	pool->free_blocks += size_to_blocks(hdr->size);
    326 	pool_unlock(pool);
    327 }
    328 
    329 void sfree(void *ptr)
    330 {
    331 	struct pool *pool = NULL;
    332 	unsigned int i;
    333 
    334 	if (!ptr)
    335 		return;
    336 
    337 	global_read_lock();
    338 
    339 	for (i = 0; i < nr_pools; i++) {
    340 		if (ptr_valid(&mp[i], ptr)) {
    341 			pool = &mp[i];
    342 			break;
    343 		}
    344 	}
    345 
    346 	global_read_unlock();
    347 
    348 	assert(pool);
    349 	sfree_pool(pool, ptr);
    350 }
    351 
    352 static void *__smalloc_pool(struct pool *pool, size_t size)
    353 {
    354 	size_t nr_blocks;
    355 	unsigned int i;
    356 	unsigned int offset;
    357 	unsigned int last_idx;
    358 	void *ret = NULL;
    359 
    360 	pool_lock(pool);
    361 
    362 	nr_blocks = size_to_blocks(size);
    363 	if (nr_blocks > pool->free_blocks)
    364 		goto fail;
    365 
    366 	i = pool->next_non_full;
    367 	last_idx = 0;
    368 	offset = -1U;
    369 	while (i < pool->nr_blocks) {
    370 		unsigned int idx;
    371 
    372 		if (pool->bitmap[i] == -1U) {
    373 			i++;
    374 			pool->next_non_full = i;
    375 			last_idx = 0;
    376 			continue;
    377 		}
    378 
    379 		idx = find_next_zero(pool->bitmap[i], last_idx);
    380 		if (!blocks_free(pool, i, idx, nr_blocks)) {
    381 			idx += nr_blocks;
    382 			if (idx < SMALLOC_BPI)
    383 				last_idx = idx;
    384 			else {
    385 				last_idx = 0;
    386 				while (idx >= SMALLOC_BPI) {
    387 					i++;
    388 					idx -= SMALLOC_BPI;
    389 				}
    390 			}
    391 			continue;
    392 		}
    393 		set_blocks(pool, i, idx, nr_blocks);
    394 		offset = i * SMALLOC_BPL + idx * SMALLOC_BPB;
    395 		break;
    396 	}
    397 
    398 	if (i < pool->nr_blocks) {
    399 		pool->free_blocks -= nr_blocks;
    400 		ret = pool->map + offset;
    401 	}
    402 fail:
    403 	pool_unlock(pool);
    404 	return ret;
    405 }
    406 
    407 static void *smalloc_pool(struct pool *pool, size_t size)
    408 {
    409 	size_t alloc_size = size + sizeof(struct block_hdr);
    410 	void *ptr;
    411 
    412 	/*
    413 	 * Round to int alignment, so that the postred pointer will
    414 	 * be naturally aligned as well.
    415 	 */
    416 #ifdef SMALLOC_REDZONE
    417 	alloc_size += sizeof(unsigned int);
    418 	alloc_size = (alloc_size + int_mask) & ~int_mask;
    419 #endif
    420 
    421 	ptr = __smalloc_pool(pool, alloc_size);
    422 	if (ptr) {
    423 		struct block_hdr *hdr = ptr;
    424 
    425 		hdr->size = alloc_size;
    426 		fill_redzone(hdr);
    427 
    428 		ptr += sizeof(*hdr);
    429 		memset(ptr, 0, size);
    430 	}
    431 
    432 	return ptr;
    433 }
    434 
    435 void *smalloc(size_t size)
    436 {
    437 	unsigned int i;
    438 
    439 	if (size != (unsigned int) size)
    440 		return NULL;
    441 
    442 	global_write_lock();
    443 	i = last_pool;
    444 
    445 	do {
    446 		for (; i < nr_pools; i++) {
    447 			void *ptr = smalloc_pool(&mp[i], size);
    448 
    449 			if (ptr) {
    450 				last_pool = i;
    451 				global_write_unlock();
    452 				return ptr;
    453 			}
    454 		}
    455 		if (last_pool) {
    456 			last_pool = 0;
    457 			continue;
    458 		}
    459 
    460 		if (nr_pools + 1 > MAX_POOLS)
    461 			break;
    462 		else {
    463 			i = nr_pools;
    464 			if (add_pool(&mp[nr_pools], size))
    465 				goto out;
    466 		}
    467 	} while (1);
    468 
    469 out:
    470 	global_write_unlock();
    471 	return NULL;
    472 }
    473 
    474 char *smalloc_strdup(const char *str)
    475 {
    476 	char *ptr;
    477 
    478 	ptr = smalloc(strlen(str) + 1);
    479 	strcpy(ptr, str);
    480 	return ptr;
    481 }
    482