2019-12-04 10:31:47 +08:00
|
|
|
#include "jemalloc/internal/jemalloc_preamble.h"
|
|
|
|
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
|
|
|
|
|
|
|
#include "jemalloc/internal/assert.h"
|
2020-01-28 05:55:46 +08:00
|
|
|
#include "jemalloc/internal/emap.h"
|
2019-12-04 10:31:47 +08:00
|
|
|
#include "jemalloc/internal/extent_dss.h"
|
|
|
|
#include "jemalloc/internal/extent_mmap.h"
|
|
|
|
#include "jemalloc/internal/ph.h"
|
|
|
|
#include "jemalloc/internal/mutex.h"
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/* Data. */
|
|
|
|
|
|
|
|
size_t opt_lg_extent_max_active_fit = LG_EXTENT_MAX_ACTIVE_FIT_DEFAULT;
|
|
|
|
|
2019-12-14 05:46:25 +08:00
|
|
|
static bool extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
|
|
|
size_t offset, size_t length, bool growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
static bool extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena,
|
2019-12-10 06:36:45 +08:00
|
|
|
ehooks_t *ehooks, edata_t *edata, size_t offset, size_t length,
|
2019-12-04 10:31:47 +08:00
|
|
|
bool growing_retained);
|
|
|
|
static bool extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena,
|
2019-12-10 06:36:45 +08:00
|
|
|
ehooks_t *ehooks, edata_t *edata, size_t offset, size_t length,
|
2019-12-04 10:31:47 +08:00
|
|
|
bool growing_retained);
|
2019-12-14 05:52:34 +08:00
|
|
|
static edata_t *extent_split_impl(tsdn_t *tsdn, edata_cache_t *edata_cache,
|
2019-12-10 06:36:45 +08:00
|
|
|
ehooks_t *ehooks, edata_t *edata, size_t size_a, szind_t szind_a,
|
2019-12-04 10:31:47 +08:00
|
|
|
bool slab_a, size_t size_b, szind_t szind_b, bool slab_b,
|
|
|
|
bool growing_retained);
|
2019-12-14 03:16:58 +08:00
|
|
|
static bool extent_merge_impl(tsdn_t *tsdn, ehooks_t *ehooks,
|
|
|
|
edata_cache_t *edata_cache, edata_t *a, edata_t *b, bool growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
/* Used exclusively for gdump triggering. */
|
|
|
|
static atomic_zu_t curpages;
|
|
|
|
static atomic_zu_t highpages;
|
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
/*
|
|
|
|
* Function prototypes for static functions that are referenced prior to
|
|
|
|
* definition.
|
|
|
|
*/
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
static void extent_deregister(tsdn_t *tsdn, edata_t *edata);
|
|
|
|
static edata_t *extent_recycle(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
ecache_t *ecache, void *new_addr, size_t usize, size_t pad, size_t alignment,
|
2019-12-04 10:31:47 +08:00
|
|
|
bool slab, szind_t szind, bool *zero, bool *commit, bool growing_retained);
|
2019-12-14 05:46:25 +08:00
|
|
|
static edata_t *extent_try_coalesce(tsdn_t *tsdn, edata_cache_t *edata_cache,
|
2020-02-06 05:51:05 +08:00
|
|
|
ehooks_t *ehooks, ecache_t *ecache, edata_t *edata, bool *coalesced,
|
|
|
|
bool growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
static void extent_record(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
ecache_t *ecache, edata_t *edata, bool growing_retained);
|
2019-12-13 09:30:28 +08:00
|
|
|
static edata_t *extent_alloc_retained(tsdn_t *tsdn, arena_t *arena,
|
|
|
|
ehooks_t *ehooks, void *new_addr, size_t size, size_t pad, size_t alignment,
|
|
|
|
bool slab, szind_t szind, bool *zero, bool *commit);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
/******************************************************************************/
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_addr_randomize(tsdn_t *tsdn, arena_t *arena, edata_t *edata,
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t alignment) {
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_base_get(edata) == edata_addr_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (alignment < PAGE) {
|
|
|
|
unsigned lg_range = LG_PAGE -
|
|
|
|
lg_floor(CACHELINE_CEILING(alignment));
|
|
|
|
size_t r;
|
|
|
|
if (!tsdn_null(tsdn)) {
|
|
|
|
tsd_t *tsd = tsdn_tsd(tsdn);
|
|
|
|
r = (size_t)prng_lg_range_u64(
|
|
|
|
tsd_prng_statep_get(tsd), lg_range);
|
|
|
|
} else {
|
|
|
|
uint64_t stack_value = (uint64_t)(uintptr_t)&r;
|
|
|
|
r = (size_t)prng_lg_range_u64(&stack_value, lg_range);
|
|
|
|
}
|
|
|
|
uintptr_t random_offset = ((uintptr_t)r) << (LG_PAGE -
|
|
|
|
lg_range);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata->e_addr = (void *)((uintptr_t)edata->e_addr +
|
2019-12-04 10:31:47 +08:00
|
|
|
random_offset);
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(ALIGNMENT_ADDR2BASE(edata->e_addr, alignment) ==
|
|
|
|
edata->e_addr);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_try_delayed_coalesce(tsdn_t *tsdn, edata_cache_t *edata_cache,
|
2020-02-06 05:51:05 +08:00
|
|
|
ehooks_t *ehooks, ecache_t *ecache, edata_t *edata) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_state_set(edata, extent_state_active);
|
2019-12-04 10:31:47 +08:00
|
|
|
bool coalesced;
|
2020-02-06 05:51:05 +08:00
|
|
|
edata = extent_try_coalesce(tsdn, edata_cache, ehooks, ecache, edata,
|
|
|
|
&coalesced, false);
|
2019-12-13 08:44:49 +08:00
|
|
|
edata_state_set(edata, ecache->state);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (!coalesced) {
|
|
|
|
return true;
|
|
|
|
}
|
2019-12-13 08:25:24 +08:00
|
|
|
eset_insert(&ecache->eset, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *
|
2019-12-17 03:01:34 +08:00
|
|
|
ecache_alloc(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks, ecache_t *ecache,
|
2019-12-04 10:31:47 +08:00
|
|
|
void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
|
2020-01-17 05:28:27 +08:00
|
|
|
szind_t szind, bool *zero) {
|
2019-12-04 10:31:47 +08:00
|
|
|
assert(size + pad != 0);
|
|
|
|
assert(alignment != 0);
|
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2020-01-17 05:28:27 +08:00
|
|
|
bool commit = true;
|
2019-12-13 08:25:24 +08:00
|
|
|
edata_t *edata = extent_recycle(tsdn, arena, ehooks, ecache, new_addr,
|
2020-01-17 05:28:27 +08:00
|
|
|
size, pad, alignment, slab, szind, zero, &commit, false);
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata == NULL || edata_dumpable_get(edata));
|
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-13 09:30:28 +08:00
|
|
|
edata_t *
|
2019-12-17 03:01:34 +08:00
|
|
|
ecache_alloc_grow(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-13 09:30:28 +08:00
|
|
|
ecache_t *ecache, void *new_addr, size_t size, size_t pad, size_t alignment,
|
2020-01-17 05:28:27 +08:00
|
|
|
bool slab, szind_t szind, bool *zero) {
|
2019-12-13 09:30:28 +08:00
|
|
|
assert(size + pad != 0);
|
|
|
|
assert(alignment != 0);
|
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2020-01-17 05:28:27 +08:00
|
|
|
bool commit = true;
|
2019-12-13 09:30:28 +08:00
|
|
|
edata_t *edata = extent_alloc_retained(tsdn, arena, ehooks, new_addr,
|
2020-01-17 05:28:27 +08:00
|
|
|
size, pad, alignment, slab, szind, zero, &commit);
|
2019-12-13 09:30:28 +08:00
|
|
|
if (edata == NULL) {
|
|
|
|
if (opt_retain && new_addr != NULL) {
|
|
|
|
/*
|
|
|
|
* When retain is enabled and new_addr is set, we do not
|
|
|
|
* attempt extent_alloc_wrapper which does mmap that is
|
|
|
|
* very unlikely to succeed (unless it happens to be at
|
|
|
|
* the end).
|
|
|
|
*/
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
edata = extent_alloc_wrapper(tsdn, arena, ehooks,
|
2020-01-17 05:28:27 +08:00
|
|
|
new_addr, size, pad, alignment, slab, szind, zero, &commit);
|
2019-12-13 09:30:28 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert(edata == NULL || edata_dumpable_get(edata));
|
|
|
|
return edata;
|
|
|
|
}
|
|
|
|
|
2019-12-04 10:31:47 +08:00
|
|
|
void
|
2019-12-17 03:01:34 +08:00
|
|
|
ecache_dalloc(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks, ecache_t *ecache,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata) {
|
|
|
|
assert(edata_base_get(edata) != NULL);
|
|
|
|
assert(edata_size_get(edata) != 0);
|
|
|
|
assert(edata_dumpable_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_addr_set(edata, edata_base_get(edata));
|
|
|
|
edata_zeroed_set(edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
extent_record(tsdn, arena, ehooks, ecache, edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *
|
2019-12-17 03:01:34 +08:00
|
|
|
ecache_evict(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks, ecache_t *ecache,
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t npages_min) {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the LRU coalesced extent, if any. If coalescing was delayed,
|
|
|
|
* the loop will iterate until the LRU extent is fully coalesced.
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
while (true) {
|
|
|
|
/* Get the LRU extent, if any. */
|
2019-12-13 08:25:24 +08:00
|
|
|
edata = edata_list_first(&ecache->eset.lru);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_return;
|
|
|
|
}
|
|
|
|
/* Check the eviction limit. */
|
2019-12-13 08:25:24 +08:00
|
|
|
size_t extents_npages = ecache_npages_get(ecache);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (extents_npages <= npages_min) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata = NULL;
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_return;
|
|
|
|
}
|
2019-12-13 08:25:24 +08:00
|
|
|
eset_remove(&ecache->eset, edata);
|
2019-12-13 08:33:19 +08:00
|
|
|
if (!ecache->delay_coalesce) {
|
2019-12-04 10:31:47 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* Try to coalesce. */
|
2019-12-14 05:46:25 +08:00
|
|
|
if (extent_try_delayed_coalesce(tsdn, &arena->edata_cache,
|
2020-02-06 05:51:05 +08:00
|
|
|
ehooks, ecache, edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* The LRU extent was just coalesced and the result placed in
|
|
|
|
* the LRU at its neighbor's position. Start over.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Either mark the extent active or deregister it to protect against
|
|
|
|
* concurrent operations.
|
|
|
|
*/
|
2019-12-13 08:44:49 +08:00
|
|
|
switch (ecache->state) {
|
2019-12-04 10:31:47 +08:00
|
|
|
case extent_state_active:
|
|
|
|
not_reached();
|
|
|
|
case extent_state_dirty:
|
|
|
|
case extent_state_muzzy:
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_state_set(edata, extent_state_active);
|
2019-12-04 10:31:47 +08:00
|
|
|
break;
|
|
|
|
case extent_state_retained:
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
not_reached();
|
|
|
|
}
|
|
|
|
|
|
|
|
label_return:
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This can only happen when we fail to allocate a new extent struct (which
|
|
|
|
* indicates OOM), e.g. when trying to split an existing extent.
|
|
|
|
*/
|
|
|
|
static void
|
2019-12-13 08:25:24 +08:00
|
|
|
extents_abandon_vm(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
|
|
|
ecache_t *ecache, edata_t *edata, bool growing_retained) {
|
2019-12-10 06:36:45 +08:00
|
|
|
size_t sz = edata_size_get(edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (config_stats) {
|
|
|
|
arena_stats_accum_zu(&arena->stats.abandoned_vm, sz);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Leak extent after making sure its pages have already been purged, so
|
|
|
|
* that this is only a virtual memory leak.
|
|
|
|
*/
|
2019-12-13 08:44:49 +08:00
|
|
|
if (ecache->state == extent_state_dirty) {
|
2019-12-10 06:36:45 +08:00
|
|
|
if (extent_purge_lazy_impl(tsdn, arena, ehooks, edata, 0, sz,
|
2019-12-04 10:31:47 +08:00
|
|
|
growing_retained)) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_purge_forced_impl(tsdn, arena, ehooks, edata, 0,
|
|
|
|
edata_size_get(edata), growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
}
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate_locked(tsdn_t *tsdn, ecache_t *ecache, edata_t *edata) {
|
|
|
|
assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_state_get(edata) == extent_state_active);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:44:49 +08:00
|
|
|
edata_state_set(edata, ecache->state);
|
2019-12-13 08:25:24 +08:00
|
|
|
eset_insert(&ecache->eset, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate(tsdn_t *tsdn, ecache_t *ecache, edata_t *edata) {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &ecache->mtx);
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate_locked(tsdn, ecache, edata);
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_activate_locked(tsdn_t *tsdn, ecache_t *ecache, edata_t *edata) {
|
|
|
|
assert(edata_arena_ind_get(edata) == ecache_ind_get(ecache));
|
2019-12-13 08:44:49 +08:00
|
|
|
assert(edata_state_get(edata) == ecache->state);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
eset_remove(&ecache->eset, edata);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_state_set(edata, extent_state_active);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_add(tsdn_t *tsdn, const edata_t *edata) {
|
2019-12-04 10:31:47 +08:00
|
|
|
cassert(config_prof);
|
|
|
|
/* prof_gdump() requirement. */
|
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (opt_prof && edata_state_get(edata) == extent_state_active) {
|
|
|
|
size_t nadd = edata_size_get(edata) >> LG_PAGE;
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t cur = atomic_fetch_add_zu(&curpages, nadd,
|
|
|
|
ATOMIC_RELAXED) + nadd;
|
|
|
|
size_t high = atomic_load_zu(&highpages, ATOMIC_RELAXED);
|
|
|
|
while (cur > high && !atomic_compare_exchange_weak_zu(
|
|
|
|
&highpages, &high, cur, ATOMIC_RELAXED, ATOMIC_RELAXED)) {
|
|
|
|
/*
|
|
|
|
* Don't refresh cur, because it may have decreased
|
|
|
|
* since this thread lost the highpages update race.
|
|
|
|
* Note that high is updated in case of CAS failure.
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
if (cur > high && prof_gdump_get_unlocked()) {
|
|
|
|
prof_gdump(tsdn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_sub(tsdn_t *tsdn, const edata_t *edata) {
|
2019-12-04 10:31:47 +08:00
|
|
|
cassert(config_prof);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (opt_prof && edata_state_get(edata) == extent_state_active) {
|
|
|
|
size_t nsub = edata_size_get(edata) >> LG_PAGE;
|
2019-12-04 10:31:47 +08:00
|
|
|
assert(atomic_load_zu(&curpages, ATOMIC_RELAXED) >= nsub);
|
|
|
|
atomic_fetch_sub_zu(&curpages, nsub, ATOMIC_RELAXED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_register_impl(tsdn_t *tsdn, edata_t *edata, bool gdump_add) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/*
|
|
|
|
* We need to hold the lock to protect against a concurrent coalesce
|
|
|
|
* operation that sees us in a partial state.
|
|
|
|
*/
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_lock_edata(tsdn, &emap_global, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2020-01-31 04:40:07 +08:00
|
|
|
szind_t szind = edata_szind_get_maybe_invalid(edata);
|
|
|
|
bool slab = edata_slab_get(edata);
|
|
|
|
|
2020-02-06 05:51:05 +08:00
|
|
|
if (emap_register_boundary(tsdn, &emap_global, edata, szind, slab)) {
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (slab) {
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_register_interior(tsdn, &emap_global, edata, szind);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (config_prof && gdump_add) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_add(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_register(tsdn_t *tsdn, edata_t *edata) {
|
|
|
|
return extent_register_impl(tsdn, edata, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_register_no_gdump_add(tsdn_t *tsdn, edata_t *edata) {
|
|
|
|
return extent_register_impl(tsdn, edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_reregister(tsdn_t *tsdn, edata_t *edata) {
|
|
|
|
bool err = extent_register(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
assert(!err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Removes all pointers to the given extent from the global rtree.
|
|
|
|
*/
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister_impl(tsdn_t *tsdn, edata_t *edata, bool gdump) {
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_lock_edata(tsdn, &emap_global, edata);
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_deregister_boundary(tsdn, &emap_global, edata);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_slab_get(edata)) {
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_deregister_interior(tsdn, &emap_global, edata);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_slab_set(edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (config_prof && gdump) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_sub(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister(tsdn_t *tsdn, edata_t *edata) {
|
|
|
|
extent_deregister_impl(tsdn, edata, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister_no_gdump_sub(tsdn_t *tsdn, edata_t *edata) {
|
|
|
|
extent_deregister_impl(tsdn, edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2019-12-13 08:25:24 +08:00
|
|
|
* Tries to find and remove an extent from ecache that can be used for the
|
2019-12-04 10:31:47 +08:00
|
|
|
* given allocation request.
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_recycle_extract(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2020-02-06 05:51:05 +08:00
|
|
|
ecache_t *ecache, void *new_addr, size_t size, size_t pad, size_t alignment,
|
|
|
|
bool slab, bool growing_retained) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
|
|
|
assert(alignment > 0);
|
|
|
|
if (config_debug && new_addr != NULL) {
|
|
|
|
/*
|
|
|
|
* Non-NULL new_addr has two use cases:
|
|
|
|
*
|
|
|
|
* 1) Recycle a known-extant extent, e.g. during purging.
|
|
|
|
* 2) Perform in-place expanding reallocation.
|
|
|
|
*
|
|
|
|
* Regardless of use case, new_addr must either refer to a
|
|
|
|
* non-existing extent, or to the base of an extant extent,
|
|
|
|
* since only active slabs support interior lookups (which of
|
|
|
|
* course cannot be recycled).
|
|
|
|
*/
|
|
|
|
assert(PAGE_ADDR2BASE(new_addr) == new_addr);
|
|
|
|
assert(pad == 0);
|
|
|
|
assert(alignment <= PAGE);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t esize = size + pad;
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &ecache->mtx);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
if (new_addr != NULL) {
|
2020-02-06 05:51:05 +08:00
|
|
|
edata = emap_lock_edata_from_addr(tsdn, &emap_global, new_addr,
|
|
|
|
false);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata != NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/*
|
2019-12-10 06:36:45 +08:00
|
|
|
* We might null-out edata to report an error, but we
|
2019-12-04 10:31:47 +08:00
|
|
|
* still need to unlock the associated mutex after.
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *unlock_edata = edata;
|
|
|
|
assert(edata_base_get(edata) == new_addr);
|
|
|
|
if (edata_arena_ind_get(edata) != arena_ind_get(arena)
|
|
|
|
|| edata_size_get(edata) < esize
|
2019-12-13 08:25:24 +08:00
|
|
|
|| edata_state_get(edata)
|
2019-12-13 08:44:49 +08:00
|
|
|
!= ecache->state) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata = NULL;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, unlock_edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
} else {
|
2019-12-13 08:33:19 +08:00
|
|
|
edata = eset_fit(&ecache->eset, esize, alignment,
|
|
|
|
ecache->delay_coalesce);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_activate_locked(tsdn, ecache, edata);
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Given an allocation request and an extent guaranteed to be able to satisfy
|
2019-12-10 06:36:45 +08:00
|
|
|
* it, this splits off lead and trail extents, leaving edata pointing to an
|
2019-12-04 10:31:47 +08:00
|
|
|
* extent satisfying the allocation.
|
2019-12-13 08:25:24 +08:00
|
|
|
* This function doesn't put lead or trail into any ecache; it's the caller's
|
2019-12-04 10:31:47 +08:00
|
|
|
* job to ensure that they can be reused.
|
|
|
|
*/
|
|
|
|
typedef enum {
|
|
|
|
/*
|
2019-12-10 06:36:45 +08:00
|
|
|
* Split successfully. lead, edata, and trail, are modified to extents
|
2019-12-04 10:31:47 +08:00
|
|
|
* describing the ranges before, in, and after the given allocation.
|
|
|
|
*/
|
|
|
|
extent_split_interior_ok,
|
|
|
|
/*
|
|
|
|
* The extent can't satisfy the given allocation request. None of the
|
2019-12-10 06:36:45 +08:00
|
|
|
* input edata_t *s are touched.
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
|
|
|
extent_split_interior_cant_alloc,
|
|
|
|
/*
|
|
|
|
* In a potentially invalid state. Must leak (if *to_leak is non-NULL),
|
|
|
|
* and salvage what's still salvageable (if *to_salvage is non-NULL).
|
2019-12-10 06:36:45 +08:00
|
|
|
* None of lead, edata, or trail are valid.
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
|
|
|
extent_split_interior_error
|
|
|
|
} extent_split_interior_result_t;
|
|
|
|
|
|
|
|
static extent_split_interior_result_t
|
|
|
|
extent_split_interior(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
|
|
|
/* The result of splitting, in case of success. */
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t **edata, edata_t **lead, edata_t **trail,
|
2019-12-04 10:31:47 +08:00
|
|
|
/* The mess to clean up, in case of error. */
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t **to_leak, edata_t **to_salvage,
|
2019-12-04 10:31:47 +08:00
|
|
|
void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
|
|
|
|
szind_t szind, bool growing_retained) {
|
|
|
|
size_t esize = size + pad;
|
2019-12-10 06:36:45 +08:00
|
|
|
size_t leadsize = ALIGNMENT_CEILING((uintptr_t)edata_base_get(*edata),
|
|
|
|
PAGE_CEILING(alignment)) - (uintptr_t)edata_base_get(*edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
assert(new_addr == NULL || leadsize == 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_size_get(*edata) < leadsize + esize) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return extent_split_interior_cant_alloc;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
size_t trailsize = edata_size_get(*edata) - leadsize - esize;
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
*lead = NULL;
|
|
|
|
*trail = NULL;
|
|
|
|
*to_leak = NULL;
|
|
|
|
*to_salvage = NULL;
|
|
|
|
|
|
|
|
/* Split the lead. */
|
|
|
|
if (leadsize != 0) {
|
2019-12-10 06:36:45 +08:00
|
|
|
*lead = *edata;
|
2019-12-14 05:52:34 +08:00
|
|
|
*edata = extent_split_impl(tsdn, &arena->edata_cache, ehooks,
|
|
|
|
*lead, leadsize, SC_NSIZES, false, esize + trailsize, szind,
|
|
|
|
slab, growing_retained);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (*edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*to_leak = *lead;
|
|
|
|
*lead = NULL;
|
|
|
|
return extent_split_interior_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Split the trail. */
|
|
|
|
if (trailsize != 0) {
|
2019-12-14 05:52:34 +08:00
|
|
|
*trail = extent_split_impl(tsdn, &arena->edata_cache, ehooks,
|
|
|
|
*edata, esize, szind, slab, trailsize, SC_NSIZES, false,
|
|
|
|
growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (*trail == NULL) {
|
2019-12-10 06:36:45 +08:00
|
|
|
*to_leak = *edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
*to_salvage = *lead;
|
|
|
|
*lead = NULL;
|
2019-12-10 06:36:45 +08:00
|
|
|
*edata = NULL;
|
2019-12-04 10:31:47 +08:00
|
|
|
return extent_split_interior_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (leadsize == 0 && trailsize == 0) {
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_remap(tsdn, &emap_global, *edata, size, szind, slab);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return extent_split_interior_ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This fulfills the indicated allocation request out of the given extent (which
|
|
|
|
* the caller should have ensured was big enough). If there's any unused space
|
|
|
|
* before or after the resulting allocation, that space is given its own extent
|
2019-12-13 08:25:24 +08:00
|
|
|
* and put back into ecache.
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_recycle_split(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2020-02-06 05:51:05 +08:00
|
|
|
ecache_t *ecache, void *new_addr, size_t size, size_t pad, size_t alignment,
|
|
|
|
bool slab, szind_t szind, edata_t *edata, bool growing_retained) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *lead;
|
|
|
|
edata_t *trail;
|
|
|
|
edata_t *to_leak;
|
|
|
|
edata_t *to_salvage;
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
extent_split_interior_result_t result = extent_split_interior(
|
2020-02-06 05:51:05 +08:00
|
|
|
tsdn, arena, ehooks, &edata, &lead, &trail, &to_leak, &to_salvage,
|
|
|
|
new_addr, size, pad, alignment, slab, szind, growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (!maps_coalesce && result != extent_split_interior_ok
|
|
|
|
&& !opt_retain) {
|
|
|
|
/*
|
|
|
|
* Split isn't supported (implies Windows w/o retain). Avoid
|
2019-12-13 08:25:24 +08:00
|
|
|
* leaking the extent.
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
|
|
|
assert(to_leak != NULL && lead == NULL && trail == NULL);
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate(tsdn, ecache, to_leak);
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == extent_split_interior_ok) {
|
|
|
|
if (lead != NULL) {
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate(tsdn, ecache, lead);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
if (trail != NULL) {
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate(tsdn, ecache, trail);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We should have picked an extent that was large enough to
|
|
|
|
* fulfill our allocation request.
|
|
|
|
*/
|
|
|
|
assert(result == extent_split_interior_error);
|
|
|
|
if (to_salvage != NULL) {
|
|
|
|
extent_deregister(tsdn, to_salvage);
|
|
|
|
}
|
|
|
|
if (to_leak != NULL) {
|
2019-12-10 06:36:45 +08:00
|
|
|
void *leak = edata_base_get(to_leak);
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_deregister_no_gdump_sub(tsdn, to_leak);
|
2019-12-13 08:25:24 +08:00
|
|
|
extents_abandon_vm(tsdn, arena, ehooks, ecache, to_leak,
|
2019-12-04 10:31:47 +08:00
|
|
|
growing_retained);
|
2020-01-28 05:55:46 +08:00
|
|
|
assert(emap_lock_edata_from_addr(tsdn, &emap_global,
|
2020-02-06 05:51:05 +08:00
|
|
|
leak, false) == NULL);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
unreachable();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tries to satisfy the given allocation request by reusing one of the extents
|
2019-12-13 08:25:24 +08:00
|
|
|
* in the given ecache_t.
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-13 08:25:24 +08:00
|
|
|
extent_recycle(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks, ecache_t *ecache,
|
2019-12-04 10:31:47 +08:00
|
|
|
void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
|
|
|
|
szind_t szind, bool *zero, bool *commit, bool growing_retained) {
|
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
|
|
|
assert(new_addr == NULL || !slab);
|
|
|
|
assert(pad == 0 || !slab);
|
|
|
|
assert(!*zero || !slab);
|
|
|
|
|
2020-02-06 05:51:05 +08:00
|
|
|
edata_t *edata = extent_recycle_extract(tsdn, arena, ehooks, ecache,
|
|
|
|
new_addr, size, pad, alignment, slab, growing_retained);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2020-02-06 05:51:05 +08:00
|
|
|
edata = extent_recycle_split(tsdn, arena, ehooks, ecache, new_addr,
|
|
|
|
size, pad, alignment, slab, szind, edata, growing_retained);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (*commit && !edata_committed_get(edata)) {
|
2019-12-14 05:46:25 +08:00
|
|
|
if (extent_commit_impl(tsdn, ehooks, edata, 0,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_size_get(edata), growing_retained)) {
|
2019-12-13 08:25:24 +08:00
|
|
|
extent_record(tsdn, arena, ehooks, ecache, edata,
|
2019-12-04 10:31:47 +08:00
|
|
|
growing_retained);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_committed_get(edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*commit = true;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_zeroed_get(edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*zero = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pad != 0) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_addr_randomize(tsdn, arena, edata, alignment);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_state_get(edata) == extent_state_active);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (slab) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_slab_set(edata, slab);
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_register_interior(tsdn, &emap_global, edata, szind);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (*zero) {
|
2019-12-10 06:36:45 +08:00
|
|
|
void *addr = edata_base_get(edata);
|
|
|
|
if (!edata_zeroed_get(edata)) {
|
|
|
|
size_t size = edata_size_get(edata);
|
2019-12-14 02:44:03 +08:00
|
|
|
ehooks_zero(tsdn, ehooks, addr, size);
|
2019-12-05 09:55:24 +08:00
|
|
|
}
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If virtual memory is retained, create increasingly larger extents from which
|
|
|
|
* to split requested extents in order to limit the total number of disjoint
|
|
|
|
* virtual memory ranges retained by each arena.
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_grow_retained(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
|
|
|
size_t size, size_t pad, size_t alignment, bool slab, szind_t szind,
|
|
|
|
bool *zero, bool *commit) {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_assert_owner(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
assert(pad == 0 || !slab);
|
|
|
|
assert(!*zero || !slab);
|
|
|
|
|
|
|
|
size_t esize = size + pad;
|
|
|
|
size_t alloc_size_min = esize + PAGE_CEILING(alignment) - PAGE;
|
|
|
|
/* Beware size_t wrap-around. */
|
|
|
|
if (alloc_size_min < esize) {
|
|
|
|
goto label_err;
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Find the next extent size in the series that would be large enough to
|
|
|
|
* satisfy this request.
|
|
|
|
*/
|
|
|
|
pszind_t egn_skip = 0;
|
2019-12-13 08:25:24 +08:00
|
|
|
size_t alloc_size = sz_pind2sz(arena->ecache_grow.next + egn_skip);
|
2019-12-04 10:31:47 +08:00
|
|
|
while (alloc_size < alloc_size_min) {
|
|
|
|
egn_skip++;
|
2019-12-13 08:25:24 +08:00
|
|
|
if (arena->ecache_grow.next + egn_skip >=
|
2019-12-04 10:31:47 +08:00
|
|
|
sz_psz2ind(SC_LARGE_MAXCLASS)) {
|
|
|
|
/* Outside legal range. */
|
|
|
|
goto label_err;
|
|
|
|
}
|
2019-12-13 08:25:24 +08:00
|
|
|
alloc_size = sz_pind2sz(arena->ecache_grow.next + egn_skip);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-13 10:28:37 +08:00
|
|
|
edata_t *edata = edata_cache_get(tsdn, &arena->edata_cache);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_err;
|
|
|
|
}
|
|
|
|
bool zeroed = false;
|
|
|
|
bool committed = false;
|
|
|
|
|
|
|
|
void *ptr = ehooks_alloc(tsdn, ehooks, NULL, alloc_size, PAGE, &zeroed,
|
2019-12-14 02:44:03 +08:00
|
|
|
&committed);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (ptr == NULL) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_err;
|
|
|
|
}
|
|
|
|
|
2020-02-06 05:51:05 +08:00
|
|
|
edata_init(edata, arena_ind_get(arena), ptr, alloc_size, false,
|
|
|
|
SC_NSIZES, arena_extent_sn_next(arena), extent_state_active, zeroed,
|
|
|
|
committed, true, EXTENT_IS_HEAD);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (extent_register_no_gdump_add(tsdn, edata)) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_err;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_zeroed_get(edata) && edata_committed_get(edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*zero = true;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_committed_get(edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*commit = true;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *lead;
|
|
|
|
edata_t *trail;
|
|
|
|
edata_t *to_leak;
|
|
|
|
edata_t *to_salvage;
|
2020-02-06 05:51:05 +08:00
|
|
|
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_split_interior_result_t result = extent_split_interior(tsdn,
|
2020-02-06 05:51:05 +08:00
|
|
|
arena, ehooks, &edata, &lead, &trail, &to_leak,
|
2019-12-04 10:31:47 +08:00
|
|
|
&to_salvage, NULL, size, pad, alignment, slab, szind, true);
|
|
|
|
|
|
|
|
if (result == extent_split_interior_ok) {
|
|
|
|
if (lead != NULL) {
|
|
|
|
extent_record(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, lead, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
if (trail != NULL) {
|
|
|
|
extent_record(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, trail, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We should have allocated a sufficiently large extent; the
|
|
|
|
* cant_alloc case should not occur.
|
|
|
|
*/
|
|
|
|
assert(result == extent_split_interior_error);
|
|
|
|
if (to_salvage != NULL) {
|
|
|
|
if (config_prof) {
|
|
|
|
extent_gdump_add(tsdn, to_salvage);
|
|
|
|
}
|
|
|
|
extent_record(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, to_salvage, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
if (to_leak != NULL) {
|
|
|
|
extent_deregister_no_gdump_sub(tsdn, to_leak);
|
|
|
|
extents_abandon_vm(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, to_leak, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
goto label_err;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (*commit && !edata_committed_get(edata)) {
|
2019-12-14 05:46:25 +08:00
|
|
|
if (extent_commit_impl(tsdn, ehooks, edata, 0,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_size_get(edata), true)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_record(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, edata, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_err;
|
|
|
|
}
|
2019-12-05 09:55:24 +08:00
|
|
|
/* A successful commit should return zeroed memory. */
|
|
|
|
if (config_debug) {
|
2019-12-10 06:36:45 +08:00
|
|
|
void *addr = edata_addr_get(edata);
|
2019-12-05 09:55:24 +08:00
|
|
|
size_t *p = (size_t *)(uintptr_t)addr;
|
|
|
|
/* Check the first page only. */
|
|
|
|
for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
|
|
|
|
assert(p[i] == 0);
|
|
|
|
}
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Increment extent_grow_next if doing so wouldn't exceed the allowed
|
|
|
|
* range.
|
|
|
|
*/
|
2019-12-13 08:25:24 +08:00
|
|
|
if (arena->ecache_grow.next + egn_skip + 1 <=
|
|
|
|
arena->ecache_grow.limit) {
|
|
|
|
arena->ecache_grow.next += egn_skip + 1;
|
2019-12-04 10:31:47 +08:00
|
|
|
} else {
|
2019-12-13 08:25:24 +08:00
|
|
|
arena->ecache_grow.next = arena->ecache_grow.limit;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
/* All opportunities for failure are past. */
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (config_prof) {
|
|
|
|
/* Adjust gdump stats now that extent is final size. */
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_add(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
if (pad != 0) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_addr_randomize(tsdn, arena, edata, alignment);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
if (slab) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_slab_set(edata, true);
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_register_interior(tsdn, &emap_global, edata, szind);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
if (*zero && !edata_zeroed_get(edata)) {
|
|
|
|
void *addr = edata_base_get(edata);
|
|
|
|
size_t size = edata_size_get(edata);
|
2019-12-14 02:44:03 +08:00
|
|
|
ehooks_zero(tsdn, ehooks, addr, size);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
label_err:
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-04 10:31:47 +08:00
|
|
|
extent_alloc_retained(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
|
|
|
void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
|
|
|
|
szind_t szind, bool *zero, bool *commit) {
|
|
|
|
assert(size != 0);
|
|
|
|
assert(alignment != 0);
|
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata = extent_recycle(tsdn, arena, ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
&arena->ecache_retained, new_addr, size, pad, alignment, slab,
|
2019-12-04 10:31:47 +08:00
|
|
|
szind, zero, commit, true);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata != NULL) {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (config_prof) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_add(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
} else if (opt_retain && new_addr == NULL) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata = extent_grow_retained(tsdn, arena, ehooks, size, pad,
|
2019-12-04 10:31:47 +08:00
|
|
|
alignment, slab, szind, zero, commit);
|
|
|
|
/* extent_grow_retained() always releases extent_grow_mtx. */
|
|
|
|
} else {
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_assert_not_owner(tsdn, &arena->ecache_grow.mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-13 09:30:28 +08:00
|
|
|
edata_t *
|
|
|
|
extent_alloc_wrapper(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-04 10:31:47 +08:00
|
|
|
void *new_addr, size_t size, size_t pad, size_t alignment, bool slab,
|
|
|
|
szind_t szind, bool *zero, bool *commit) {
|
2019-12-13 09:30:28 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t esize = size + pad;
|
2019-12-13 10:28:37 +08:00
|
|
|
edata_t *edata = edata_cache_get(tsdn, &arena->edata_cache);
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata == NULL) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
size_t palignment = ALIGNMENT_CEILING(alignment, PAGE);
|
|
|
|
void *addr = ehooks_alloc(tsdn, ehooks, new_addr, esize, palignment,
|
2019-12-14 02:44:03 +08:00
|
|
|
zero, commit);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (addr == NULL) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_init(edata, arena_ind_get(arena), addr, esize, slab, szind,
|
2019-12-04 10:31:47 +08:00
|
|
|
arena_extent_sn_next(arena), extent_state_active, *zero, *commit,
|
|
|
|
true, EXTENT_NOT_HEAD);
|
|
|
|
if (pad != 0) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_addr_randomize(tsdn, arena, edata, alignment);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
if (extent_register(tsdn, edata)) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-14 02:52:51 +08:00
|
|
|
extent_can_coalesce(ecache_t *ecache, const edata_t *inner,
|
2019-12-10 06:36:45 +08:00
|
|
|
const edata_t *outer) {
|
2019-12-14 03:33:03 +08:00
|
|
|
assert(edata_arena_ind_get(inner) == ecache_ind_get(ecache));
|
|
|
|
|
2019-12-14 02:52:51 +08:00
|
|
|
if (edata_arena_ind_get(inner) != edata_arena_ind_get(outer)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_state_get(inner) == extent_state_active);
|
2019-12-13 08:44:49 +08:00
|
|
|
if (edata_state_get(outer) != ecache->state) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (edata_committed_get(inner) != edata_committed_get(outer)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-14 05:37:00 +08:00
|
|
|
extent_coalesce(tsdn_t *tsdn, edata_cache_t *edata_cache, ehooks_t *ehooks,
|
2019-12-13 08:25:24 +08:00
|
|
|
ecache_t *ecache, edata_t *inner, edata_t *outer, bool forward,
|
|
|
|
bool growing_retained) {
|
2019-12-14 02:52:51 +08:00
|
|
|
assert(extent_can_coalesce(ecache, inner, outer));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_activate_locked(tsdn, ecache, outer);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-14 05:37:00 +08:00
|
|
|
bool err = extent_merge_impl(tsdn, ehooks, edata_cache,
|
2019-12-04 10:31:47 +08:00
|
|
|
forward ? inner : outer, forward ? outer : inner, growing_retained);
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (err) {
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate_locked(tsdn, ecache, outer);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_try_coalesce_impl(tsdn_t *tsdn, edata_cache_t *edata_cache,
|
2020-02-06 05:51:05 +08:00
|
|
|
ehooks_t *ehooks, ecache_t *ecache, edata_t *edata, bool *coalesced,
|
|
|
|
bool growing_retained, bool inactive_only) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/*
|
|
|
|
* We avoid checking / locking inactive neighbors for large size
|
|
|
|
* classes, since they are eagerly coalesced on deallocation which can
|
|
|
|
* cause lock contention.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Continue attempting to coalesce until failure, to protect against
|
|
|
|
* races with other threads that are thwarted by this one.
|
|
|
|
*/
|
|
|
|
bool again;
|
|
|
|
do {
|
|
|
|
again = false;
|
|
|
|
|
|
|
|
/* Try to coalesce forward. */
|
2020-01-28 05:55:46 +08:00
|
|
|
edata_t *next = emap_lock_edata_from_addr(tsdn, &emap_global,
|
2020-02-06 05:51:05 +08:00
|
|
|
edata_past_get(edata), inactive_only);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (next != NULL) {
|
|
|
|
/*
|
2019-12-13 08:25:24 +08:00
|
|
|
* ecache->mtx only protects against races for
|
|
|
|
* like-state extents, so call extent_can_coalesce()
|
2019-12-04 10:31:47 +08:00
|
|
|
* before releasing next's pool lock.
|
|
|
|
*/
|
2019-12-14 02:52:51 +08:00
|
|
|
bool can_coalesce = extent_can_coalesce(ecache,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata, next);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, next);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-14 05:46:25 +08:00
|
|
|
if (can_coalesce && !extent_coalesce(tsdn, edata_cache,
|
|
|
|
ehooks, ecache, edata, next, true,
|
|
|
|
growing_retained)) {
|
2019-12-13 08:33:19 +08:00
|
|
|
if (ecache->delay_coalesce) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/* Do minimal coalescing. */
|
|
|
|
*coalesced = true;
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
again = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to coalesce backward. */
|
2020-01-28 05:55:46 +08:00
|
|
|
edata_t *prev = emap_lock_edata_from_addr(tsdn, &emap_global,
|
2020-02-06 05:51:05 +08:00
|
|
|
edata_before_get(edata), inactive_only);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (prev != NULL) {
|
2019-12-14 02:52:51 +08:00
|
|
|
bool can_coalesce = extent_can_coalesce(ecache, edata,
|
|
|
|
prev);
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata(tsdn, &emap_global, prev);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-14 05:46:25 +08:00
|
|
|
if (can_coalesce && !extent_coalesce(tsdn, edata_cache,
|
|
|
|
ehooks, ecache, edata, prev, false,
|
|
|
|
growing_retained)) {
|
2019-12-10 06:36:45 +08:00
|
|
|
edata = prev;
|
2019-12-13 08:33:19 +08:00
|
|
|
if (ecache->delay_coalesce) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/* Do minimal coalescing. */
|
|
|
|
*coalesced = true;
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
again = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (again);
|
|
|
|
|
2019-12-13 08:33:19 +08:00
|
|
|
if (ecache->delay_coalesce) {
|
2019-12-04 10:31:47 +08:00
|
|
|
*coalesced = false;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
return edata;
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_try_coalesce(tsdn_t *tsdn, edata_cache_t *edata_cache, ehooks_t *ehooks,
|
2020-02-06 05:51:05 +08:00
|
|
|
ecache_t *ecache, edata_t *edata, bool *coalesced, bool growing_retained) {
|
|
|
|
return extent_try_coalesce_impl(tsdn, edata_cache, ehooks, ecache,
|
|
|
|
edata, coalesced, growing_retained, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_try_coalesce_large(tsdn_t *tsdn, edata_cache_t *edata_cache,
|
2020-02-06 05:51:05 +08:00
|
|
|
ehooks_t *ehooks, ecache_t *ecache, edata_t *edata, bool *coalesced,
|
|
|
|
bool growing_retained) {
|
|
|
|
return extent_try_coalesce_impl(tsdn, edata_cache, ehooks, ecache,
|
|
|
|
edata, coalesced, growing_retained, true);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Does the metadata management portions of putting an unused extent into the
|
2019-12-13 08:25:24 +08:00
|
|
|
* given ecache_t (coalesces, deregisters slab interiors, the heap operations).
|
2019-12-04 10:31:47 +08:00
|
|
|
*/
|
|
|
|
static void
|
2019-12-13 08:25:24 +08:00
|
|
|
extent_record(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks, ecache_t *ecache,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, bool growing_retained) {
|
2019-12-13 08:44:49 +08:00
|
|
|
assert((ecache->state != extent_state_dirty &&
|
|
|
|
ecache->state != extent_state_muzzy) ||
|
2019-12-10 06:36:45 +08:00
|
|
|
!edata_zeroed_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_lock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_szind_set(edata, SC_NSIZES);
|
|
|
|
if (edata_slab_get(edata)) {
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_deregister_interior(tsdn, &emap_global, edata);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_slab_set(edata, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_assert_mapped(tsdn, &emap_global, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:33:19 +08:00
|
|
|
if (!ecache->delay_coalesce) {
|
2019-12-14 05:46:25 +08:00
|
|
|
edata = extent_try_coalesce(tsdn, &arena->edata_cache, ehooks,
|
2020-02-06 05:51:05 +08:00
|
|
|
ecache, edata, NULL, growing_retained);
|
2019-12-10 06:36:45 +08:00
|
|
|
} else if (edata_size_get(edata) >= SC_LARGE_MINCLASS) {
|
2019-12-13 08:25:24 +08:00
|
|
|
assert(ecache == &arena->ecache_dirty);
|
|
|
|
/* Always coalesce large extents eagerly. */
|
2019-12-04 10:31:47 +08:00
|
|
|
bool coalesced;
|
|
|
|
do {
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_state_get(edata) == extent_state_active);
|
2019-12-14 05:46:25 +08:00
|
|
|
edata = extent_try_coalesce_large(tsdn,
|
2020-02-06 05:51:05 +08:00
|
|
|
&arena->edata_cache, ehooks, ecache, edata,
|
|
|
|
&coalesced, growing_retained);
|
2019-12-04 10:31:47 +08:00
|
|
|
} while (coalesced);
|
2020-01-14 08:18:32 +08:00
|
|
|
if (edata_size_get(edata) >= oversize_threshold &&
|
|
|
|
arena_may_force_decay(arena)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/* Shortcut to purge the oversize extent eagerly. */
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-10 06:36:45 +08:00
|
|
|
arena_decay_extent(tsdn, arena, ehooks, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2019-12-14 05:34:35 +08:00
|
|
|
extent_deactivate_locked(tsdn, ecache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
malloc_mutex_unlock(tsdn, &ecache->mtx);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_dalloc_gap(tsdn_t *tsdn, arena_t *arena, edata_t *edata) {
|
2019-12-04 10:31:47 +08:00
|
|
|
ehooks_t *ehooks = arena_get_ehooks(arena);
|
|
|
|
|
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
if (extent_register(tsdn, edata)) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_dalloc_wrapper(tsdn, arena, ehooks, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
extent_dalloc_wrapper_try(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata) {
|
2019-12-04 10:31:47 +08:00
|
|
|
bool err;
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_base_get(edata) != NULL);
|
|
|
|
assert(edata_size_get(edata) != 0);
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_addr_set(edata, edata_base_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
/* Try to deallocate. */
|
2019-12-10 06:36:45 +08:00
|
|
|
err = ehooks_dalloc(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), edata_committed_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (!err) {
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
extent_dalloc_wrapper(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata) {
|
|
|
|
assert(edata_dumpable_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
|
|
|
/* Avoid calling the default extent_dalloc unless have to. */
|
2019-12-12 05:35:43 +08:00
|
|
|
if (!ehooks_dalloc_will_fail(ehooks)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
/*
|
|
|
|
* Deregister first to avoid a race with other allocating
|
|
|
|
* threads, and reregister if deallocation fails.
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister(tsdn, edata);
|
|
|
|
if (!extent_dalloc_wrapper_try(tsdn, arena, ehooks, edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
return;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_reregister(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Try to decommit; purge if that fails. */
|
|
|
|
bool zeroed;
|
2019-12-10 06:36:45 +08:00
|
|
|
if (!edata_committed_get(edata)) {
|
2019-12-04 10:31:47 +08:00
|
|
|
zeroed = true;
|
2019-12-14 05:46:25 +08:00
|
|
|
} else if (!extent_decommit_wrapper(tsdn, ehooks, edata, 0,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_size_get(edata))) {
|
2019-12-04 10:31:47 +08:00
|
|
|
zeroed = true;
|
2019-12-10 06:36:45 +08:00
|
|
|
} else if (!ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), 0, edata_size_get(edata))) {
|
2019-12-04 10:31:47 +08:00
|
|
|
zeroed = true;
|
2019-12-10 06:36:45 +08:00
|
|
|
} else if (edata_state_get(edata) == extent_state_muzzy ||
|
|
|
|
!ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), 0, edata_size_get(edata))) {
|
2019-12-04 10:31:47 +08:00
|
|
|
zeroed = false;
|
|
|
|
} else {
|
|
|
|
zeroed = false;
|
|
|
|
}
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_zeroed_set(edata, zeroed);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (config_prof) {
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_gdump_sub(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
2019-12-13 08:25:24 +08:00
|
|
|
extent_record(tsdn, arena, ehooks, &arena->ecache_retained, edata,
|
|
|
|
false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
extent_destroy_wrapper(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata) {
|
|
|
|
assert(edata_base_get(edata) != NULL);
|
|
|
|
assert(edata_size_get(edata) != 0);
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
|
|
|
|
|
|
|
/* Deregister first to avoid a race with other allocating threads. */
|
2019-12-10 06:36:45 +08:00
|
|
|
extent_deregister(tsdn, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_addr_set(edata, edata_base_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
/* Try to destroy; silently fail otherwise. */
|
2019-12-10 06:36:45 +08:00
|
|
|
ehooks_destroy(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), edata_committed_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-12 03:17:19 +08:00
|
|
|
edata_cache_put(tsdn, &arena->edata_cache, edata);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_commit_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
|
|
|
size_t offset, size_t length, bool growing_retained) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
bool err = ehooks_commit(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), offset, length);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_committed_set(edata, edata_committed_get(edata) || !err);
|
2019-12-04 10:31:47 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_commit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
|
|
|
size_t offset, size_t length) {
|
|
|
|
return extent_commit_impl(tsdn, ehooks, edata, offset, length,
|
2019-12-04 10:31:47 +08:00
|
|
|
false);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-12-14 05:46:25 +08:00
|
|
|
extent_decommit_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
|
|
|
size_t offset, size_t length) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
bool err = ehooks_decommit(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), offset, length);
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_committed_set(edata, edata_committed_get(edata) && err);
|
2019-12-04 10:31:47 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
extent_purge_lazy_impl(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t offset, size_t length, bool growing_retained) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
bool err = ehooks_purge_lazy(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), offset, length);
|
2019-12-04 10:31:47 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
extent_purge_lazy_wrapper(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t offset, size_t length) {
|
|
|
|
return extent_purge_lazy_impl(tsdn, arena, ehooks, edata, offset,
|
2019-12-04 10:31:47 +08:00
|
|
|
length, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
extent_purge_forced_impl(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t offset, size_t length, bool growing_retained) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
bool err = ehooks_purge_forced(tsdn, ehooks, edata_base_get(edata),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(edata), offset, length);
|
2019-12-04 10:31:47 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
extent_purge_forced_wrapper(tsdn_t *tsdn, arena_t *arena, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t offset, size_t length) {
|
|
|
|
return extent_purge_forced_impl(tsdn, arena, ehooks, edata,
|
2019-12-04 10:31:47 +08:00
|
|
|
offset, length, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Accepts the extent to split, and the characteristics of each side of the
|
|
|
|
* split. The 'a' parameters go with the 'lead' of the resulting pair of
|
|
|
|
* extents (the lower addressed portion of the split), and the 'b' parameters go
|
|
|
|
* with the trail (the higher addressed portion). This makes 'extent' the lead,
|
|
|
|
* and returns the trail (except in case of error).
|
|
|
|
*/
|
2019-12-10 06:36:45 +08:00
|
|
|
static edata_t *
|
2019-12-14 05:52:34 +08:00
|
|
|
extent_split_impl(tsdn_t *tsdn, edata_cache_t *edata_cache, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t size_a, szind_t szind_a, bool slab_a,
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t size_b, szind_t szind_b, bool slab_b, bool growing_retained) {
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_size_get(edata) == size_a + size_b);
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
|
|
|
|
|
|
|
if (ehooks_split_will_fail(ehooks)) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-14 05:52:34 +08:00
|
|
|
edata_t *trail = edata_cache_get(tsdn, edata_cache);
|
2019-12-04 10:31:47 +08:00
|
|
|
if (trail == NULL) {
|
|
|
|
goto label_error_a;
|
|
|
|
}
|
|
|
|
|
2020-02-04 06:21:48 +08:00
|
|
|
emap_prepare_t prepare;
|
2020-02-06 05:51:05 +08:00
|
|
|
bool err = emap_split_prepare(tsdn, &emap_global, &prepare, edata,
|
|
|
|
size_a, szind_a, slab_a, trail, size_b, szind_b, slab_b);
|
2020-02-04 05:27:21 +08:00
|
|
|
if (err) {
|
2019-12-04 10:31:47 +08:00
|
|
|
goto label_error_b;
|
|
|
|
}
|
|
|
|
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_lock_edata2(tsdn, &emap_global, edata, trail);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2020-02-04 05:27:21 +08:00
|
|
|
err = ehooks_split(tsdn, ehooks, edata_base_get(edata), size_a + size_b,
|
|
|
|
size_a, size_b, edata_committed_get(edata));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
goto label_error_c;
|
|
|
|
}
|
|
|
|
|
2020-02-04 06:21:48 +08:00
|
|
|
emap_split_commit(tsdn, &emap_global, &prepare, edata, size_a, szind_a,
|
|
|
|
slab_a, trail, size_b, szind_b, slab_b);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata2(tsdn, &emap_global, edata, trail);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
return trail;
|
|
|
|
label_error_c:
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata2(tsdn, &emap_global, edata, trail);
|
2019-12-04 10:31:47 +08:00
|
|
|
label_error_b:
|
2019-12-14 05:52:34 +08:00
|
|
|
edata_cache_put(tsdn, edata_cache, trail);
|
2019-12-04 10:31:47 +08:00
|
|
|
label_error_a:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *
|
2019-12-14 05:52:34 +08:00
|
|
|
extent_split_wrapper(tsdn_t *tsdn, edata_cache_t *edata_cache, ehooks_t *ehooks,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *edata, size_t size_a, szind_t szind_a, bool slab_a,
|
2019-12-04 10:31:47 +08:00
|
|
|
size_t size_b, szind_t szind_b, bool slab_b) {
|
2019-12-14 05:52:34 +08:00
|
|
|
return extent_split_impl(tsdn, edata_cache, ehooks, edata, size_a,
|
|
|
|
szind_a, slab_a, size_b, szind_b, slab_b, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
2019-12-14 03:16:58 +08:00
|
|
|
extent_merge_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_cache_t *edata_cache,
|
|
|
|
edata_t *a, edata_t *b, bool growing_retained) {
|
2019-12-04 10:31:47 +08:00
|
|
|
witness_assert_depth_to_rank(tsdn_witness_tsdp_get(tsdn),
|
|
|
|
WITNESS_RANK_CORE, growing_retained ? 1 : 0);
|
2019-12-10 06:36:45 +08:00
|
|
|
assert(edata_base_get(a) < edata_base_get(b));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-14 03:16:58 +08:00
|
|
|
assert(edata_arena_ind_get(a) == edata_arena_ind_get(b));
|
|
|
|
assert(edata_arena_ind_get(a) == ehooks_ind_get(ehooks));
|
|
|
|
|
2019-12-10 06:36:45 +08:00
|
|
|
bool err = ehooks_merge(tsdn, ehooks, edata_base_get(a),
|
2019-12-12 09:23:24 +08:00
|
|
|
edata_size_get(a), edata_is_head_get(a), edata_base_get(b),
|
2019-12-14 02:44:03 +08:00
|
|
|
edata_size_get(b), edata_is_head_get(b), edata_committed_get(a));
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rtree writes must happen while all the relevant elements are
|
|
|
|
* owned, so the following code uses decomposed helper functions rather
|
|
|
|
* than extent_{,de}register() to do things in the right order.
|
|
|
|
*/
|
2020-02-04 06:21:48 +08:00
|
|
|
emap_prepare_t prepare;
|
2020-02-06 05:51:05 +08:00
|
|
|
emap_merge_prepare(tsdn, &emap_global, &prepare, a, b);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2020-02-04 06:21:48 +08:00
|
|
|
emap_lock_edata2(tsdn, &emap_global, a, b);
|
|
|
|
emap_merge_commit(tsdn, &emap_global, &prepare, a, b);
|
2020-01-28 05:55:46 +08:00
|
|
|
emap_unlock_edata2(tsdn, &emap_global, a, b);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
2019-12-14 03:16:58 +08:00
|
|
|
edata_cache_put(tsdn, edata_cache, b);
|
2019-12-04 10:31:47 +08:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2019-12-14 03:16:58 +08:00
|
|
|
extent_merge_wrapper(tsdn_t *tsdn, ehooks_t *ehooks, edata_cache_t *edata_cache,
|
2019-12-10 06:36:45 +08:00
|
|
|
edata_t *a, edata_t *b) {
|
2019-12-14 03:16:58 +08:00
|
|
|
return extent_merge_impl(tsdn, ehooks, edata_cache, a, b, false);
|
2019-12-04 10:31:47 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
extent_boot(void) {
|
2019-12-19 05:38:14 +08:00
|
|
|
assert(sizeof(slab_data_t) >= sizeof(e_prof_info_t));
|
|
|
|
|
2019-12-04 10:31:47 +08:00
|
|
|
if (have_dss) {
|
|
|
|
extent_dss_boot();
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|