Fix a prof-related locking order bug.

Fix a locking order bug that could cause deadlock during fork if heap
profiling were enabled.
This commit is contained in:
Jason Evans 2013-02-06 11:59:30 -08:00
parent 06912756cc
commit 88c222c8e9
5 changed files with 44 additions and 25 deletions

View File

@ -9,6 +9,8 @@ found in the git revision history:
* 3.x.x (XXX Not yet released) * 3.x.x (XXX Not yet released)
Bug fixes: Bug fixes:
- Fix a locking order bug that could cause deadlock during fork if heap
profiling were enabled.
- Fix a chunk recycling bug that could cause the allocator to lose track of - Fix a chunk recycling bug that could cause the allocator to lose track of
whether a chunk was zeroed. On FreeBSD, NetBSD, and OS X, it could cause whether a chunk was zeroed. On FreeBSD, NetBSD, and OS X, it could cause
corruption if allocating via sbrk(2) (unlikely unless running with the corruption if allocating via sbrk(2) (unlikely unless running with the

View File

@ -463,9 +463,9 @@ void arena_mapbits_small_set(arena_chunk_t *chunk, size_t pageind,
size_t runind, size_t binind, size_t flags); size_t runind, size_t binind, size_t flags);
void arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind, void arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
size_t unzeroed); size_t unzeroed);
void arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes); bool arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes);
void arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes); bool arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes);
void arena_prof_accum(arena_t *arena, uint64_t accumbytes); bool arena_prof_accum(arena_t *arena, uint64_t accumbytes);
size_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits); size_t arena_ptr_small_binind_get(const void *ptr, size_t mapbits);
size_t arena_bin_index(arena_t *arena, arena_bin_t *bin); size_t arena_bin_index(arena_t *arena, arena_bin_t *bin);
unsigned arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info, unsigned arena_run_regind(arena_run_t *run, arena_bin_info_t *bin_info,
@ -663,7 +663,7 @@ arena_mapbits_unzeroed_set(arena_chunk_t *chunk, size_t pageind,
*mapbitsp = (*mapbitsp & ~CHUNK_MAP_UNZEROED) | unzeroed; *mapbitsp = (*mapbitsp & ~CHUNK_MAP_UNZEROED) | unzeroed;
} }
JEMALLOC_INLINE void JEMALLOC_INLINE bool
arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes) arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes)
{ {
@ -672,33 +672,40 @@ arena_prof_accum_impl(arena_t *arena, uint64_t accumbytes)
arena->prof_accumbytes += accumbytes; arena->prof_accumbytes += accumbytes;
if (arena->prof_accumbytes >= prof_interval) { if (arena->prof_accumbytes >= prof_interval) {
prof_idump();
arena->prof_accumbytes -= prof_interval; arena->prof_accumbytes -= prof_interval;
return (true);
} }
return (false);
} }
JEMALLOC_INLINE void JEMALLOC_INLINE bool
arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes) arena_prof_accum_locked(arena_t *arena, uint64_t accumbytes)
{ {
cassert(config_prof); cassert(config_prof);
if (prof_interval == 0) if (prof_interval == 0)
return; return (false);
arena_prof_accum_impl(arena, accumbytes); return (arena_prof_accum_impl(arena, accumbytes));
} }
JEMALLOC_INLINE void JEMALLOC_INLINE bool
arena_prof_accum(arena_t *arena, uint64_t accumbytes) arena_prof_accum(arena_t *arena, uint64_t accumbytes)
{ {
cassert(config_prof); cassert(config_prof);
if (prof_interval == 0) if (prof_interval == 0)
return; return (false);
malloc_mutex_lock(&arena->lock);
arena_prof_accum_impl(arena, accumbytes); {
malloc_mutex_unlock(&arena->lock); bool ret;
malloc_mutex_lock(&arena->lock);
ret = arena_prof_accum_impl(arena, accumbytes);
malloc_mutex_unlock(&arena->lock);
return (ret);
}
} }
JEMALLOC_ALWAYS_INLINE size_t JEMALLOC_ALWAYS_INLINE size_t

View File

@ -1338,8 +1338,8 @@ arena_tcache_fill_small(arena_t *arena, tcache_bin_t *tbin, size_t binind,
assert(tbin->ncached == 0); assert(tbin->ncached == 0);
if (config_prof) if (config_prof && arena_prof_accum(arena, prof_accumbytes))
arena_prof_accum(arena, prof_accumbytes); prof_idump();
bin = &arena->bins[binind]; bin = &arena->bins[binind];
malloc_mutex_lock(&bin->lock); malloc_mutex_lock(&bin->lock);
for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >> for (i = 0, nfill = (tcache_bin_info[binind].ncached_max >>
@ -1447,8 +1447,8 @@ arena_malloc_small(arena_t *arena, size_t size, bool zero)
bin->stats.nrequests++; bin->stats.nrequests++;
} }
malloc_mutex_unlock(&bin->lock); malloc_mutex_unlock(&bin->lock);
if (config_prof && isthreaded == false) if (config_prof && isthreaded == false && arena_prof_accum(arena, size))
arena_prof_accum(arena, size); prof_idump();
if (zero == false) { if (zero == false) {
if (config_fill) { if (config_fill) {
@ -1475,6 +1475,7 @@ void *
arena_malloc_large(arena_t *arena, size_t size, bool zero) arena_malloc_large(arena_t *arena, size_t size, bool zero)
{ {
void *ret; void *ret;
UNUSED bool idump;
/* Large allocation. */ /* Large allocation. */
size = PAGE_CEILING(size); size = PAGE_CEILING(size);
@ -1493,8 +1494,10 @@ arena_malloc_large(arena_t *arena, size_t size, bool zero)
arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++; arena->stats.lstats[(size >> LG_PAGE) - 1].curruns++;
} }
if (config_prof) if (config_prof)
arena_prof_accum_locked(arena, size); idump = arena_prof_accum_locked(arena, size);
malloc_mutex_unlock(&arena->lock); malloc_mutex_unlock(&arena->lock);
if (config_prof && idump)
prof_idump();
if (zero == false) { if (zero == false) {
if (config_fill) { if (config_fill) {

View File

@ -1753,12 +1753,12 @@ _malloc_prefork(void)
/* Acquire all mutexes in a safe order. */ /* Acquire all mutexes in a safe order. */
ctl_prefork(); ctl_prefork();
prof_prefork();
malloc_mutex_prefork(&arenas_lock); malloc_mutex_prefork(&arenas_lock);
for (i = 0; i < narenas_total; i++) { for (i = 0; i < narenas_total; i++) {
if (arenas[i] != NULL) if (arenas[i] != NULL)
arena_prefork(arenas[i]); arena_prefork(arenas[i]);
} }
prof_prefork();
chunk_prefork(); chunk_prefork();
base_prefork(); base_prefork();
huge_prefork(); huge_prefork();
@ -1784,12 +1784,12 @@ _malloc_postfork(void)
huge_postfork_parent(); huge_postfork_parent();
base_postfork_parent(); base_postfork_parent();
chunk_postfork_parent(); chunk_postfork_parent();
prof_postfork_parent();
for (i = 0; i < narenas_total; i++) { for (i = 0; i < narenas_total; i++) {
if (arenas[i] != NULL) if (arenas[i] != NULL)
arena_postfork_parent(arenas[i]); arena_postfork_parent(arenas[i]);
} }
malloc_mutex_postfork_parent(&arenas_lock); malloc_mutex_postfork_parent(&arenas_lock);
prof_postfork_parent();
ctl_postfork_parent(); ctl_postfork_parent();
} }
@ -1804,12 +1804,12 @@ jemalloc_postfork_child(void)
huge_postfork_child(); huge_postfork_child();
base_postfork_child(); base_postfork_child();
chunk_postfork_child(); chunk_postfork_child();
prof_postfork_child();
for (i = 0; i < narenas_total; i++) { for (i = 0; i < narenas_total; i++) {
if (arenas[i] != NULL) if (arenas[i] != NULL)
arena_postfork_child(arenas[i]); arena_postfork_child(arenas[i]);
} }
malloc_mutex_postfork_child(&arenas_lock); malloc_mutex_postfork_child(&arenas_lock);
prof_postfork_child();
ctl_postfork_child(); ctl_postfork_child();
} }

View File

@ -97,7 +97,8 @@ tcache_bin_flush_small(tcache_bin_t *tbin, size_t binind, unsigned rem,
arena_bin_t *bin = &arena->bins[binind]; arena_bin_t *bin = &arena->bins[binind];
if (config_prof && arena == tcache->arena) { if (config_prof && arena == tcache->arena) {
arena_prof_accum(arena, tcache->prof_accumbytes); if (arena_prof_accum(arena, tcache->prof_accumbytes))
prof_idump();
tcache->prof_accumbytes = 0; tcache->prof_accumbytes = 0;
} }
@ -174,11 +175,14 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE( arena_chunk_t *chunk = (arena_chunk_t *)CHUNK_ADDR2BASE(
tbin->avail[0]); tbin->avail[0]);
arena_t *arena = chunk->arena; arena_t *arena = chunk->arena;
UNUSED bool idump;
if (config_prof)
idump = false;
malloc_mutex_lock(&arena->lock); malloc_mutex_lock(&arena->lock);
if ((config_prof || config_stats) && arena == tcache->arena) { if ((config_prof || config_stats) && arena == tcache->arena) {
if (config_prof) { if (config_prof) {
arena_prof_accum_locked(arena, idump = arena_prof_accum_locked(arena,
tcache->prof_accumbytes); tcache->prof_accumbytes);
tcache->prof_accumbytes = 0; tcache->prof_accumbytes = 0;
} }
@ -210,6 +214,8 @@ tcache_bin_flush_large(tcache_bin_t *tbin, size_t binind, unsigned rem,
} }
} }
malloc_mutex_unlock(&arena->lock); malloc_mutex_unlock(&arena->lock);
if (config_prof && idump)
prof_idump();
} }
if (config_stats && merged_stats == false) { if (config_stats && merged_stats == false) {
/* /*
@ -341,8 +347,9 @@ tcache_destroy(tcache_t *tcache)
} }
} }
if (config_prof && tcache->prof_accumbytes > 0) if (config_prof && tcache->prof_accumbytes > 0 &&
arena_prof_accum(tcache->arena, tcache->prof_accumbytes); arena_prof_accum(tcache->arena, tcache->prof_accumbytes))
prof_idump();
tcache_size = arena_salloc(tcache, false); tcache_size = arena_salloc(tcache, false);
if (tcache_size <= SMALL_MAXCLASS) { if (tcache_size <= SMALL_MAXCLASS) {