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