mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-26 19:48:01 +02:00
Added TCMalloc and JEMalloc projects
This commit is contained in:
parent
53376f3861
commit
652142e189
242 changed files with 67746 additions and 6 deletions
412
BeefRT/JEMalloc/include/jemalloc/internal/ehooks.h
Normal file
412
BeefRT/JEMalloc/include/jemalloc/internal/ehooks.h
Normal file
|
@ -0,0 +1,412 @@
|
|||
#ifndef JEMALLOC_INTERNAL_EHOOKS_H
|
||||
#define JEMALLOC_INTERNAL_EHOOKS_H
|
||||
|
||||
#include "jemalloc/internal/atomic.h"
|
||||
#include "jemalloc/internal/extent_mmap.h"
|
||||
|
||||
/*
|
||||
* This module is the internal interface to the extent hooks (both
|
||||
* user-specified and external). Eventually, this will give us the flexibility
|
||||
* to use multiple different versions of user-visible extent-hook APIs under a
|
||||
* single user interface.
|
||||
*
|
||||
* Current API expansions (not available to anyone but the default hooks yet):
|
||||
* - Head state tracking. Hooks can decide whether or not to merge two
|
||||
* extents based on whether or not one of them is the head (i.e. was
|
||||
* allocated on its own). The later extent loses its "head" status.
|
||||
*/
|
||||
|
||||
extern const extent_hooks_t ehooks_default_extent_hooks;
|
||||
|
||||
typedef struct ehooks_s ehooks_t;
|
||||
struct ehooks_s {
|
||||
/*
|
||||
* The user-visible id that goes with the ehooks (i.e. that of the base
|
||||
* they're a part of, the associated arena's index within the arenas
|
||||
* array).
|
||||
*/
|
||||
unsigned ind;
|
||||
/* Logically an extent_hooks_t *. */
|
||||
atomic_p_t ptr;
|
||||
};
|
||||
|
||||
extern const extent_hooks_t ehooks_default_extent_hooks;
|
||||
|
||||
/*
|
||||
* These are not really part of the public API. Each hook has a fast-path for
|
||||
* the default-hooks case that can avoid various small inefficiencies:
|
||||
* - Forgetting tsd and then calling tsd_get within the hook.
|
||||
* - Getting more state than necessary out of the extent_t.
|
||||
* - Doing arena_ind -> arena -> arena_ind lookups.
|
||||
* By making the calls to these functions visible to the compiler, it can move
|
||||
* those extra bits of computation down below the fast-paths where they get ignored.
|
||||
*/
|
||||
void *ehooks_default_alloc_impl(tsdn_t *tsdn, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit, unsigned arena_ind);
|
||||
bool ehooks_default_dalloc_impl(void *addr, size_t size);
|
||||
void ehooks_default_destroy_impl(void *addr, size_t size);
|
||||
bool ehooks_default_commit_impl(void *addr, size_t offset, size_t length);
|
||||
bool ehooks_default_decommit_impl(void *addr, size_t offset, size_t length);
|
||||
#ifdef PAGES_CAN_PURGE_LAZY
|
||||
bool ehooks_default_purge_lazy_impl(void *addr, size_t offset, size_t length);
|
||||
#endif
|
||||
#ifdef PAGES_CAN_PURGE_FORCED
|
||||
bool ehooks_default_purge_forced_impl(void *addr, size_t offset, size_t length);
|
||||
#endif
|
||||
bool ehooks_default_split_impl();
|
||||
/*
|
||||
* Merge is the only default extent hook we declare -- see the comment in
|
||||
* ehooks_merge.
|
||||
*/
|
||||
bool ehooks_default_merge(extent_hooks_t *extent_hooks, void *addr_a,
|
||||
size_t size_a, void *addr_b, size_t size_b, bool committed,
|
||||
unsigned arena_ind);
|
||||
bool ehooks_default_merge_impl(tsdn_t *tsdn, void *addr_a, void *addr_b);
|
||||
void ehooks_default_zero_impl(void *addr, size_t size);
|
||||
void ehooks_default_guard_impl(void *guard1, void *guard2);
|
||||
void ehooks_default_unguard_impl(void *guard1, void *guard2);
|
||||
|
||||
/*
|
||||
* We don't officially support reentrancy from wtihin the extent hooks. But
|
||||
* various people who sit within throwing distance of the jemalloc team want
|
||||
* that functionality in certain limited cases. The default reentrancy guards
|
||||
* assert that we're not reentrant from a0 (since it's the bootstrap arena,
|
||||
* where reentrant allocations would be redirected), which we would incorrectly
|
||||
* trigger in cases where a0 has extent hooks (those hooks themselves can't be
|
||||
* reentrant, then, but there are reasonable uses for such functionality, like
|
||||
* putting internal metadata on hugepages). Therefore, we use the raw
|
||||
* reentrancy guards.
|
||||
*
|
||||
* Eventually, we need to think more carefully about whether and where we
|
||||
* support allocating from within extent hooks (and what that means for things
|
||||
* like profiling, stats collection, etc.), and document what the guarantee is.
|
||||
*/
|
||||
static inline void
|
||||
ehooks_pre_reentrancy(tsdn_t *tsdn) {
|
||||
tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
|
||||
tsd_pre_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_post_reentrancy(tsdn_t *tsdn) {
|
||||
tsd_t *tsd = tsdn_null(tsdn) ? tsd_fetch() : tsdn_tsd(tsdn);
|
||||
tsd_post_reentrancy_raw(tsd);
|
||||
}
|
||||
|
||||
/* Beginning of the public API. */
|
||||
void ehooks_init(ehooks_t *ehooks, extent_hooks_t *extent_hooks, unsigned ind);
|
||||
|
||||
static inline unsigned
|
||||
ehooks_ind_get(const ehooks_t *ehooks) {
|
||||
return ehooks->ind;
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_set_extent_hooks_ptr(ehooks_t *ehooks, extent_hooks_t *extent_hooks) {
|
||||
atomic_store_p(&ehooks->ptr, extent_hooks, ATOMIC_RELEASE);
|
||||
}
|
||||
|
||||
static inline extent_hooks_t *
|
||||
ehooks_get_extent_hooks_ptr(ehooks_t *ehooks) {
|
||||
return (extent_hooks_t *)atomic_load_p(&ehooks->ptr, ATOMIC_ACQUIRE);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_are_default(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks) ==
|
||||
&ehooks_default_extent_hooks;
|
||||
}
|
||||
|
||||
/*
|
||||
* In some cases, a caller needs to allocate resources before attempting to call
|
||||
* a hook. If that hook is doomed to fail, this is wasteful. We therefore
|
||||
* include some checks for such cases.
|
||||
*/
|
||||
static inline bool
|
||||
ehooks_dalloc_will_fail(ehooks_t *ehooks) {
|
||||
if (ehooks_are_default(ehooks)) {
|
||||
return opt_retain;
|
||||
} else {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->dalloc == NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_split_will_fail(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->split == NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_merge_will_fail(ehooks_t *ehooks) {
|
||||
return ehooks_get_extent_hooks_ptr(ehooks)->merge == NULL;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_guard_will_fail(ehooks_t *ehooks) {
|
||||
/*
|
||||
* Before the guard hooks are officially introduced, limit the use to
|
||||
* the default hooks only.
|
||||
*/
|
||||
return !ehooks_are_default(ehooks);
|
||||
}
|
||||
|
||||
/*
|
||||
* Some hooks are required to return zeroed memory in certain situations. In
|
||||
* debug mode, we do some heuristic checks that they did what they were supposed
|
||||
* to.
|
||||
*
|
||||
* This isn't really ehooks-specific (i.e. anyone can check for zeroed memory).
|
||||
* But incorrect zero information indicates an ehook bug.
|
||||
*/
|
||||
static inline void
|
||||
ehooks_debug_zero_check(void *addr, size_t size) {
|
||||
assert(((uintptr_t)addr & PAGE_MASK) == 0);
|
||||
assert((size & PAGE_MASK) == 0);
|
||||
assert(size > 0);
|
||||
if (config_debug) {
|
||||
/* Check the whole first page. */
|
||||
size_t *p = (size_t *)addr;
|
||||
for (size_t i = 0; i < PAGE / sizeof(size_t); i++) {
|
||||
assert(p[i] == 0);
|
||||
}
|
||||
/*
|
||||
* And 4 spots within. There's a tradeoff here; the larger
|
||||
* this number, the more likely it is that we'll catch a bug
|
||||
* where ehooks return a sparsely non-zero range. But
|
||||
* increasing the number of checks also increases the number of
|
||||
* page faults in debug mode. FreeBSD does much of their
|
||||
* day-to-day development work in debug mode, so we don't want
|
||||
* even the debug builds to be too slow.
|
||||
*/
|
||||
const size_t nchecks = 4;
|
||||
assert(PAGE >= sizeof(size_t) * nchecks);
|
||||
for (size_t i = 0; i < nchecks; ++i) {
|
||||
assert(p[i * (size / sizeof(size_t) / nchecks)] == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static inline void *
|
||||
ehooks_alloc(tsdn_t *tsdn, ehooks_t *ehooks, void *new_addr, size_t size,
|
||||
size_t alignment, bool *zero, bool *commit) {
|
||||
bool orig_zero = *zero;
|
||||
void *ret;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ret = ehooks_default_alloc_impl(tsdn, new_addr, size,
|
||||
alignment, zero, commit, ehooks_ind_get(ehooks));
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
ret = extent_hooks->alloc(extent_hooks, new_addr, size,
|
||||
alignment, zero, commit, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
assert(new_addr == NULL || ret == NULL || new_addr == ret);
|
||||
assert(!orig_zero || *zero);
|
||||
if (*zero && ret != NULL) {
|
||||
ehooks_debug_zero_check(ret, size);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_dalloc(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_dalloc_impl(addr, size);
|
||||
} else if (extent_hooks->dalloc == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->dalloc(extent_hooks, addr, size,
|
||||
committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_destroy(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_destroy_impl(addr, size);
|
||||
} else if (extent_hooks->destroy == NULL) {
|
||||
/* Do nothing. */
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
extent_hooks->destroy(extent_hooks, addr, size, committed,
|
||||
ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_commit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
bool err;
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
err = ehooks_default_commit_impl(addr, offset, length);
|
||||
} else if (extent_hooks->commit == NULL) {
|
||||
err = true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
err = extent_hooks->commit(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
}
|
||||
if (!err) {
|
||||
ehooks_debug_zero_check(addr, size);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_decommit(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_decommit_impl(addr, offset, length);
|
||||
} else if (extent_hooks->decommit == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->decommit(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_purge_lazy(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
#ifdef PAGES_CAN_PURGE_LAZY
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_purge_lazy_impl(addr, offset, length);
|
||||
}
|
||||
#endif
|
||||
if (extent_hooks->purge_lazy == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->purge_lazy(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_purge_forced(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t offset, size_t length) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
/*
|
||||
* It would be correct to have a ehooks_debug_zero_check call at the end
|
||||
* of this function; purge_forced is required to zero. But checking
|
||||
* would touch the page in question, which may have performance
|
||||
* consequences (imagine the hooks are using hugepages, with a global
|
||||
* zero page off). Even in debug mode, it's usually a good idea to
|
||||
* avoid cases that can dramatically increase memory consumption.
|
||||
*/
|
||||
#ifdef PAGES_CAN_PURGE_FORCED
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_purge_forced_impl(addr, offset, length);
|
||||
}
|
||||
#endif
|
||||
if (extent_hooks->purge_forced == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->purge_forced(extent_hooks, addr, size,
|
||||
offset, length, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_split(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size,
|
||||
size_t size_a, size_t size_b, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (ehooks_are_default(ehooks)) {
|
||||
return ehooks_default_split_impl();
|
||||
} else if (extent_hooks->split == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->split(extent_hooks, addr, size, size_a,
|
||||
size_b, committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_merge(tsdn_t *tsdn, ehooks_t *ehooks, void *addr_a, size_t size_a,
|
||||
void *addr_b, size_t size_b, bool committed) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
return ehooks_default_merge_impl(tsdn, addr_a, addr_b);
|
||||
} else if (extent_hooks->merge == NULL) {
|
||||
return true;
|
||||
} else {
|
||||
ehooks_pre_reentrancy(tsdn);
|
||||
bool err = extent_hooks->merge(extent_hooks, addr_a, size_a,
|
||||
addr_b, size_b, committed, ehooks_ind_get(ehooks));
|
||||
ehooks_post_reentrancy(tsdn);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
ehooks_zero(tsdn_t *tsdn, ehooks_t *ehooks, void *addr, size_t size) {
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_zero_impl(addr, size);
|
||||
} else {
|
||||
/*
|
||||
* It would be correct to try using the user-provided purge
|
||||
* hooks (since they are required to have zeroed the extent if
|
||||
* they indicate success), but we don't necessarily know their
|
||||
* cost. We'll be conservative and use memset.
|
||||
*/
|
||||
memset(addr, 0, size);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_guard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
|
||||
bool err;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_guard_impl(guard1, guard2);
|
||||
err = false;
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ehooks_unguard(tsdn_t *tsdn, ehooks_t *ehooks, void *guard1, void *guard2) {
|
||||
bool err;
|
||||
extent_hooks_t *extent_hooks = ehooks_get_extent_hooks_ptr(ehooks);
|
||||
|
||||
if (extent_hooks == &ehooks_default_extent_hooks) {
|
||||
ehooks_default_unguard_impl(guard1, guard2);
|
||||
err = false;
|
||||
} else {
|
||||
err = true;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif /* JEMALLOC_INTERNAL_EHOOKS_H */
|
Loading…
Add table
Add a link
Reference in a new issue