smalloc.c revision d24c33a479fcd68debad128da057814495f65e20
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 <sys/types.h> 12#include <limits.h> 13 14#include "mutex.h" 15 16#undef ENABLE_RESIZE /* define to enable pool resizing */ 17#define MP_SAFE /* define to made allocator thread safe */ 18 19#define INITIAL_SIZE 65536 /* new pool size */ 20#define MAX_POOLS 32 /* maximum number of pools to setup */ 21 22#ifdef ENABLE_RESIZE 23#define MAX_SIZE 8 * INITIAL_SIZE 24static unsigned int resize_error; 25#endif 26 27struct pool { 28 struct fio_mutex *lock; /* protects this pool */ 29 void *map; /* map of blocks */ 30 void *last; /* next free block hint */ 31 unsigned int size; /* size of pool */ 32 unsigned int room; /* size left in pool */ 33 unsigned int largest_block; /* largest block free */ 34 unsigned int free_since_compact; /* sfree() since compact() */ 35 int fd; /* memory backing fd */ 36 char file[PATH_MAX]; /* filename for fd */ 37}; 38 39static struct pool mp[MAX_POOLS]; 40static unsigned int nr_pools; 41static unsigned int last_pool; 42static struct fio_mutex *lock; 43 44struct mem_hdr { 45 unsigned int size; 46}; 47 48static inline void pool_lock(struct pool *pool) 49{ 50 if (pool->lock) 51 fio_mutex_down(pool->lock); 52} 53 54static inline void pool_unlock(struct pool *pool) 55{ 56 if (pool->lock) 57 fio_mutex_up(pool->lock); 58} 59 60static inline void global_lock(void) 61{ 62 if (lock) 63 fio_mutex_down(lock); 64} 65 66static inline void global_unlock(void) 67{ 68 if (lock) 69 fio_mutex_up(lock); 70} 71 72#define hdr_free(hdr) ((hdr)->size & 0x80000000) 73#define hdr_size(hdr) ((hdr)->size & ~0x80000000) 74#define hdr_mark_free(hdr) ((hdr)->size |= 0x80000000) 75 76static inline int ptr_valid(struct pool *pool, void *ptr) 77{ 78 return (ptr >= pool->map) && (ptr < pool->map + pool->size); 79} 80 81static inline int __hdr_valid(struct pool *pool, struct mem_hdr *hdr, 82 unsigned int size) 83{ 84 return ptr_valid(pool, hdr) && ptr_valid(pool, (void *) hdr + size - 1); 85} 86 87static inline int hdr_valid(struct pool *pool, struct mem_hdr *hdr) 88{ 89 return __hdr_valid(pool, hdr, hdr_size(hdr)); 90} 91 92static inline int region_free(struct mem_hdr *hdr) 93{ 94 return hdr_free(hdr) || (!hdr_free(hdr) && !hdr_size(hdr)); 95} 96 97static inline struct mem_hdr *__hdr_nxt(struct pool *pool, struct mem_hdr *hdr, 98 unsigned int size) 99{ 100 struct mem_hdr *nxt = (void *) hdr + size + sizeof(*hdr); 101 102 if (__hdr_valid(pool, nxt, size)) 103 return nxt; 104 105 return NULL; 106} 107 108static inline struct mem_hdr *hdr_nxt(struct pool *pool, struct mem_hdr *hdr) 109{ 110 return __hdr_nxt(pool, hdr, hdr_size(hdr)); 111} 112 113static void merge(struct pool *pool, struct mem_hdr *hdr, struct mem_hdr *nxt) 114{ 115 unsigned int hfree = hdr_free(hdr); 116 unsigned int nfree = hdr_free(nxt); 117 118 hdr->size = hdr_size(hdr) + hdr_size(nxt) + sizeof(*nxt); 119 nxt->size = 0; 120 121 if (hfree) 122 hdr_mark_free(hdr); 123 if (nfree) 124 hdr_mark_free(nxt); 125 126 if (pool->last == nxt) 127 pool->last = hdr; 128} 129 130static int combine(struct pool *pool, struct mem_hdr *prv, struct mem_hdr *hdr) 131{ 132 if (prv && hdr_free(prv) && hdr_free(hdr)) { 133 merge(pool, prv, hdr); 134 return 1; 135 } 136 137 return 0; 138} 139 140static int compact_pool(struct pool *pool) 141{ 142 struct mem_hdr *hdr = pool->map, *nxt; 143 unsigned int compacted = 0; 144 145 if (pool->free_since_compact < 50) 146 return 1; 147 148 while (hdr) { 149 nxt = hdr_nxt(pool, hdr); 150 if (!nxt) 151 break; 152 if (hdr_free(nxt) && hdr_free(hdr)) { 153 merge(pool, hdr, nxt); 154 compacted++; 155 continue; 156 } 157 hdr = hdr_nxt(pool, hdr); 158 } 159 160 pool->free_since_compact = 0; 161 return !!compacted; 162} 163 164static int resize_pool(struct pool *pool) 165{ 166#ifdef ENABLE_RESIZE 167 unsigned int new_size = pool->size << 1; 168 struct mem_hdr *hdr, *last_hdr; 169 void *ptr; 170 171 if (new_size >= MAX_SIZE || resize_error) 172 return 1; 173 174 if (ftruncate(pool->fd, new_size) < 0) 175 goto fail; 176 177 ptr = mremap(pool->map, pool->size, new_size, 0); 178 if (ptr == MAP_FAILED) 179 goto fail; 180 181 pool->map = ptr; 182 hdr = pool; 183 do { 184 last_hdr = hdr; 185 } while ((hdr = hdr_nxt(hdr)) != NULL); 186 187 if (hdr_free(last_hdr)) { 188 last_hdr->size = hdr_size(last_hdr) + new_size - pool_size; 189 hdr_mark_free(last_hdr); 190 } else { 191 struct mem_hdr *nxt; 192 193 nxt = (void *) last_hdr + hdr_size(last_hdr) + sizeof(*hdr); 194 nxt->size = new_size - pool_size - sizeof(*hdr); 195 hdr_mark_free(nxt); 196 } 197 198 pool_room += new_size - pool_size; 199 pool_size = new_size; 200 return 0; 201fail: 202 perror("resize"); 203 resize_error = 1; 204#else 205 return 1; 206#endif 207} 208 209static int add_pool(struct pool *pool) 210{ 211 struct mem_hdr *hdr; 212 void *ptr; 213 int fd; 214 215 strcpy(pool->file, "/tmp/.fio_smalloc.XXXXXX"); 216 fd = mkstemp(pool->file); 217 if (fd < 0) 218 goto out_close; 219 220 pool->size = INITIAL_SIZE; 221 if (ftruncate(fd, pool->size) < 0) 222 goto out_unlink; 223 224 ptr = mmap(NULL, pool->size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 225 if (ptr == MAP_FAILED) 226 goto out_unlink; 227 228 memset(ptr, 0, pool->size); 229 pool->map = pool->last = ptr; 230 231#ifdef MP_SAFE 232 pool->lock = fio_mutex_init(1); 233 if (!pool->lock) 234 goto out_unlink; 235#endif 236 237 pool->fd = fd; 238 239 hdr = pool->map; 240 pool->room = hdr->size = pool->size - sizeof(*hdr); 241 pool->largest_block = pool->room; 242 hdr_mark_free(hdr); 243 nr_pools++; 244 return 0; 245out_unlink: 246 if (pool->map) 247 munmap(pool->map, pool->size); 248 unlink(pool->file); 249out_close: 250 if (fd >= 0) 251 close(fd); 252 return 1; 253} 254 255void sinit(void) 256{ 257 int ret = add_pool(&mp[0]); 258 259#ifdef MP_SAFE 260 lock = fio_mutex_init(1); 261#endif 262 assert(!ret); 263} 264 265static void cleanup_pool(struct pool *pool) 266{ 267 unlink(pool->file); 268 close(pool->fd); 269 munmap(pool->map, pool->size); 270 271 if (pool->lock) 272 fio_mutex_remove(pool->lock); 273} 274 275void scleanup(void) 276{ 277 unsigned int i; 278 279 for (i = 0; i < nr_pools; i++) 280 cleanup_pool(&mp[i]); 281 282 if (lock) 283 fio_mutex_remove(lock); 284} 285 286static void sfree_pool(struct pool *pool, void *ptr) 287{ 288 struct mem_hdr *hdr, *nxt; 289 290 if (!ptr) 291 return; 292 293 assert(ptr_valid(pool, ptr)); 294 295 pool_lock(pool); 296 hdr = ptr - sizeof(*hdr); 297 assert(!hdr_free(hdr)); 298 hdr_mark_free(hdr); 299 pool->room -= hdr_size(hdr); 300 301 nxt = hdr_nxt(pool, hdr); 302 if (nxt && hdr_free(nxt)) 303 merge(pool, hdr, nxt); 304 305 if (hdr_size(hdr) > pool->largest_block) 306 pool->largest_block = hdr_size(hdr); 307 308 pool->free_since_compact++; 309 pool_unlock(pool); 310} 311 312void sfree(void *ptr) 313{ 314 struct pool *pool = NULL; 315 unsigned int i; 316 317 global_lock(); 318 319 for (i = 0; i < nr_pools; i++) { 320 if (ptr_valid(&mp[i], ptr)) { 321 pool = &mp[i]; 322 break; 323 } 324 } 325 326 global_unlock(); 327 328 assert(pool); 329 sfree_pool(pool, ptr); 330} 331 332static void *smalloc_pool(struct pool *pool, unsigned int size) 333{ 334 struct mem_hdr *hdr, *prv; 335 int did_restart = 0; 336 void *ret; 337 338 /* 339 * slight chance of race with sfree() here, but acceptable 340 */ 341 if (!size || size > pool->room + sizeof(*hdr) || 342 ((size > pool->largest_block) && pool->largest_block)) 343 return NULL; 344 345 pool_lock(pool); 346restart: 347 hdr = pool->last; 348 prv = NULL; 349 do { 350 if (combine(pool, prv, hdr)) 351 hdr = prv; 352 353 if (hdr_free(hdr) && hdr_size(hdr) >= size) 354 break; 355 356 prv = hdr; 357 } while ((hdr = hdr_nxt(pool, hdr)) != NULL); 358 359 if (!hdr) 360 goto fail; 361 362 /* 363 * more room, adjust next header if any 364 */ 365 if (hdr_size(hdr) - size >= 2 * sizeof(*hdr)) { 366 struct mem_hdr *nxt = __hdr_nxt(pool, hdr, size); 367 368 if (nxt) { 369 nxt->size = hdr_size(hdr) - size - sizeof(*hdr); 370 if (hdr_size(hdr) == pool->largest_block) 371 pool->largest_block = hdr_size(nxt); 372 hdr_mark_free(nxt); 373 } else 374 size = hdr_size(hdr); 375 } else 376 size = hdr_size(hdr); 377 378 if (size == hdr_size(hdr) && size == pool->largest_block) 379 pool->largest_block = 0; 380 381 /* 382 * also clears free bit 383 */ 384 hdr->size = size; 385 pool->last = hdr_nxt(pool, hdr); 386 if (!pool->last) 387 pool->last = pool->map; 388 pool->room -= size; 389 pool_unlock(pool); 390 391 ret = (void *) hdr + sizeof(*hdr); 392 memset(ret, 0, size); 393 return ret; 394fail: 395 /* 396 * if we fail to allocate, first compact the entries that we missed. 397 * if that also fails, increase the size of the pool 398 */ 399 ++did_restart; 400 if (did_restart <= 1) { 401 if (!compact_pool(pool)) { 402 pool->last = pool->map; 403 goto restart; 404 } 405 } 406 ++did_restart; 407 if (did_restart <= 2) { 408 if (!resize_pool(pool)) { 409 pool->last = pool->map; 410 goto restart; 411 } 412 } 413 pool_unlock(pool); 414 return NULL; 415} 416 417void *smalloc(unsigned int size) 418{ 419 unsigned int i; 420 421 global_lock(); 422 i = last_pool; 423 424 do { 425 for (; i < nr_pools; i++) { 426 void *ptr = smalloc_pool(&mp[i], size); 427 428 if (ptr) { 429 last_pool = i; 430 global_unlock(); 431 return ptr; 432 } 433 } 434 if (last_pool) { 435 last_pool = 0; 436 continue; 437 } 438 439 if (nr_pools + 1 >= MAX_POOLS) 440 break; 441 else { 442 i = nr_pools; 443 if (add_pool(&mp[nr_pools])) 444 break; 445 } 446 } while (1); 447 448 global_unlock(); 449 return NULL; 450} 451 452char *smalloc_strdup(const char *str) 453{ 454 char *ptr; 455 456 ptr = smalloc(strlen(str) + 1); 457 strcpy(ptr, str); 458 return ptr; 459} 460