// The Construct // // Copyright (C) The Construct Developers, Authors & Contributors // Copyright (C) 2016-2020 Jason Volk // // Permission to use, copy, modify, and/or distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice is present in all copies. The // full license for this software is available in the LICENSE file. #include ("arenas.create"); char extent_hooks_keybuf[32]; const string_view cache_arena_hooks_key{fmt::sprintf { extent_hooks_keybuf, "arena.%u.extent_hooks", cache_arena }}; cache_arena_hooks.alloc = cache_arena_handle_alloc; cache_arena_hooks.dalloc = cache_arena_handle_dalloc; cache_arena_hooks.destroy = cache_arena_handle_destroy; cache_arena_hooks.commit = cache_arena_handle_commit; cache_arena_hooks.decommit = cache_arena_handle_decommit; cache_arena_hooks.purge_lazy = cache_arena_handle_purge_lazy; cache_arena_hooks.purge_forced = cache_arena_handle_purge_forced; cache_arena_hooks.split = cache_arena_handle_split; cache_arena_hooks.merge = cache_arena_handle_merge; ircd::allocator::set(cache_arena_hooks_key, &cache_arena_hooks, their_cache_arena_hooks); assert(their_cache_arena_hooks); #endif } void ircd::db::database::allocator::fini() noexcept { #ifdef IRCD_DB_USE_JEMALLOC if(likely(cache_arena != 0)) { char keybuf[64]; ircd::allocator::get(string_view(fmt::sprintf { keybuf, "arena.%u.reset", cache_arena })); ircd::allocator::get(string_view(fmt::sprintf { keybuf, "arena.%u.destroy", cache_arena })); } #endif } #ifdef IRCD_DB_USE_JEMALLOC void * ircd::db::cache_arena_handle_alloc(extent_hooks_t *const hooks, void *const new_addr, size_t size, size_t alignment, bool *const zero, bool *const commit, unsigned arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); assert(zero); assert(commit); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u alloc addr:%p size:%zu align:%zu z:%b c:%b ind:%u", database::allocator::cache_arena, new_addr, size, alignment, *zero, *commit, arena_ind, }; void *const ret { their_hooks.alloc(hooks, new_addr, size, alignment, zero, commit, arena_ind) }; // This feature is only enabled when RLIMIT_MEMLOCK is unlimited. We don't // want to deal with any limit at all. #if defined(HAVE_MLOCK2) && defined(MLOCK_ONFAULT) if(database::allocator::mlock_enabled) { syscall(::mlock2, ret, size, MLOCK_ONFAULT); database::allocator::mlock_current += size; } #endif return ret; } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_dalloc(extent_hooks_t *hooks, void *const ptr, size_t size, bool committed, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u dalloc addr:%p size:%zu align:%zu z:%b c:%b ind:%u", database::allocator::cache_arena, ptr, size, committed, arena_ind, }; const bool ret { their_hooks.dalloc(hooks, ptr, size, committed, arena_ind) }; #if defined(HAVE_MLOCK2) if(database::allocator::mlock_current && !ret) { syscall(::munlock, ptr, size); assert(database::allocator::mlock_current >= size); database::allocator::mlock_current -= size; } #endif return ret; } #endif #ifdef IRCD_DB_USE_JEMALLOC void ircd::db::cache_arena_handle_destroy(extent_hooks_t *hooks, void *const ptr, size_t size, bool committed, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u destroy addr:%p size:%zu align:%zu z:%b c:%b ind:%u", database::allocator::cache_arena, ptr, size, committed, arena_ind, }; #if defined(HAVE_MLOCK2) if(database::allocator::mlock_current) { syscall(::munlock, ptr, size); assert(database::allocator::mlock_current >= size); database::allocator::mlock_current -= size; } #endif return their_hooks.destroy(hooks, ptr, size, committed, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_commit(extent_hooks_t *const hooks, void *const ptr, size_t size, size_t offset, size_t length, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u commit addr:%p size:%zu offset:%zu length:%zu ind:%u", database::allocator::cache_arena, ptr, size, offset, length, arena_ind, }; return their_hooks.commit(hooks, ptr, size, offset, length, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_decommit(extent_hooks_t *const hooks, void *const ptr, size_t size, size_t offset, size_t length, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u decommit addr:%p size:%zu offset:%zu length:%zu ind:%u", database::allocator::cache_arena, ptr, size, offset, length, arena_ind, }; return their_hooks.decommit(hooks, ptr, size, offset, length, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_purge_lazy(extent_hooks_t *const hooks, void *const ptr, size_t size, size_t offset, size_t length, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u purge lazy addr:%p size:%zu offset:%zu length:%zu ind:%u", database::allocator::cache_arena, ptr, size, offset, length, arena_ind, }; return their_hooks.purge_lazy(hooks, ptr, size, offset, length, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_purge_forced(extent_hooks_t *const hooks, void *const ptr, size_t size, size_t offset, size_t length, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u purge forced addr:%p size:%zu offset:%zu length:%zu ind:%u", database::allocator::cache_arena, ptr, size, offset, length, arena_ind, }; return their_hooks.purge_forced(hooks, ptr, size, offset, length, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_split(extent_hooks_t *const hooks, void *const ptr, size_t size, size_t size_a, size_t size_b, bool committed, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u split addr:%p size:%zu size_a:%zu size_b:%zu committed:%b ind:%u", database::allocator::cache_arena, ptr, size, size_a, size_b, committed, arena_ind, }; return their_hooks.split(hooks, ptr, size, size_a, size_b, committed, arena_ind); } #endif #ifdef IRCD_DB_USE_JEMALLOC bool ircd::db::cache_arena_handle_merge(extent_hooks_t *const hooks, void *const addr_a, size_t size_a, void *const addr_b, size_t size_b, bool committed, uint arena_ind) noexcept { assert(their_cache_arena_hooks); const auto &their_hooks(*their_cache_arena_hooks); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "cache arena:%u merge a[addr:%p size:%zu] b[addr:%p size:%zu] committed:%b ind:%u", database::allocator::cache_arena, addr_a, size_a, addr_b, size_b, committed, arena_ind, }; return their_hooks.merge(hooks, addr_a, size_a, addr_b, size_b, committed, arena_ind); } #endif // // allocator::allocator // ircd::db::database::allocator::allocator(database *const &d, database::column *const &c, const unsigned &arena, const size_t &alignment) :d{d} ,c{c} ,alignment{alignment} ,arena{arena} ,arena_flags { 0 #ifdef IRCD_DB_USE_JEMALLOC | MALLOCX_ARENA(this->arena) | MALLOCX_ALIGN(this->alignment) | MALLOCX_TCACHE_NONE #endif } { assert(math::is_pow2(alignment)); } ircd::db::database::allocator::~allocator() noexcept { } size_t ircd::db::database::allocator::UsableSize(void *const ptr, size_t size) const noexcept { const size_t ret { #ifdef IRCD_DB_USE_JEMALLOC sallocx(ptr, arena_flags) #else size % alignment != 0? size + (alignment - (size % alignment)): size #endif }; #ifndef IRCD_DB_USE_JEMALLOC assert(ret % alignment == 0); assert(alignment % sizeof(void *) == 0); #endif return ret; } void ircd::db::database::allocator::Deallocate(void *const ptr) noexcept { #ifdef IRCD_DB_USE_JEMALLOC dallocx(ptr, arena_flags); #else std::free(ptr); #endif } void * ircd::db::database::allocator::Allocate(size_t size) noexcept { assert(size > 0UL); assert(size < 256_GiB); const auto ptr { #ifdef IRCD_DB_USE_JEMALLOC mallocx(size, arena_flags) #else ircd::allocator::aligned_alloc(alignment, size).release() #endif }; assert(d); if constexpr(RB_DEBUG_DB_ALLOCATOR) log::debug { log, "[%s]'%s' allocate:%zu alignment:%zu %p", db::name(*d), c? string_view(db::name(*c)): string_view{}, size, alignment, ptr, }; return ptr; } const char * ircd::db::database::allocator::Name() const noexcept { return c? db::name(*c).c_str(): d? db::name(*d).c_str(): "unaffiliated"; } #endif IRCD_DB_HAS_ALLOCATOR