The Pedigree Project 0.1
MemoryPressureManager.cc
1/*
2 * Copyright (c) 2008-2014, Pedigree Developers
3 *
4 * Please see the CONTRIB file in the root of the source tree for a full
5 * list of contributors.
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/Metrics.h"
23#include "pedigree/kernel/process/MemoryPressureManager.h"
24#include "pedigree/kernel/process/TerminationDeferral.h"
25#if THREADS
26#include "pedigree/kernel/process/Scheduler.h"
27#include "pedigree/kernel/process/Thread.h"
28#endif
29#include "pedigree/kernel/processor/Processor.h"
30#include "pedigree/kernel/processor/ProcessorInformation.h"
31#include "pedigree/kernel/utilities/assert.h"
32#include "pedigree/kernel/utilities/utility.h"
33
34MemoryPressureManager MemoryPressureManager::m_Instance;
35
36namespace {
37const void* currentCallbackOwner() {
38#if THREADS
39 Thread* thread = Processor::information().getCurrentThread();
40 if (thread) {
41 return static_cast<const void*>(thread);
42 }
43#endif
44 return static_cast<const void*>(&Processor::information());
45}
46} // namespace
47
48MemoryPressureHandler::MemoryPressureHandler()
49 : m_pPrevious(nullptr),
50 m_pNext(nullptr),
51 m_Priority(0),
52 m_RegistrationSequence(0),
53 m_bRegistered(false),
54 m_bRemoving(false)
55#if THREADS
56 ,
57 m_CallbacksInFlight(0),
58 m_pCallbackOwner(nullptr),
59 m_CallbackWaiters()
60#endif
61{
62}
63
64MemoryPressureHandler::~MemoryPressureHandler() = default;
65
67#if THREADS
68 if (!Processor::information().getCurrentThread() || !Processor::getInterrupts()) {
69 // Recovery callbacks may block and may allocate through subsystems
70 // whose outer spinlock triggered this pressure pass. Running them in
71 // atomic context would turn allocation failure into a lock inversion.
72 return false;
73 }
74#endif
75
76 // A callback may own stack-backed lifetime state. Terminal requests can
77 // interrupt its work, but cannot skip retirement of the callback and
78 // global compact pins below.
79 TerminationDeferral terminationDeferral;
80 const void* owner = currentCallbackOwner();
81
82 // The compact predicate and its wakeup share a WaitQueue guard. This keeps
83 // concurrent allocators from missing the pass which should replenish
84 // memory without allocating any waiter storage.
85#if THREADS
86 while (true) {
87 auto compactGuard = m_CompactWaiters.acquire();
88 if (m_bCompacting.compareAndSwap(false, true)) {
89 __atomic_store_n(&m_pCompactOwner, owner, __ATOMIC_RELEASE);
90 break;
91 }
92
93 if (__atomic_load_n(&m_pCompactOwner, __ATOMIC_ACQUIRE) == owner) {
94 return false;
95 }
96
97 const WaitQueue::WakeReason compactWake = compactGuard.waitForCompletion(
98 WaitQueue::Channel(this), Thread::CallbackDrain, reinterpret_cast<uintptr_t>(this));
99 (void)compactWake;
100 }
101#else
102 if (!m_bCompacting.compareAndSwap(false, true)) {
103 return false;
104 }
105 __atomic_store_n(&m_pCompactOwner, owner, __ATOMIC_RELEASE);
106#endif
107
108 if (Processor::m_Initialised == 2) {
109 Metrics::increment(Metrics::MemoryPressurePass);
110 }
111
112 size_t registrationLimit = 0;
113 {
114 LockGuard<Spinlock> guard(m_Lock);
115 registrationLimit = m_NextRegistrationSequence;
116 }
117
118 bool releasedPages = false;
119 size_t priority = 0;
120 size_t registrationCursor = 0;
121 while (priority < MAX_MEMPRESSURE_PRIORITY) {
122 MemoryPressureHandler* handler = nullptr;
123 {
124 LockGuard<Spinlock> guard(m_Lock);
125 for (MemoryPressureHandler* candidate = m_Handlers[priority]; candidate;
126 candidate = candidate->m_pNext) {
127 if (candidate->m_RegistrationSequence > registrationCursor &&
128 candidate->m_RegistrationSequence <= registrationLimit) {
129 handler = candidate;
130 registrationCursor = candidate->m_RegistrationSequence;
131 break;
132 }
133 }
134
135#if THREADS
136 if (handler) {
137 auto callbackGuard = handler->m_CallbackWaiters.acquire();
138 assert(!handler->m_CallbacksInFlight);
139 handler->m_CallbacksInFlight = 1;
140 handler->m_pCallbackOwner = currentCallbackOwner();
141 }
142#endif
143 }
144
145 if (!handler) {
146 ++priority;
147 registrationCursor = 0;
148 continue;
149 }
150
151 const char* description = handler->getMemoryPressureDescription();
152 NOTICE("Compact: " << (description ? description : "<unnamed memory-pressure handler>"));
153 releasedPages = handler->compact();
154 if (releasedPages) {
155 NOTICE(" -> pages released!");
156 } else {
157 NOTICE(" -> no pages released.");
158 }
159
160#if THREADS
161 {
162 auto callbackGuard = handler->m_CallbackWaiters.acquire();
163 assert(handler->m_CallbacksInFlight == 1);
164 handler->m_CallbacksInFlight = 0;
165 handler->m_pCallbackOwner = nullptr;
166 callbackGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(handler));
167 }
168#endif
169
170 if (releasedPages) {
171 break;
172 }
173 }
174
175#if THREADS
176 {
177 auto compactGuard = m_CompactWaiters.acquire();
178 __atomic_store_n(&m_pCompactOwner, nullptr, __ATOMIC_RELEASE);
179 const bool cleared = m_bCompacting.compareAndSwap(true, false);
180 assert(cleared);
181 compactGuard.wakeAll(WaitQueue::WakeReason::Signalled, WaitQueue::Channel(this));
182 }
183#else
184 __atomic_store_n(&m_pCompactOwner, nullptr, __ATOMIC_RELEASE);
185 const bool cleared = m_bCompacting.compareAndSwap(true, false);
186 assert(cleared);
187#endif
188 if (releasedPages && Processor::m_Initialised == 2) {
189 Metrics::increment(Metrics::MemoryPressurePassSuccess);
190 }
191 return releasedPages;
192}
193
195 return m_bCompacting &&
196 __atomic_load_n(&m_pCompactOwner, __ATOMIC_ACQUIRE) == currentCallbackOwner();
197}
198
199MemoryPressureManager::MemoryPressureManager()
200 : m_Lock(false),
201 m_Handlers(),
202 m_HandlerTails(),
203 m_NextRegistrationSequence(0),
204 m_bCompacting(false),
205 m_pCompactOwner(nullptr)
206#if THREADS
207 ,
208 m_CompactWaiters()
209#endif
210{
211}
212
213MemoryPressureManager::~MemoryPressureManager() = default;
214
216 if (!pHandler) {
217 FATAL("Cannot register a null memory-pressure handler.");
218 }
219
220 if (prio >= MAX_MEMPRESSURE_PRIORITY) {
221 prio = MAX_MEMPRESSURE_PRIORITY - 1;
222 }
223
224 LockGuard<Spinlock> guard(m_Lock);
225#if THREADS
226 auto callbackGuard = pHandler->m_CallbackWaiters.acquire();
227#endif
228 if (pHandler->m_bRegistered || pHandler->m_bRemoving) {
229 FATAL("Memory-pressure handler registered more than once.");
230 }
231
232#if THREADS
233 if (pHandler->m_CallbacksInFlight) {
234 FATAL("Memory-pressure handler registered during callback removal.");
235 }
236#endif
237
238 pHandler->m_Priority = prio;
239 pHandler->m_RegistrationSequence = ++m_NextRegistrationSequence;
240 pHandler->m_pPrevious = m_HandlerTails[prio];
241 pHandler->m_pNext = nullptr;
242 pHandler->m_bRegistered = true;
243
244 if (m_HandlerTails[prio]) {
245 m_HandlerTails[prio]->m_pNext = pHandler;
246 } else {
247 m_Handlers[prio] = pHandler;
248 }
249 m_HandlerTails[prio] = pHandler;
250}
251
253 if (!pHandler) {
254 return;
255 }
256
257 bool needsDrain = false;
258 {
259 LockGuard<Spinlock> guard(m_Lock);
260
261#if THREADS
262 auto callbackGuard = pHandler->m_CallbackWaiters.acquire();
263 if (pHandler->m_CallbacksInFlight && pHandler->m_pCallbackOwner == currentCallbackOwner()) {
264 FATAL(
265 "A memory-pressure callback cannot synchronously remove "
266 "itself.");
267 }
268#endif
269
270 if (pHandler->m_bRegistered) {
271 const size_t priority = pHandler->m_Priority;
272 if (pHandler->m_pPrevious) {
273 pHandler->m_pPrevious->m_pNext = pHandler->m_pNext;
274 } else {
275 m_Handlers[priority] = pHandler->m_pNext;
276 }
277 if (pHandler->m_pNext) {
278 pHandler->m_pNext->m_pPrevious = pHandler->m_pPrevious;
279 } else {
280 m_HandlerTails[priority] = pHandler->m_pPrevious;
281 }
282
283 pHandler->m_pPrevious = nullptr;
284 pHandler->m_pNext = nullptr;
285 pHandler->m_bRegistered = false;
286 pHandler->m_bRemoving = true;
287 needsDrain = true;
288 } else {
289 if (pHandler->m_bRemoving) {
290 FATAL(
291 "Concurrent removal of one memory-pressure handler is "
292 "not permitted.");
293 }
294 }
295 }
296
297 if (!needsDrain) {
298 return;
299 }
300
301#if THREADS
302 const bool canBlock = Processor::information().getCurrentThread() && Processor::getInterrupts();
303 while (true) {
304 auto callbackGuard = pHandler->m_CallbackWaiters.acquire();
305 const bool callbacksInFlight = pHandler->m_CallbacksInFlight;
306 if (!callbacksInFlight) {
307 break;
308 }
309
310 if (!canBlock) {
311 FATAL(
312 "Cannot drain a live memory-pressure callback from atomic "
313 "context.");
314 }
315
316 const WaitQueue::WakeReason callbackWake = callbackGuard.waitForCompletion(
317 WaitQueue::Channel(pHandler), Thread::CallbackDrain, reinterpret_cast<uintptr_t>(pHandler));
318 (void)callbackWake;
319 }
320
321 {
322 LockGuard<Spinlock> guard(m_Lock);
323 assert(pHandler->m_bRemoving);
324 assert(!pHandler->m_bRegistered);
325 pHandler->m_bRemoving = false;
326 }
327#else
328 {
329 LockGuard<Spinlock> guard(m_Lock);
330 if (!pHandler->m_bRegistered) {
331 pHandler->m_bRemoving = false;
332 }
333 }
334#endif
335}
virtual bool compact()=0
virtual const char * getMemoryPressureDescription()=0
void registerHandler(size_t prio, MemoryPressureHandler *pHandler)
void removeHandler(MemoryPressureHandler *pHandler)
static bool getInterrupts()
static ProcessorInformation & information()
static size_t m_Initialised
Definition Processor.h:483