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