2020-07-11 08:40:13 +08:00
|
|
|
#include "jemalloc/internal/jemalloc_preamble.h"
|
|
|
|
#include "jemalloc/internal/jemalloc_internal_includes.h"
|
|
|
|
|
|
|
|
#include "jemalloc/internal/psset.h"
|
|
|
|
|
|
|
|
#include "jemalloc/internal/flat_bitmap.h"
|
|
|
|
|
|
|
|
void
|
|
|
|
psset_init(psset_t *psset) {
|
|
|
|
for (unsigned i = 0; i < PSSET_NPSIZES; i++) {
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_age_heap_new(&psset->pageslabs[i]);
|
2020-07-11 08:40:13 +08:00
|
|
|
}
|
2021-02-09 03:04:46 +08:00
|
|
|
fb_init(psset->bitmap, PSSET_NPSIZES);
|
2020-12-07 04:49:03 +08:00
|
|
|
memset(&psset->merged_stats, 0, sizeof(psset->merged_stats));
|
2020-11-11 08:23:03 +08:00
|
|
|
memset(&psset->stats, 0, sizeof(psset->stats));
|
2020-12-07 01:49:26 +08:00
|
|
|
hpdata_empty_list_init(&psset->empty);
|
|
|
|
hpdata_purge_list_init(&psset->to_purge);
|
|
|
|
hpdata_hugify_list_init(&psset->to_hugify);
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
|
|
|
|
2020-11-11 08:23:03 +08:00
|
|
|
static void
|
|
|
|
psset_bin_stats_accum(psset_bin_stats_t *dst, psset_bin_stats_t *src) {
|
2020-12-04 10:32:42 +08:00
|
|
|
dst->npageslabs += src->npageslabs;
|
|
|
|
dst->nactive += src->nactive;
|
2020-12-04 11:15:54 +08:00
|
|
|
dst->ndirty += src->ndirty;
|
2020-11-11 08:23:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
psset_stats_accum(psset_stats_t *dst, psset_stats_t *src) {
|
2020-12-04 10:32:42 +08:00
|
|
|
psset_bin_stats_accum(&dst->full_slabs[0], &src->full_slabs[0]);
|
|
|
|
psset_bin_stats_accum(&dst->full_slabs[1], &src->full_slabs[1]);
|
2020-12-06 09:42:04 +08:00
|
|
|
psset_bin_stats_accum(&dst->empty_slabs[0], &src->empty_slabs[0]);
|
|
|
|
psset_bin_stats_accum(&dst->empty_slabs[1], &src->empty_slabs[1]);
|
2020-11-11 08:23:03 +08:00
|
|
|
for (pszind_t i = 0; i < PSSET_NPSIZES; i++) {
|
2020-12-04 10:32:42 +08:00
|
|
|
psset_bin_stats_accum(&dst->nonfull_slabs[i][0],
|
|
|
|
&src->nonfull_slabs[i][0]);
|
|
|
|
psset_bin_stats_accum(&dst->nonfull_slabs[i][1],
|
|
|
|
&src->nonfull_slabs[i][1]);
|
2020-11-11 08:23:03 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-03 03:59:10 +08:00
|
|
|
/*
|
|
|
|
* The stats maintenance strategy is simple, but not necessarily obvious.
|
|
|
|
* edata_nfree and the bitmap must remain consistent at all times. If they
|
|
|
|
* change while an edata is within an edata_heap (or full), then the associated
|
|
|
|
* stats bin (or the full bin) must also change. If they change while not in a
|
|
|
|
* bin (say, in between extraction and reinsertion), then the bin stats need not
|
|
|
|
* change. If a pageslab is removed from a bin (or becomes nonfull), it should
|
|
|
|
* no longer contribute to that bin's stats (or the full stats). These help
|
|
|
|
* ensure we don't miss any heap modification operations.
|
|
|
|
*/
|
|
|
|
JEMALLOC_ALWAYS_INLINE void
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_insert_remove(psset_t *psset, psset_bin_stats_t *binstats,
|
|
|
|
hpdata_t *ps, bool insert) {
|
2020-11-10 05:49:30 +08:00
|
|
|
size_t mul = insert ? (size_t)1 : (size_t)-1;
|
2020-12-04 10:32:42 +08:00
|
|
|
size_t huge_idx = (size_t)hpdata_huge_get(ps);
|
2020-12-07 04:49:03 +08:00
|
|
|
|
2020-12-04 10:32:42 +08:00
|
|
|
binstats[huge_idx].npageslabs += mul * 1;
|
2020-12-04 11:15:54 +08:00
|
|
|
binstats[huge_idx].nactive += mul * hpdata_nactive_get(ps);
|
|
|
|
binstats[huge_idx].ndirty += mul * hpdata_ndirty_get(ps);
|
2020-12-07 04:49:03 +08:00
|
|
|
|
|
|
|
psset->merged_stats.npageslabs += mul * 1;
|
|
|
|
psset->merged_stats.nactive += mul * hpdata_nactive_get(ps);
|
|
|
|
psset->merged_stats.ndirty += mul * hpdata_ndirty_get(ps);
|
|
|
|
|
|
|
|
if (config_debug) {
|
|
|
|
psset_bin_stats_t check_stats = {0};
|
|
|
|
for (size_t huge = 0; huge <= 1; huge++) {
|
|
|
|
psset_bin_stats_accum(&check_stats,
|
|
|
|
&psset->stats.full_slabs[huge]);
|
|
|
|
psset_bin_stats_accum(&check_stats,
|
|
|
|
&psset->stats.empty_slabs[huge]);
|
|
|
|
for (pszind_t pind = 0; pind < PSSET_NPSIZES; pind++) {
|
|
|
|
psset_bin_stats_accum(&check_stats,
|
|
|
|
&psset->stats.nonfull_slabs[pind][huge]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assert(psset->merged_stats.npageslabs
|
|
|
|
== check_stats.npageslabs);
|
|
|
|
assert(psset->merged_stats.nactive == check_stats.nactive);
|
|
|
|
assert(psset->merged_stats.ndirty == check_stats.ndirty);
|
|
|
|
}
|
2020-11-10 05:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_insert(psset_t *psset, psset_bin_stats_t *binstats,
|
|
|
|
hpdata_t *ps) {
|
|
|
|
psset_bin_stats_insert_remove(psset, binstats, ps, true);
|
2020-11-10 05:49:30 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_remove(psset_t *psset, psset_bin_stats_t *binstats,
|
|
|
|
hpdata_t *ps) {
|
|
|
|
psset_bin_stats_insert_remove(psset, binstats, ps, false);
|
2020-11-10 05:49:30 +08:00
|
|
|
}
|
|
|
|
|
2020-09-03 03:59:10 +08:00
|
|
|
static void
|
2020-11-18 08:32:45 +08:00
|
|
|
psset_hpdata_heap_remove(psset_t *psset, pszind_t pind, hpdata_t *ps) {
|
|
|
|
hpdata_age_heap_remove(&psset->pageslabs[pind], ps);
|
2020-12-06 09:42:04 +08:00
|
|
|
if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
|
2021-02-09 03:04:46 +08:00
|
|
|
fb_unset(psset->bitmap, PSSET_NPSIZES, (size_t)pind);
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|
2020-09-03 03:59:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2020-11-18 08:32:45 +08:00
|
|
|
psset_hpdata_heap_insert(psset_t *psset, pszind_t pind, hpdata_t *ps) {
|
2020-12-06 09:42:04 +08:00
|
|
|
if (hpdata_age_heap_empty(&psset->pageslabs[pind])) {
|
2021-02-09 03:04:46 +08:00
|
|
|
fb_set(psset->bitmap, PSSET_NPSIZES, (size_t)pind);
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_age_heap_insert(&psset->pageslabs[pind], ps);
|
2020-07-11 08:40:13 +08:00
|
|
|
}
|
|
|
|
|
2020-12-07 01:49:26 +08:00
|
|
|
static void
|
|
|
|
psset_stats_insert(psset_t* psset, hpdata_t *ps) {
|
|
|
|
if (hpdata_empty(ps)) {
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_insert(psset, psset->stats.empty_slabs, ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
} else if (hpdata_full(ps)) {
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_insert(psset, psset->stats.full_slabs, ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
} else {
|
|
|
|
size_t longest_free_range = hpdata_longest_free_range_get(ps);
|
|
|
|
|
|
|
|
pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
|
|
|
|
longest_free_range << LG_PAGE));
|
|
|
|
assert(pind < PSSET_NPSIZES);
|
|
|
|
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_insert(psset, psset->stats.nonfull_slabs[pind],
|
|
|
|
ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
psset_stats_remove(psset_t *psset, hpdata_t *ps) {
|
|
|
|
if (hpdata_empty(ps)) {
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_remove(psset, psset->stats.empty_slabs, ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
} else if (hpdata_full(ps)) {
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_remove(psset, psset->stats.full_slabs, ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
} else {
|
|
|
|
size_t longest_free_range = hpdata_longest_free_range_get(ps);
|
|
|
|
|
|
|
|
pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
|
|
|
|
longest_free_range << LG_PAGE));
|
|
|
|
assert(pind < PSSET_NPSIZES);
|
|
|
|
|
2020-12-07 04:49:03 +08:00
|
|
|
psset_bin_stats_remove(psset, psset->stats.nonfull_slabs[pind],
|
|
|
|
ps);
|
2020-12-07 01:49:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
/*
|
2020-12-07 01:49:26 +08:00
|
|
|
* Put ps into some container so that it can be found during future allocation
|
|
|
|
* requests.
|
2020-12-06 09:42:04 +08:00
|
|
|
*/
|
|
|
|
static void
|
2020-12-07 01:49:26 +08:00
|
|
|
psset_alloc_container_insert(psset_t *psset, hpdata_t *ps) {
|
|
|
|
assert(!hpdata_in_psset_alloc_container_get(ps));
|
|
|
|
hpdata_in_psset_alloc_container_set(ps, true);
|
2020-12-06 09:42:04 +08:00
|
|
|
if (hpdata_empty(ps)) {
|
|
|
|
/*
|
|
|
|
* This prepend, paired with popping the head in psset_fit,
|
|
|
|
* means we implement LIFO ordering for the empty slabs set,
|
|
|
|
* which seems reasonable.
|
|
|
|
*/
|
2020-12-07 01:49:26 +08:00
|
|
|
hpdata_empty_list_prepend(&psset->empty, ps);
|
2020-12-06 09:42:04 +08:00
|
|
|
} else if (hpdata_full(ps)) {
|
|
|
|
/*
|
|
|
|
* We don't need to keep track of the full slabs; we're never
|
|
|
|
* going to return them from a psset_pick_alloc call.
|
|
|
|
*/
|
|
|
|
} else {
|
|
|
|
size_t longest_free_range = hpdata_longest_free_range_get(ps);
|
2020-12-06 07:58:31 +08:00
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
|
|
|
|
longest_free_range << LG_PAGE));
|
|
|
|
assert(pind < PSSET_NPSIZES);
|
2020-11-10 09:24:31 +08:00
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
psset_hpdata_heap_insert(psset, pind, ps);
|
2020-11-10 09:24:31 +08:00
|
|
|
}
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|
2020-11-10 09:24:31 +08:00
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
/* Remove ps from those collections. */
|
|
|
|
static void
|
2020-12-07 01:49:26 +08:00
|
|
|
psset_alloc_container_remove(psset_t *psset, hpdata_t *ps) {
|
|
|
|
assert(hpdata_in_psset_alloc_container_get(ps));
|
|
|
|
hpdata_in_psset_alloc_container_set(ps, false);
|
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
if (hpdata_empty(ps)) {
|
2020-12-07 01:49:26 +08:00
|
|
|
hpdata_empty_list_remove(&psset->empty, ps);
|
2020-12-06 09:42:04 +08:00
|
|
|
} else if (hpdata_full(ps)) {
|
2020-12-07 01:49:26 +08:00
|
|
|
/* Same as above -- do nothing in this case. */
|
2020-12-06 09:42:04 +08:00
|
|
|
} else {
|
|
|
|
size_t longest_free_range = hpdata_longest_free_range_get(ps);
|
|
|
|
|
|
|
|
pszind_t pind = sz_psz2ind(sz_psz_quantize_floor(
|
|
|
|
longest_free_range << LG_PAGE));
|
|
|
|
assert(pind < PSSET_NPSIZES);
|
|
|
|
|
|
|
|
psset_hpdata_heap_remove(psset, pind, ps);
|
2020-11-10 09:24:31 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
void
|
|
|
|
psset_update_begin(psset_t *psset, hpdata_t *ps) {
|
|
|
|
hpdata_assert_consistent(ps);
|
|
|
|
assert(hpdata_in_psset_get(ps));
|
|
|
|
hpdata_updating_set(ps, true);
|
2020-12-07 01:49:26 +08:00
|
|
|
psset_stats_remove(psset, ps);
|
|
|
|
if (hpdata_in_psset_alloc_container_get(ps)) {
|
|
|
|
/*
|
|
|
|
* Some metadata updates can break alloc container invariants
|
|
|
|
* (e.g. the longest free range determines the hpdata_heap_t the
|
|
|
|
* pageslab lives in).
|
|
|
|
*/
|
|
|
|
assert(hpdata_alloc_allowed_get(ps));
|
|
|
|
psset_alloc_container_remove(psset, ps);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* We don't update presence in the purge list or hugify list; we try to
|
|
|
|
* keep those FIFO, even in the presence of other metadata updates.
|
|
|
|
* We'll update presence at the end of the metadata update if necessary.
|
|
|
|
*/
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|
|
|
|
|
2020-11-10 09:24:31 +08:00
|
|
|
void
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_update_end(psset_t *psset, hpdata_t *ps) {
|
2020-12-06 09:42:04 +08:00
|
|
|
assert(hpdata_in_psset_get(ps));
|
2020-12-06 07:58:31 +08:00
|
|
|
hpdata_updating_set(ps, false);
|
2020-12-07 01:49:26 +08:00
|
|
|
psset_stats_insert(psset, ps);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The update begin should have removed ps from whatever alloc container
|
|
|
|
* it was in.
|
|
|
|
*/
|
|
|
|
assert(!hpdata_in_psset_alloc_container_get(ps));
|
|
|
|
if (hpdata_alloc_allowed_get(ps)) {
|
|
|
|
psset_alloc_container_insert(psset, ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hpdata_purge_allowed_get(ps)
|
|
|
|
&& !hpdata_in_psset_purge_container_get(ps)) {
|
|
|
|
hpdata_in_psset_purge_container_set(ps, true);
|
|
|
|
hpdata_purge_list_append(&psset->to_purge, ps);
|
|
|
|
} else if (!hpdata_purge_allowed_get(ps)
|
|
|
|
&& hpdata_in_psset_purge_container_get(ps)) {
|
|
|
|
hpdata_in_psset_purge_container_set(ps, false);
|
|
|
|
hpdata_purge_list_remove(&psset->to_purge, ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hpdata_hugify_allowed_get(ps)
|
|
|
|
&& !hpdata_in_psset_hugify_container_get(ps)) {
|
|
|
|
hpdata_in_psset_hugify_container_set(ps, true);
|
|
|
|
hpdata_hugify_list_append(&psset->to_hugify, ps);
|
|
|
|
} else if (!hpdata_hugify_allowed_get(ps)
|
|
|
|
&& hpdata_in_psset_hugify_container_get(ps)) {
|
|
|
|
hpdata_in_psset_hugify_container_set(ps, false);
|
|
|
|
hpdata_hugify_list_remove(&psset->to_hugify, ps);
|
|
|
|
}
|
2020-12-07 05:48:46 +08:00
|
|
|
hpdata_assert_consistent(ps);
|
2020-11-10 09:24:31 +08:00
|
|
|
}
|
|
|
|
|
2020-12-01 05:28:54 +08:00
|
|
|
hpdata_t *
|
2020-12-06 07:58:31 +08:00
|
|
|
psset_pick_alloc(psset_t *psset, size_t size) {
|
2020-12-06 09:42:04 +08:00
|
|
|
assert((size & PAGE_MASK) == 0);
|
|
|
|
assert(size <= HUGEPAGE);
|
|
|
|
|
2020-09-19 07:36:40 +08:00
|
|
|
pszind_t min_pind = sz_psz2ind(sz_psz_quantize_ceil(size));
|
2021-02-09 03:04:46 +08:00
|
|
|
pszind_t pind = (pszind_t)fb_ffs(psset->bitmap, PSSET_NPSIZES,
|
2020-09-19 07:36:40 +08:00
|
|
|
(size_t)min_pind);
|
|
|
|
if (pind == PSSET_NPSIZES) {
|
2020-12-07 01:49:26 +08:00
|
|
|
return hpdata_empty_list_first(&psset->empty);
|
2020-07-11 08:40:13 +08:00
|
|
|
}
|
2020-11-18 08:32:45 +08:00
|
|
|
hpdata_t *ps = hpdata_age_heap_first(&psset->pageslabs[pind]);
|
2020-09-19 07:36:40 +08:00
|
|
|
if (ps == NULL) {
|
2020-07-11 08:40:13 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2020-09-03 03:59:10 +08:00
|
|
|
|
2020-11-19 06:52:19 +08:00
|
|
|
hpdata_assert_consistent(ps);
|
2020-12-01 05:28:54 +08:00
|
|
|
|
2020-09-19 07:36:40 +08:00
|
|
|
return ps;
|
2020-07-11 08:40:13 +08:00
|
|
|
}
|
2020-12-06 09:42:04 +08:00
|
|
|
|
2020-12-07 01:49:26 +08:00
|
|
|
hpdata_t *
|
|
|
|
psset_pick_purge(psset_t *psset) {
|
|
|
|
return hpdata_purge_list_first(&psset->to_purge);
|
|
|
|
}
|
|
|
|
|
|
|
|
hpdata_t *
|
|
|
|
psset_pick_hugify(psset_t *psset) {
|
|
|
|
return hpdata_hugify_list_first(&psset->to_hugify);
|
|
|
|
}
|
|
|
|
|
2020-12-06 09:42:04 +08:00
|
|
|
void
|
|
|
|
psset_insert(psset_t *psset, hpdata_t *ps) {
|
|
|
|
hpdata_in_psset_set(ps, true);
|
2020-12-07 01:49:26 +08:00
|
|
|
|
|
|
|
psset_stats_insert(psset, ps);
|
|
|
|
if (hpdata_alloc_allowed_get(ps)) {
|
|
|
|
psset_alloc_container_insert(psset, ps);
|
|
|
|
}
|
|
|
|
if (hpdata_purge_allowed_get(ps)) {
|
|
|
|
hpdata_in_psset_purge_container_set(ps, true);
|
|
|
|
hpdata_purge_list_append(&psset->to_purge, ps);
|
|
|
|
}
|
|
|
|
if (hpdata_hugify_allowed_get(ps)) {
|
|
|
|
hpdata_in_psset_hugify_container_set(ps, true);
|
|
|
|
hpdata_hugify_list_append(&psset->to_hugify, ps);
|
|
|
|
}
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
psset_remove(psset_t *psset, hpdata_t *ps) {
|
|
|
|
hpdata_in_psset_set(ps, false);
|
2020-12-07 01:49:26 +08:00
|
|
|
|
|
|
|
psset_stats_remove(psset, ps);
|
|
|
|
if (hpdata_in_psset_alloc_container_get(ps)) {
|
|
|
|
psset_alloc_container_remove(psset, ps);
|
|
|
|
}
|
|
|
|
if (hpdata_in_psset_purge_container_get(ps)) {
|
|
|
|
hpdata_in_psset_purge_container_set(ps, false);
|
|
|
|
hpdata_purge_list_remove(&psset->to_purge, ps);
|
|
|
|
}
|
|
|
|
if (hpdata_in_psset_purge_container_get(ps)) {
|
|
|
|
hpdata_in_psset_purge_container_set(ps, false);
|
|
|
|
hpdata_purge_list_remove(&psset->to_purge, ps);
|
|
|
|
}
|
2020-12-06 09:42:04 +08:00
|
|
|
}
|