2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/managed-arena.h"
19 #include "hphp/util/address-range.h"
20 #include "hphp/util/assertions.h"
22 #if USE_JEMALLOC_EXTENT_HOOKS
24 namespace HPHP::alloc
{
26 static_assert(sizeof(RangeState
) <= 64, "");
27 static_assert(alignof(RangeState
) <= 64, "");
28 using RangeStateStorage
= std::aligned_storage
<sizeof(RangeState
), 64>::type
;
30 RangeArenaStorage g_lowerArena
{};
31 RangeArenaStorage g_lowArena
{};
32 RangeArenaStorage g_highArena
{};
33 RangeArenaStorage g_coldArena
{};
34 RangeArenaStorage g_lowColdArena
{};
35 RangeStateStorage g_ranges
[5];
37 PreMappedArena
* g_arena0
; // arena 0, if we end up injecting pages
38 std::vector
<PreMappedArena
*> g_local_arenas
; // keyed by numa node id
40 NEVER_INLINE RangeState
& getRange(AddrRangeClass index
) {
41 auto result
= reinterpret_cast<RangeState
*>(g_ranges
+ index
);
43 static std::atomic_flag lock
= ATOMIC_FLAG_INIT
;
44 while (lock
.test_and_set(std::memory_order_acquire
)) {
45 // Spin while another thread initializes the ranges. We don't really reach
46 // here, because the function is called very early during process
47 // initialization when there is only one thread. Do it just for extra
48 // safety in case someone starts to abuse the code.
50 if (!result
->high()) {
51 new (&(g_ranges
[AddrRangeClass::VeryLow
]))
52 RangeState(lowArenaMinAddr(), 2ull << 30);
53 new (&(g_ranges
[AddrRangeClass::Low
]))
54 RangeState(2ull << 30, kLowArenaMaxAddr
- kLowEmergencySize
);
55 new (&(g_ranges
[AddrRangeClass::LowEmergency
]))
56 RangeState(kLowArenaMaxAddr
- kLowEmergencySize
, kLowArenaMaxAddr
);
57 new (&(g_ranges
[AddrRangeClass::Uncounted
]))
58 RangeState(kLowArenaMaxAddr
, kHighArenaMaxAddr
);
59 new (&(g_ranges
[AddrRangeClass::UncountedCold
]))
60 RangeState(kHighArenaMaxAddr
, kUncountedMaxAddr
);
62 lock
.clear(std::memory_order_release
);
63 assertx(result
->high());
68 //////////////////////////////////////////////////////////////////////
70 template<typename ExtentAllocator
>
71 std::string ManagedArena
<ExtentAllocator
>::reportStats() {
73 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
74 std::snprintf(buffer
, sizeof(buffer
),
75 "Arena %d: capacity %zd, max_capacity %zd, used %zd\n",
77 ExtentAllocator::allocatedSize(),
78 ExtentAllocator::maxCapacity(),
79 s_pageSize
* mallctl_pactive(id()));
80 return std::string
{buffer
};
83 template<typename ExtentAllocator
>
84 size_t ManagedArena
<ExtentAllocator
>::unusedSize() {
85 auto const active
= s_pageSize
* mallctl_pactive(id());
86 return ExtentAllocator::allocatedSize() - active
;
89 template<typename ExtentAllocator
>
90 void ManagedArena
<ExtentAllocator
>::create() {
91 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
92 assertx(m_arenaId
== kInvalidArena
);
93 size_t idSize
= sizeof(m_arenaId
);
94 if (mallctl("arenas.create", &m_arenaId
, &idSize
, nullptr, 0)) {
95 throw std::runtime_error
{"arenas.create"};
98 ssize_t decay_ms
= Traits::get_decay_ms();
99 std::snprintf(command
, sizeof(command
),
100 "arena.%d.dirty_decay_ms", m_arenaId
);
101 if (mallctl(command
, nullptr, nullptr, &decay_ms
, sizeof(decay_ms
))) {
102 throw std::runtime_error
{command
};
107 template<typename ExtentAllocator
>
108 void ManagedArena
<ExtentAllocator
>::updateHook() {
109 using Traits
= extent_allocator_traits
<ExtentAllocator
>;
110 if (auto hooks_ptr
= Traits::get_hooks()) {
111 // We need to do `GetByArenaId()` in custom extent hooks, so register the
112 // arena in the global list. It is important that we do this before actually
113 // updating the hooks.
114 assertx(GetByArenaId
<ManagedArena
>(m_arenaId
) == nullptr);
115 bool registered
= false;
116 for (auto& i
: g_arenas
) {
125 throw std::out_of_range
{
126 "too many ManagedArena's, check MAX_MANAGED_ARENA_COUNT"};
130 std::snprintf(command
, sizeof(command
), "arena.%d.extent_hooks", m_arenaId
);
131 auto fallback
= Traits::get_fallback(this);
132 size_t oldSize
= sizeof(extent_hooks_t
*);
133 if (mallctl(command
, fallback
, (fallback
? &oldSize
: nullptr),
134 &hooks_ptr
, sizeof(hooks_ptr
))) {
135 throw std::runtime_error
{command
};
137 #if (JEMALLOC_VERSION_MAJOR > 5) || \
138 ((JEMALLOC_VERSION_MAJOR == 5) && (JEMALLOC_VERSION_MINOR >= 1))
139 // Avoid asking excessive memory through the hook, in order to make better
140 // use of preallocated pages.
141 std::snprintf(command
, sizeof(command
),
142 "arena.%d.retain_grow_limit", m_arenaId
);
143 size_t limit
= 16ull << 20;
144 if (mallctl(command
, nullptr, nullptr, &limit
, sizeof(limit
))) {
145 throw std::runtime_error
{command
};
151 template void ManagedArena
<MultiRangeExtentAllocator
>::create();
152 template void ManagedArena
<MultiRangeExtentAllocator
>::updateHook();
153 template size_t ManagedArena
<MultiRangeExtentAllocator
>::unusedSize();
154 template std::string ManagedArena
<MultiRangeExtentAllocator
>::reportStats();
156 template void ManagedArena
<DefaultExtentAllocator
>::create();
157 template void ManagedArena
<DefaultExtentAllocator
>::updateHook();
158 template void ManagedArena
<RangeFallbackExtentAllocator
>::create();
159 template void ManagedArena
<RangeFallbackExtentAllocator
>::updateHook();