diff options
author | jasone <jasone@FreeBSD.org> | 2006-04-27 01:03:00 +0000 |
---|---|---|
committer | jasone <jasone@FreeBSD.org> | 2006-04-27 01:03:00 +0000 |
commit | 3ddbb17f57326091621bfaf9b3e465ed0bea8ccf (patch) | |
tree | 39344335150eec89b27d223d41164c600c16dfe1 | |
parent | c5efd132705ed34e89c736de34ba8308355286d1 (diff) | |
download | FreeBSD-src-3ddbb17f57326091621bfaf9b3e465ed0bea8ccf.zip FreeBSD-src-3ddbb17f57326091621bfaf9b3e465ed0bea8ccf.tar.gz |
Change the semantics of brk_max to dynamically deal with data segment
bounds. [1]
Modify logic for utilizing the data segment, such that it is possible to
create huge allocations there.
Shrink the data segment when deallocating a chunk, if it is at the end of
the data segment.
Rename chunk_size to csize in huge_malloc(), in order to avoid masking a
static variable of the same name. [1]
Reported by: Paul Allen <nospam@ugcs.caltech.edu>
-rw-r--r-- | lib/libc/stdlib/malloc.c | 154 |
1 files changed, 83 insertions, 71 deletions
diff --git a/lib/libc/stdlib/malloc.c b/lib/libc/stdlib/malloc.c index 11e732f..398422c 100644 --- a/lib/libc/stdlib/malloc.c +++ b/lib/libc/stdlib/malloc.c @@ -707,7 +707,7 @@ static chunk_tree_t huge; static void *brk_base; /* Current end of brk, or ((void *)-1) if brk is exhausted. */ static void *brk_prev; -/* Upper limit on brk addresses (may be an over-estimate). */ +/* Current upper limit on brk addresses. */ static void *brk_max; #endif @@ -929,6 +929,7 @@ malloc_mutex_unlock(malloc_mutex_t *a_mutex) static inline size_t pow2_ceil(size_t x) { + x--; x |= x >> 1; x |= x >> 2; @@ -1136,9 +1137,6 @@ pages_map(void *addr, size_t size) { void *ret; -#ifdef USE_BRK -AGAIN: -#endif /* * We don't use MAP_FIXED here, because it can cause the *replacement* * of existing mappings, and we only want to create new mappings. @@ -1164,18 +1162,6 @@ AGAIN: } ret = NULL; } -#ifdef USE_BRK - else if ((uintptr_t)ret >= (uintptr_t)brk_base - && (uintptr_t)ret < (uintptr_t)brk_max) { - /* - * We succeeded in mapping memory, but at a location that could - * be confused with brk. Leave the mapping intact so that this - * won't ever happen again, then try again. - */ - assert(addr == NULL); - goto AGAIN; - } -#endif assert(ret == NULL || (addr == NULL && ret != addr) || (addr != NULL && ret == addr)); @@ -1239,47 +1225,47 @@ chunk_alloc(size_t size) goto RETURN; } } + } #ifdef USE_BRK + /* + * Try to create allocations in brk, in order to make full use of + * limited address space. + */ + if (brk_prev != (void *)-1) { + void *brk_cur; + intptr_t incr; + /* - * Try to create chunk-size allocations in brk, in order to - * make full use of limited address space. + * The loop is necessary to recover from races with other + * threads that are using brk for something other than malloc. */ - if (brk_prev != (void *)-1) { - void *brk_cur; - intptr_t incr; + do { + /* Get the current end of brk. */ + brk_cur = sbrk(0); /* - * The loop is necessary to recover from races with - * other threads that are using brk for something other - * than malloc. + * Calculate how much padding is necessary to + * chunk-align the end of brk. */ - do { - /* Get the current end of brk. */ - brk_cur = sbrk(0); - - /* - * Calculate how much padding is necessary to - * chunk-align the end of brk. - */ - incr = (intptr_t)chunk_size - - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur); - if (incr == chunk_size) { - ret = brk_cur; - } else { - ret = (void *)(intptr_t)brk_cur + incr; - incr += chunk_size; - } + incr = (intptr_t)size + - (intptr_t)CHUNK_ADDR2OFFSET(brk_cur); + if (incr == size) { + ret = brk_cur; + } else { + ret = (void *)(intptr_t)brk_cur + incr; + incr += size; + } - brk_prev = sbrk(incr); - if (brk_prev == brk_cur) { - /* Success. */ - goto RETURN; - } - } while (brk_prev != (void *)-1); - } -#endif + brk_prev = sbrk(incr); + if (brk_prev == brk_cur) { + /* Success. */ + brk_max = (void *)(intptr_t)ret + size; + goto RETURN; + } + } while (brk_prev != (void *)-1); } +#endif /* * Try to over-allocate, but allow the OS to place the allocation @@ -1340,29 +1326,56 @@ chunk_dealloc(void *chunk, size_t size) assert(size % chunk_size == 0); malloc_mutex_lock(&chunks_mtx); + +#ifdef USE_BRK + if ((uintptr_t)chunk >= (uintptr_t)brk_base + && (uintptr_t)chunk < (uintptr_t)brk_max) { + void *brk_cur; + + /* Get the current end of brk. */ + brk_cur = sbrk(0); + + /* + * Try to shrink the data segment if this chunk is at the end + * of the data segment. The sbrk() call here is subject to a + * race condition with threads that use brk(2) or sbrk(2) + * directly, but the alternative would be to leak memory for + * the sake of poorly designed multi-threaded programs. + */ + if (brk_cur == brk_max + && (void *)(uintptr_t)chunk + size == brk_max + && sbrk(-(intptr_t)size) == brk_max) { + if (brk_prev == brk_max) { + /* Success. */ + brk_prev = (void *)(intptr_t)brk_max + - (intptr_t)size; + brk_max = brk_prev; + } + goto RETURN; + } else + madvise(chunk, size, MADV_FREE); + } else +#endif + pages_unmap(chunk, size); + + /* + * Iteratively create records of each chunk-sized memory region that + * 'chunk' is comprised of, so that the address range can be recycled + * if memory usage increases later on. + */ for (offset = 0; offset < size; offset += chunk_size) { node = base_chunk_node_alloc(); if (node == NULL) break; - /* - * Create a record of this chunk before deallocating it, so - * that the address range can be recycled if memory usage - * increases later on. - */ node->chunk = (void *)((uintptr_t)chunk + (uintptr_t)offset); node->size = chunk_size; RB_INSERT(chunk_tree_s, &old_chunks, node); } #ifdef USE_BRK - if ((uintptr_t)chunk >= (uintptr_t)brk_base - && (uintptr_t)chunk < (uintptr_t)brk_max) - madvise(chunk, size, MADV_FREE); - else +RETURN: #endif - pages_unmap(chunk, size); - #ifdef MALLOC_STATS stats_chunks.curchunks -= (size / chunk_size); #endif @@ -1415,7 +1428,7 @@ choose_arena(void) * working. Even so, the hashing can be easily thwarted by * inconvenient _pthread_self() values. Without specific * knowledge of how _pthread_self() calculates values, we can't - * do much better than this. + * easily do much better than this. */ ind = (unsigned long) _pthread_self() % narenas; @@ -1825,7 +1838,6 @@ arena_bin_run_promote(arena_t *arena, arena_bin_t *bin, arena_run_t *run, } } - static void arena_bin_run_demote(arena_t *arena, arena_bin_t *bin, arena_run_t *run, size_t size) @@ -2514,13 +2526,13 @@ static void * huge_malloc(size_t size) { void *ret; - size_t chunk_size; + size_t csize; chunk_node_t *node; - /* Allocate a chunk for this request. */ + /* Allocate one or more contiguous chunks for this request. */ - chunk_size = CHUNK_CEILING(size); - if (chunk_size == 0) { + csize = CHUNK_CEILING(size); + if (csize == 0) { /* size is large enough to cause size_t wrap-around. */ return (NULL); } @@ -2530,7 +2542,7 @@ huge_malloc(size_t size) if (node == NULL) return (NULL); - ret = chunk_alloc(chunk_size); + ret = chunk_alloc(csize); if (ret == NULL) { base_chunk_node_dealloc(node); return (NULL); @@ -2538,20 +2550,20 @@ huge_malloc(size_t size) /* Insert node into chunks. */ node->chunk = ret; - node->size = chunk_size; + node->size = csize; malloc_mutex_lock(&chunks_mtx); RB_INSERT(chunk_tree_s, &huge, node); #ifdef MALLOC_STATS huge_nmalloc++; - huge_allocated += chunk_size; + huge_allocated += csize; #endif malloc_mutex_unlock(&chunks_mtx); if (opt_junk && ret != NULL) - memset(ret, 0xa5, chunk_size); + memset(ret, 0xa5, csize); else if (opt_zero && ret != NULL) - memset(ret, 0, chunk_size); + memset(ret, 0, csize); return (ret); } @@ -3211,7 +3223,7 @@ malloc_init_hard(void) #ifdef USE_BRK brk_base = sbrk(0); brk_prev = brk_base; - brk_max = (void *)((uintptr_t)brk_base + MAXDSIZ); + brk_max = brk_base; #endif #ifdef MALLOC_STATS huge_nmalloc = 0; |