Permalink
Browse files

not assuming the number of jemalloc arenas is small

Summary:
Previously, the implementation of ManagedArena assumes the number of jemalloc arena is very small (it is 1 by default), so an array indexed by arena id is used to obtain a pointer to the arena given an id.
However, it crashes when MALLOC_CONF sets it large and when ManagedArena is used.
This fixes it by using an array of pair<id, pointer> to store the mapping between arena id and pointers to the arena.

Reviewed By: interwq

Differential Revision: D7206066

fbshipit-source-id: abbfcb573f2bc2581f6b7a8b6927c208a51fb17f
  • Loading branch information...
binliu19 authored and hhvm-bot committed Mar 13, 2018
1 parent bbfed19 commit f91e26405e50ac9b36eea3682629b9e4e201b53c
Showing with 43 additions and 54 deletions.
  1. +19 −3 hphp/util/alloc.h
  2. +1 −3 hphp/util/extent-hooks.cpp
  3. +0 −14 hphp/util/extent-hooks.h
  4. +22 −11 hphp/util/managed-arena.cpp
  5. +1 −23 hphp/util/managed-arena.h
@@ -17,10 +17,11 @@
#ifndef incl_HPHP_UTIL_ALLOC_H_
#define incl_HPHP_UTIL_ALLOC_H_
#include <stdint.h>
#include <cassert>
#include <array>
#include <atomic>
#include <stdint.h>
#include <folly/CPortability.h>
#include <folly/Portability.h>
#include <folly/portability/PThread.h>
@@ -157,9 +158,24 @@ inline int low_dallocx_flags() {
#ifdef USE_JEMALLOC_EXTENT_HOOKS
#ifndef MAX_MANAGED_ARENA_COUNT
#define MAX_MANAGED_ARENA_COUNT 8
#define MAX_MANAGED_ARENA_COUNT 4
#endif
static_assert(MAX_MANAGED_ARENA_COUNT >= 1, "");
// All ManagedArena's represented as an array of pair<id, pointer>. Each
// pointer can be casted to the underlying ExtentAllocator/Arena. We use this
// to access the state of ExtentAllocators in extent hooks. An id of zero
// indicates an empty entry.
using ArenaArray = std::array<std::pair<unsigned, void*>,
MAX_MANAGED_ARENA_COUNT>;
extern ArenaArray g_arenas;
template<typename T> inline T* GetByArenaId(unsigned id) {
for (auto i : g_arenas) {
if (i.first == id) {
return static_cast<T*>(i.second);
}
}
return nullptr;
}
extern unsigned low_huge1g_arena;
extern unsigned low_huge1g_arena_real;
@@ -18,8 +18,6 @@
#ifdef USE_JEMALLOC_EXTENT_HOOKS
#include <folly/portability/SysMman.h>
namespace HPHP { namespace alloc {
// trivial jemalloc extent hooks
@@ -99,7 +97,7 @@ extent_alloc(extent_hooks_t* extent_hooks, void* addr,
assert(folly::isPowTwo(alignment));
const uintptr_t mask = ~(alignment - 1);
BumpExtentAllocator* extAlloc = GetForArena(arena_ind);
BumpExtentAllocator* extAlloc = GetByArenaId<BumpExtentAllocator>(arena_ind);
do {
size_t oldSize = extAlloc->m_size.load(std::memory_order_relaxed);
uintptr_t newFrontier = (extAlloc->m_base - oldSize - size) & mask;
@@ -39,16 +39,6 @@
namespace HPHP { namespace alloc {
extern void* g_arenas[MAX_MANAGED_ARENA_COUNT];
template<typename ExtentAllocator>
inline static ExtentAllocator* GetByArenaId(unsigned id) {
assert(id < MAX_MANAGED_ARENA_COUNT);
void* r = g_arenas[id];
assert(r);
return reinterpret_cast<ExtentAllocator*>(r);
}
/**
* Extent hooks that do bump mapping for ManagedArena.
*/
@@ -65,10 +55,6 @@ struct BumpExtentAllocator : private BumpAllocState {
size_t size, size_t alignment, bool* zero,
bool* commit, unsigned arena_ind);
static BumpExtentAllocator* GetForArena(unsigned arena_ind) {
return GetByArenaId<BumpExtentAllocator>(arena_ind);
}
static constexpr bool IsPurgingSupported() { return false; }
// The hook passed to the underlying arena upon creation.
@@ -18,12 +18,12 @@
#ifdef USE_JEMALLOC_EXTENT_HOOKS
#include <cinttypes>
#include <stdexcept>
namespace HPHP {
namespace HPHP { namespace alloc {
ArenaArray g_arenas;
namespace alloc {
void* g_arenas[MAX_MANAGED_ARENA_COUNT];
static_assert(alignof(HighArena) <= 64, "");
static_assert(alignof(LowHugeArena) <= 64, "");
alignas(64) uint8_t g_highArena[sizeof(HighArena)];
@@ -88,13 +88,18 @@ size_t ManagedArena<ExtentAllocator>::unusedSize() {
template<typename ExtentAllocator>
void ManagedArena<ExtentAllocator>::init() {
if (!g_mib_initialized) {
initializeMibs();
}
if (m_arenaId != 0) {
// Should call init() multiple times for the same instance.
not_reached();
return;
}
size_t idSize = sizeof(m_arenaId);
if (mallctl("arenas.create", &m_arenaId, &idSize, nullptr, 0)) {
throw std::runtime_error{"arenas.create"};
}
if (m_arenaId >= MAX_MANAGED_ARENA_COUNT) {
throw std::out_of_range{"too many arenas, check MAX_HUGE_ARENA_COUNT"};
}
char command[32];
std::snprintf(command, sizeof(command), "arena.%d.extent_hooks", m_arenaId);
extent_hooks_t* hooks_ptr = &ExtentAllocator::s_hooks;
@@ -115,11 +120,17 @@ void ManagedArena<ExtentAllocator>::init() {
throw std::runtime_error{command};
}
}
assert(m_arenaId < MAX_MANAGED_ARENA_COUNT);
g_arenas[m_arenaId] = this;
if (!g_mib_initialized) {
initializeMibs();
assert(GetByArenaId<ManagedArena>(m_arenaId) == nullptr);
for (auto& i : g_arenas) {
if (!i.first) {
i.first = m_arenaId;
i.second = this;
return;
}
}
// Should never reached here, as there should be spare entries in g_arenas.
throw std::out_of_range{
"too many ManagedArena's, check MAX_HUGE_ARENA_COUNT"};
}
template void ManagedArena<BumpExtentAllocator>::init();
@@ -37,21 +37,7 @@ namespace HPHP { namespace alloc {
* For various purposes, we want to control the properties of the underlying
* memory in a particular arena, such as address range, physical placement on
* NUMA nodes, or huge pages. The extent alloc hook comes in handy for the
* purpose.
*/
template <typename ExtentAllocator> struct ManagedArena;
// List of all ManagedArenas (of different types). Each can be casted to the
// underlying ExtentAllocator. We need this to access the state of
// ExtentAllocators in extent hooks.
extern void* g_arenas[MAX_MANAGED_ARENA_COUNT];
////////////////////////////////////////////////////////////////////////////////
/*
* jemalloc arena with customized extent hooks. The extent_hook_t is
* described in the ExtentAllocator policy class.
* purpose, and is wrapped in the ExtentAllocator policy class.
*/
template <typename ExtentAllocator>
struct ManagedArena : public ExtentAllocator {
@@ -73,21 +59,13 @@ struct ManagedArena : public ExtentAllocator {
public:
inline unsigned id() const {
assert(m_arenaId < MAX_MANAGED_ARENA_COUNT);
return m_arenaId;
}
// For stats reporting
size_t unusedSize();
std::string reportStats();
static ManagedArena* GetArenaById(unsigned id) {
assert(id < MAX_MANAGED_ARENA_COUNT);
void* r = g_arenas[id];
assert(r);
return reinterpret_cast<ManagedArena*>(r);
}
template<typename... Args>
static ManagedArena* CreateAt(void* addr, Args&&... args) {
return new (addr) ManagedArena(std::forward<Args>(args)...);

0 comments on commit f91e264

Please sign in to comment.