The Pedigree Project 0.1
MemoryPool.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/processor/PhysicalMemoryManager.h"
23#include "pedigree/kernel/processor/Processor.h"
24#include "pedigree/kernel/processor/VirtualAddressSpace.h"
25#include "pedigree/kernel/utilities/MemoryPool.h"
26#include "pedigree/kernel/utilities/assert.h"
27#include "pedigree/kernel/utilities/utility.h"
28
29static void map(uintptr_t location) {
31
32 void* page = page_align(reinterpret_cast<void*>(location));
33 if (!va.isMapped(page)) {
34 physical_uintptr_t phys = PhysicalMemoryManager::instance().allocatePage();
36 }
37}
38
39static bool unmap(uintptr_t location) {
41
42 void* page = page_align(reinterpret_cast<void*>(location));
43 bool result = false;
44 if ((result = va.isMapped(page))) {
45 size_t flags = 0;
46 physical_uintptr_t phys = 0;
47 va.getMapping(page, phys, flags);
48
49 va.unmap(page);
51 }
52
53 return result;
54}
55
56MemoryPoolPressureHandler::MemoryPoolPressureHandler(MemoryPool* pool) : m_Pool(pool) {}
57
58MemoryPoolPressureHandler::~MemoryPoolPressureHandler() {}
59
61 return "MemoryPool: freeing unused pages";
62}
63
65 return m_Pool->trim();
66}
67
68MemoryPool::MemoryPool()
69 :
70#if THREADS
71 m_Condition(),
72 m_DrainCondition(),
73 m_Lock(),
74 m_MappingLock(),
75#endif
76 m_BufferSize(1024),
77 m_BufferCount(0),
78 m_Pool("memory-pool"),
79 m_bInitialised(false),
80 m_bClosing(false),
81 m_ActiveOperations(0),
82 m_AllocBitmap(),
83 m_PressureHandler(this) {
84}
85
86MemoryPool::MemoryPool(const char* poolName)
87 :
88#if THREADS
89 m_Condition(),
90 m_DrainCondition(),
91 m_Lock(),
92 m_MappingLock(),
93#endif
94 m_BufferSize(1024),
95 m_BufferCount(0),
96 m_Pool(poolName),
97 m_bInitialised(false),
98 m_bClosing(false),
99 m_ActiveOperations(0),
100 m_AllocBitmap(),
101 m_PressureHandler(this) {
102}
103
104MemoryPool::~MemoryPool() {
105 TerminationDeferral terminationDeferral;
106#if THREADS
107 m_Lock.acquire();
108#endif
109 bool wasInitialised = m_bInitialised;
110 m_bClosing = true;
111 m_bInitialised = false;
112#if THREADS
113 m_Condition.broadcast();
114 while (m_ActiveOperations) {
115 m_DrainCondition.waitForCompletion(m_Lock);
116 }
117 m_Lock.release();
118#endif
119
120 if (wasInitialised) {
121 MemoryPressureManager::instance().removeHandler(&m_PressureHandler);
122 }
123}
124
125MemoryPool::ActiveOperation::ActiveOperation(MemoryPool& pool)
126 : m_TerminationDeferral(), m_Pool(pool.beginOperation() ? &pool : nullptr) {}
127
128MemoryPool::ActiveOperation::~ActiveOperation() {
129 if (m_Pool) {
130 m_Pool->endOperation();
131 }
132}
133
134bool MemoryPool::beginOperation() {
135#if THREADS
136 LockGuard<Mutex> guard(m_Lock);
137#endif
138 if (m_bClosing || !m_bInitialised) {
139 return false;
140 }
141
143 return true;
144}
145
146void MemoryPool::endOperation() {
147#if THREADS
148 LockGuard<Mutex> guard(m_Lock);
149#endif
152#if THREADS
154 m_DrainCondition.signal();
155 }
156#endif
157}
158
159bool MemoryPool::initialise(size_t poolSize, size_t bufferSize) {
160#if THREADS
161 LockGuard<Mutex> guard(m_Lock);
162#endif
163
164 if (m_bClosing)
165 return false;
166
167 if (m_bInitialised)
168 return true;
169
170 if (!poolSize || !bufferSize)
171 return false;
172
173 const size_t maxSize = ~static_cast<size_t>(0);
174 const size_t pageSize = PhysicalMemoryManager::getPageSize();
175 if (poolSize > (maxSize / pageSize))
176 return false;
177 const size_t poolBytes = poolSize * pageSize;
178
179 // Find the next power of two for bufferSize, if it isn't already one
180 if ((bufferSize & (bufferSize - 1))) {
181 size_t powerOf2 = 1;
182 while (powerOf2 < bufferSize) {
183 if (powerOf2 > (maxSize >> 1))
184 return false;
185 powerOf2 <<= 1;
186 }
187 bufferSize = powerOf2;
188 }
189
190 if (bufferSize > poolBytes)
191 return false;
192
193 m_BufferSize = bufferSize;
194
195 NOTICE("MemoryPool: allocating memory pool '" << m_Pool.name() << "', " << Dec
196 << (poolBytes / 1024) << Hex << "K. Buffer size is "
197 << m_BufferSize << ".");
201 if (!m_bInitialised)
202 return false;
203
205
206 // Register us as a memory pressure handler, with top priority. We should
207 // very easily be able to free pages in most cases.
208 MemoryPressureManager::instance().registerHandler(MemoryPressureManager::HighestPriority,
210
211 return true;
212}
213
215 return allocateDoer(true);
216}
217
219 return allocateDoer(false);
220}
221
222uintptr_t MemoryPool::allocateDoer(bool canBlock) {
223 ActiveOperation operation(*this);
224 if (!operation) {
225 return 0;
226 }
227
228 uintptr_t result = 0;
229 {
230#if THREADS
231 LockGuard<Mutex> guard(m_Lock);
232#endif
233
234 size_t poolSize = m_Pool.size();
235 size_t nBuffers = poolSize / m_BufferSize;
236 uintptr_t poolBase = reinterpret_cast<uintptr_t>(m_Pool.virtualAddress());
237
238#if THREADS
239 while (m_bInitialised && !m_BufferCount) {
240 if (!canBlock) {
241 return 0;
242 }
243
244 ConditionVariable::Error error = ConditionVariable::NoError;
245 if (!m_Condition.wait(m_Lock, error)) {
247 guard.disown();
248 }
249 return 0;
250 }
251 }
252
253 if (!m_bInitialised) {
254 return 0;
255 }
256#else
257 if (!m_bInitialised || !m_BufferCount) {
258 return 0;
259 }
260#endif
261
262 size_t n = m_AllocBitmap.getFirstClear();
263 assert(n < nBuffers);
265
266 size_t offset = n * m_BufferSize;
267 assert((offset % m_BufferSize) == 0);
268 assert(offset < poolSize);
269 assert(m_BufferSize <= (poolSize - offset));
270 result = poolBase + offset;
271
273 }
274
275#if THREADS
276 {
277 LockGuard<Mutex> mappingGuard(m_MappingLock);
278 for (size_t offset = 0; offset < m_BufferSize; offset += PhysicalMemoryManager::getPageSize()) {
279 map(result + offset);
280 }
281 }
282#else
283 for (size_t offset = 0; offset < m_BufferSize; offset += PhysicalMemoryManager::getPageSize()) {
284 map(result + offset);
285 }
286#endif
287 return result;
288}
289
290void MemoryPool::free(uintptr_t buffer) {
291 ActiveOperation operation(*this);
292 if (!operation) {
293 return;
294 }
295
296#if THREADS
297 LockGuard<Mutex> guard(m_Lock);
298#endif
299
301 return;
302
303 uintptr_t poolBase = reinterpret_cast<uintptr_t>(m_Pool.virtualAddress());
304 size_t poolSize = m_Pool.size();
305 assert(buffer >= poolBase);
306 size_t offset = buffer - poolBase;
307 assert(offset < poolSize);
308 assert((offset % m_BufferSize) == 0);
309
310 size_t n = offset / m_BufferSize;
311 assert(n < (poolSize / m_BufferSize));
314
316 assert(m_BufferCount <= (poolSize / m_BufferSize));
317
318#if THREADS
319 m_Condition.signal();
320#endif
321}
322
324 ActiveOperation operation(*this);
325 if (!operation) {
326 return false;
327 }
328
329#if THREADS
330 LockGuard<Mutex> guard(m_Lock);
331#endif
332
333 if (!m_bInitialised || m_bClosing) {
334 return false;
335 }
336
337#if THREADS
338 // Compaction is opportunistic and can be entered by allocatePage() while
339 // this same execution context owns the mapping lock. Never wait on the
340 // allocator whose failure invoked us.
341 if (!m_MappingLock.tryAcquire()) {
342 return false;
343 }
344#endif
345
346 size_t poolSize = m_Pool.size();
347 size_t nBuffers = poolSize / m_BufferSize;
348 uintptr_t poolBase = reinterpret_cast<uintptr_t>(m_Pool.virtualAddress());
349
350 // Easy trim if buffers are pages or larger (remember that buffer sizes are
351 // rounded up to the next power of two).
352 size_t nFreed = 0;
354 for (size_t n = 0; n < nBuffers; ++n) {
355 if (!m_AllocBitmap.test(n)) {
356 uintptr_t page = poolBase + (n * m_BufferSize);
357 for (size_t off = 0; off < m_BufferSize; off += PhysicalMemoryManager::getPageSize()) {
358 if (unmap(page + off))
359 ++nFreed;
360 }
361 }
362 }
363 } else {
364 // Need to find N contiguous sets of bits.
365 // We also need to navigate in blocks of pages.
367 for (size_t n = 0, m = 0; n < nBuffers; n += N, ++m) {
368 if (m_AllocBitmap.test(n))
369 continue;
370
371 bool ok = true;
372 for (size_t y = 1; y < N; ++y) {
373 if (m_AllocBitmap.test(n + y)) {
374 ok = false;
375 break;
376 }
377 }
378
379 if (!ok)
380 continue;
381
382 uintptr_t page = poolBase + (m * PhysicalMemoryManager::getPageSize());
383 if (unmap(page))
384 ++nFreed;
385 }
386 }
387
388#if THREADS
389 m_MappingLock.release();
390#endif
391 return nFreed > 0;
392}
393
394#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
395void MemoryPool::acquireHostedOperationLock() {
396 m_Lock.acquire();
397}
398
399void MemoryPool::releaseHostedOperationLock() {
400 m_Lock.release();
401}
402
403void MemoryPool::acquireHostedMappingLock() {
404 m_MappingLock.acquire();
405}
406
407void MemoryPool::releaseHostedMappingLock() {
408 m_MappingLock.release();
409}
410
411size_t MemoryPool::getHostedActiveOperationCount() {
412 LockGuard<Mutex> guard(m_Lock);
413 return m_ActiveOperations;
414}
415#endif
void waitForCompletion(Mutex &mutex)
MUST_USE_RESULT bool wait(Mutex &mutex, Time::Timestamp &timeout, Error &error, WaitQueue::StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
static bool mutexAcquired(Error error)
bool test(size_t n) const
void clear(size_t n)
void set(size_t n)
void disown()
Definition LockGuard.h:69
virtual const char * getMemoryPressureDescription()
Definition MemoryPool.cc:60
void free(uintptr_t buffer)
Frees an allocated buffer, allowing it to be used elsewhere.
size_t m_BufferSize
Size of each buffer in this pool.
Definition MemoryPool.h:130
bool m_bInitialised
Has this instance been initialised yet?
Definition MemoryPool.h:141
ExtensibleBitmap m_AllocBitmap
Allocation bitmap.
Definition MemoryPool.h:150
size_t m_ActiveOperations
Operations which entered before destruction began.
Definition MemoryPool.h:147
uintptr_t allocate()
bool m_bClosing
Destruction has begun; no new operations may enter.
Definition MemoryPool.h:144
size_t m_BufferCount
Number of buffers we have available.
Definition MemoryPool.h:133
bool trim()
Trims the pool, freeing pages that are not otherwise in use.
MemoryRegion m_Pool
MemoryRegion describing the actual pool of memory.
Definition MemoryPool.h:137
MemoryPoolPressureHandler m_PressureHandler
Memory pressure handler for this pool.
Definition MemoryPool.h:154
uintptr_t allocateNow()
uintptr_t allocateDoer(bool canBlock)
Allocation doer.
bool initialise(size_t poolSize, size_t bufferSize=1024)
void registerHandler(size_t prio, MemoryPressureHandler *pHandler)
void removeHandler(MemoryPressureHandler *pHandler)
const char * name() const
void * virtualAddress() const
size_t size() const
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
void release(size_t n=1)
Definition Semaphore.cc:546
bool tryAcquire(size_t n=1)
Definition Semaphore.cc:481
bool acquire(size_t n=1, size_t timeoutSecs=0, size_t timeoutUsecs=0)
Definition Semaphore.cc:352
virtual bool map(physical_uintptr_t physicalAddress, void *virtualAddress, size_t flags)=0
virtual bool isMapped(void *virtualAddress)=0
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
virtual void unmap(void *virtualAddress)=0
#define assert(x)
Definition assert.h:39
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
EXPORTED_PUBLIC void * page_align(void *p) PURE
Definition utility.cc:29