The Pedigree Project 0.1
SlamAllocator.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#if !SLAM_USE_DEBUG_ALLOCATOR
21
22#include "pedigree/kernel/LockGuard.h"
23#include "pedigree/kernel/Log.h"
24#include "pedigree/kernel/core/SlamAllocator.h"
25#include "pedigree/kernel/debugger/Backtrace.h"
26#include "pedigree/kernel/debugger/commands/SlamCommand.h"
27#include "pedigree/kernel/machine/Machine.h"
28#include "pedigree/kernel/panic.h"
29#include "pedigree/kernel/process/Process.h"
30#include "pedigree/kernel/process/Thread.h"
31#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
32#include "pedigree/kernel/processor/Processor.h"
33#include "pedigree/kernel/processor/ProcessorInformation.h"
34#include "pedigree/kernel/processor/VirtualAddressSpace.h"
35#include "pedigree/kernel/utilities/MemoryTracing.h"
36#include "pedigree/kernel/utilities/assert.h"
37#include "pedigree/kernel/utilities/utility.h"
38
39#if X64 && !PEDIGREE_BENCHMARK
40#include "system/kernel/core/processor/x64/utils.h"
41#endif
42
43#if MULTIPROCESSOR
44#define ATOMIC_POP_MEMORY_ORDER __ATOMIC_ACQUIRE
45#define ATOMIC_POP_FAILURE_MEMORY_ORDER __ATOMIC_ACQUIRE
46#define ATOMIC_PUSH_MEMORY_ORDER __ATOMIC_RELEASE
47#define ATOMIC_CAS_WEAK true
48#else
49#define ATOMIC_POP_MEMORY_ORDER __ATOMIC_RELAXED
50#define ATOMIC_POP_FAILURE_MEMORY_ORDER __ATOMIC_RELAXED
51#define ATOMIC_PUSH_MEMORY_ORDER __ATOMIC_RELAXED
52#define ATOMIC_CAS_WEAK true
53#endif
54
55SlamAllocator SlamAllocator::m_Instance;
56
57#if BITS_32
58static constexpr uintptr_t POINTER_MASK = ~uintptr_t(0);
59static constexpr uintptr_t POINTER_TAG_MASK = 0;
60static constexpr uintptr_t POINTER_TAG_INCREMENT = 0;
61#else
62static constexpr uintptr_t POINTER_MASK = 0x0000FFFFFFFFFFFFULL;
63static constexpr uintptr_t POINTER_TAG_MASK = ~POINTER_MASK;
64static constexpr uintptr_t POINTER_TAG_INCREMENT = 0x0001000000000000ULL;
65#endif
66
67template <typename T>
68inline T* untagged(T* p) PURE;
69
70template <typename T>
71inline T* tagged(T* p) PURE;
72
73template <typename T>
74inline T* next_tag(T* p, T* currentHead) PURE;
75
76template <typename T>
77inline T* untagged(T* p) {
78#if BITS_32
79 return p;
80#else
82 // All heap pointers begin with 32 bits of ones. So we shove a tag there.
83 uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
84 EMIT_IF(PEDIGREE_BENCHMARK || HOSTED) {
85 // The upper 16 bits are available for hosted userspace addresses.
86 ptr &= POINTER_MASK;
87 }
88 else {
89 // Restore the upper 16 bits to make this a canonical kernel address.
90 ptr |= 0xFFFF000000000000ULL;
91 }
92 return reinterpret_cast<T*>(ptr);
93#endif
94}
95
96template <typename T>
97inline T* tagged(T* p) {
98#if BITS_32
99 return p;
100#else
101 uintptr_t ptr = reinterpret_cast<uintptr_t>(p);
102 ptr &= POINTER_MASK;
103 return reinterpret_cast<T*>(ptr);
104#endif
105}
106
107template <typename T>
108inline T* next_tag(T* p, T* currentHead) {
109#if BITS_32
110 (void)currentHead;
111 return p;
112#else
113 uintptr_t ptr = reinterpret_cast<uintptr_t>(p) & POINTER_MASK;
114 uintptr_t tag =
115 (reinterpret_cast<uintptr_t>(currentHead) + POINTER_TAG_INCREMENT) & POINTER_TAG_MASK;
116 return reinterpret_cast<T*>(ptr | tag);
117#endif
118}
119
120inline void spin_pause() {
121#if PEDIGREE_BENCHMARK
122#if defined(__i386__) || defined(__x86_64__)
123 asm("pause");
124#elif defined(__aarch64__)
125 asm("yield");
126#endif
127#else
129#endif
130}
131
132inline uintptr_t getHeapBase() {
133#if PEDIGREE_BENCHMARK
134 return SlamSupport::getHeapBase();
135#else
137#endif
138}
139
140inline uintptr_t getHeapEnd() {
141#if PEDIGREE_BENCHMARK
142 return SlamSupport::getHeapEnd();
143#else
145#endif
146}
147
148inline size_t getPageSize() {
149#if PEDIGREE_BENCHMARK
151#else
153#endif
154}
155
156inline void allocateAndMapAt(void* addr, bool cowOk = false) {
157#if PEDIGREE_BENCHMARK
158 SlamSupport::getPageAt(addr);
159#else
161
162 static physical_uintptr_t physZero = 0;
163 bool needZeroPage = false;
164 size_t extraFlags = 0;
165
166 physical_uintptr_t phys = 0;
167 if (cowOk) {
168 if (!physZero) {
169 // allocate the zero page, we'll zero it shortly
171 // Retain one permanent owner in addition to the reference held by
172 // every CoW mapping. A fault drops only its mapping reference.
174 needZeroPage = true;
175
176 // allow us to zero out the page
177 extraFlags |= VirtualAddressSpace::Write;
178 } else {
180 }
181
182 // disable writing (for CoW to work properly)
183 standardFlags &= ~VirtualAddressSpace::Write;
184
185 phys = physZero;
186 } else {
188 }
189
191 if (!va.map(phys, addr, standardFlags | extraFlags)) {
192 FATAL("SlamAllocator: failed to allocate and map at " << addr);
193 }
194
195 if (cowOk) {
197 }
198
199 if (needZeroPage) {
200 ByteSet(addr, 0, PhysicalMemoryManager::getPageSize());
201
202 // Page zeroed - mark page copy on write now so the zero page works
203 va.setFlags(addr, standardFlags | VirtualAddressSpace::CopyOnWrite);
204 }
205#endif
206}
207
208inline void unmap(void* addr) {
209#if PEDIGREE_BENCHMARK
210 SlamSupport::unmapPage(addr);
211// munmap(addr, getPageSize());
212#else
214 if (!va.isMapped(addr))
215 return;
216
217 physical_uintptr_t phys;
218 size_t flags;
219 va.getMapping(addr, phys, flags);
220 va.unmap(addr);
221
223#endif
224}
225
227 : m_PartialLists(),
228 m_FastSlabs(),
229 m_LargeFreeList(nullptr),
230 m_ObjectSize(0),
231 m_SlabSize(0),
232 m_SlabObjectOffset(0),
233 m_SlabObjectCount(0),
234 m_FirstSlab(),
235 m_FastPathState(0),
236 m_RecoveryLock(false, true)
237#if BITS_32
238 ,
239 m_FreeLock(false)
240#endif
241{
242}
243
245
246void SlamCache::initialise(SlamAllocator* parent, size_t objectSize) {
247 if (objectSize < OBJECT_MINIMUM_SIZE)
248 return;
249
250 m_ObjectSize = objectSize;
251 if (m_ObjectSize > SLAB_MINIMUM_SIZE)
252 m_SlabSize = m_ObjectSize;
253 else
254 m_SlabSize = SLAB_MINIMUM_SIZE;
255
256 for (size_t i = 0; i < NUM_LISTS; i++)
257 m_PartialLists[i] = nullptr;
258 for (size_t i = 0; i < NUM_LISTS; i++)
259 m_FastSlabs[i] = nullptr;
260 m_LargeFreeList = nullptr;
261 m_FastPathState = 0;
262
263 if (m_ObjectSize < getPageSize()) {
264 m_SlabObjectOffset = ((sizeof(Slab) + m_ObjectSize - 1) / m_ObjectSize) * m_ObjectSize;
265 m_SlabObjectCount = (m_SlabSize - m_SlabObjectOffset) / m_ObjectSize;
266 }
267
268 m_pParentAllocator = parent;
269
270 assert((m_SlabSize % m_ObjectSize) == 0);
271 assert(m_ObjectSize >= sizeof(Node));
272 if (m_ObjectSize < getPageSize()) {
273 assert(m_SlabObjectOffset < m_SlabSize);
274 assert(m_SlabObjectCount);
275 }
276}
277
278size_t SlamCache::currentList() const {
279#if defined(PEDIGREE_BUILDUTILS)
280 return m_TestList;
281#else
282 size_t list = 0;
283 EMIT_IF(MULTIPROCESSOR) {
284 list = Processor::id();
285 }
286 assert(list < NUM_LISTS);
287 return list;
288#endif
289}
290
291#if defined(PEDIGREE_BUILDUTILS)
292void SlamCache::setListForTest(size_t list) {
293 assert(list < NUM_LISTS);
294 m_TestList = list;
295}
296#endif
297
298void SlamCache::addSlab(Slab* slab, size_t list) {
299 assert(!slab->onList);
300 slab->list = list;
301 slab->previous = nullptr;
302 slab->next = m_PartialLists[list];
303 if (slab->next)
304 slab->next->previous = slab;
305 m_PartialLists[list] = slab;
306 slab->onList = true;
307}
308
309void SlamCache::removeSlab(Slab* slab) {
310 assert(slab->onList);
311 if (slab->previous)
312 slab->previous->next = slab->next;
313 else
314 m_PartialLists[slab->list] = slab->next;
315 if (slab->next)
316 slab->next->previous = slab->previous;
317 slab->next = nullptr;
318 slab->previous = nullptr;
319 slab->onList = false;
320}
321
322bool SlamCache::beginFastPath() {
323 constexpr size_t writer = static_cast<size_t>(1) << ((sizeof(size_t) * 8) - 1);
324 size_t state = __atomic_load_n(&m_FastPathState, __ATOMIC_ACQUIRE);
325 while (!(state & writer)) {
326 if (__atomic_compare_exchange_n(&m_FastPathState, &state, state + 1, false, __ATOMIC_ACQUIRE,
327 __ATOMIC_RELAXED))
328 return true;
329 }
330 return false;
331}
332
333void SlamCache::endFastPath() {
334 __atomic_fetch_sub(&m_FastPathState, static_cast<size_t>(1), __ATOMIC_RELEASE);
335}
336
337SlamCache::Node* SlamCache::popFreeObject(Slab* slab) {
338#if BITS_32
339 LockGuard<Spinlock> guard(m_FreeLock);
340 Node* head = slab->freeHead;
341 if (head) {
342 slab->freeHead = head->next;
343 __atomic_fetch_sub(&slab->freeObjects, static_cast<size_t>(1), __ATOMIC_ACQ_REL);
344 }
345 return head;
346#else
347 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
348 return nullptr;
349
350 Node* taggedHead = __atomic_load_n(&slab->freeHead, ATOMIC_POP_MEMORY_ORDER);
351 // An empty head still carries an ABA tag; the count can lag behind a pop.
352 while (reinterpret_cast<uintptr_t>(taggedHead) & POINTER_MASK) {
353 Node* head = untagged(taggedHead);
354 Node* next = head->next;
355 if (__atomic_compare_exchange_n(&slab->freeHead, &taggedHead, next_tag(next, taggedHead),
356 ATOMIC_CAS_WEAK, ATOMIC_POP_MEMORY_ORDER,
357 ATOMIC_POP_FAILURE_MEMORY_ORDER)) {
358 __atomic_fetch_sub(&slab->freeObjects, static_cast<size_t>(1), __ATOMIC_ACQ_REL);
359 return head;
360 }
361 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
362 return nullptr;
363 }
364
365 return nullptr;
366#endif
367}
368
369void SlamCache::pushFreeObject(Slab* slab, Node* node) {
370#if BITS_32
371 LockGuard<Spinlock> guard(m_FreeLock);
372 node->next = slab->freeHead;
373 slab->freeHead = node;
374 __atomic_fetch_add(&slab->freeObjects, static_cast<size_t>(1), __ATOMIC_RELEASE);
375#else
376 Node* head = __atomic_load_n(&slab->freeHead, __ATOMIC_RELAXED);
377 do {
378 node->next = head;
379 } while (!__atomic_compare_exchange_n(&slab->freeHead, &head, next_tag(node, head),
380 ATOMIC_CAS_WEAK, ATOMIC_PUSH_MEMORY_ORDER,
381 __ATOMIC_RELAXED));
382 __atomic_fetch_add(&slab->freeObjects, static_cast<size_t>(1), __ATOMIC_RELEASE);
383#endif
384}
385
386SlamCache::Node* SlamCache::objectAt(uintptr_t slab, size_t index) const {
387 return reinterpret_cast<Node*>(slab + m_SlabObjectOffset + (index * m_ObjectSize));
388}
389
390SlamCache::Slab* SlamCache::slabForObject(uintptr_t object) const {
391 assert(m_ObjectSize < getPageSize());
392 return reinterpret_cast<Slab*>(object & ~(getPageSize() - 1));
393}
394
396 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
397 return getSlab();
398 }
399
400 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
401 if (m_ObjectSize >= getPageSize()) {
402 // just return a big-enough slab - allocation is page-sized or bigger
403 return getSlab();
404 }
405 }
406
407 if (m_ObjectSize >= getPageSize()) {
408 {
410 if (m_LargeFreeList) {
411 Node* node = m_LargeFreeList;
412 m_LargeFreeList = node->next;
413 EMIT_IF(USING_MAGIC) {
414 assert(node->magic == MAGIC_VALUE);
415 node->magic = TEMP_MAGIC;
416 }
417 reinterpret_cast<SlamAllocator::AllocHeader*>(node)->cache = this;
418 return reinterpret_cast<uintptr_t>(node);
419 }
420 }
421 return reinterpret_cast<uintptr_t>(initialiseSlab(getSlab()));
422 }
423
424 const size_t thisList = currentList();
425 Node* N = nullptr;
426 Slab* fastSlab = nullptr;
427 const bool fastPath = beginFastPath();
428 if (fastPath) {
429 fastSlab = __atomic_load_n(&m_FastSlabs[thisList], __ATOMIC_ACQUIRE);
430 if (fastSlab) {
431 N = popFreeObject(fastSlab);
432 if (N && !__atomic_load_n(&fastSlab->freeObjects, __ATOMIC_ACQUIRE))
433 __atomic_store_n(&m_FastSlabs[thisList], static_cast<Slab*>(nullptr), __ATOMIC_RELEASE);
434 }
435 endFastPath();
436 }
437
438 if (N && fastSlab && !__atomic_load_n(&fastSlab->freeObjects, __ATOMIC_ACQUIRE)) {
440 if (!__atomic_load_n(&fastSlab->freeObjects, __ATOMIC_ACQUIRE) && fastSlab->onList)
441 removeSlab(fastSlab);
442 }
443
444 if (N) {
445 assert(N->next != reinterpret_cast<Node*>(VIGILANT_MAGIC));
446 EMIT_IF(USING_MAGIC) {
447 assert(N->magic == TEMP_MAGIC || N->magic == MAGIC_VALUE);
448 N->magic = TEMP_MAGIC;
449 }
450 reinterpret_cast<SlamAllocator::AllocHeader*>(N)->cache = this;
451 }
452
453 if (!N) {
455 for (size_t offset = 0; offset < NUM_LISTS; ++offset) {
456 const size_t list = (thisList + offset) % NUM_LISTS;
457 if (__atomic_load_n(&m_FastSlabs[list], __ATOMIC_ACQUIRE))
458 continue;
459 for (Slab* slab = m_PartialLists[list]; slab; slab = slab->next) {
460 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
461 continue;
462 N = popFreeObject(slab);
463 if (!N)
464 continue;
465 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
466 removeSlab(slab);
467 else if (list == thisList)
468 __atomic_store_n(&m_FastSlabs[thisList], slab, __ATOMIC_RELEASE);
469 break;
470 }
471 if (N)
472 break;
473 }
474
475 if (N) {
476 // Check that the block was indeed free.
477 assert(N->next != reinterpret_cast<Node*>(VIGILANT_MAGIC));
478 EMIT_IF(USING_MAGIC) {
479 assert(N->magic == TEMP_MAGIC || N->magic == MAGIC_VALUE);
480 N->magic = TEMP_MAGIC;
481 }
482 SlamAllocator::AllocHeader* header = reinterpret_cast<SlamAllocator::AllocHeader*>(N);
483 header->cache = this;
484 }
485 }
486
487 // No CPU-local list had a free object. Allocate a new slab without holding
488 // the cache lock across physical-memory and page-table work.
489 if (UNLIKELY(!N)) {
490 Node* pNode = initialiseSlab(getSlab());
491 uintptr_t slab = reinterpret_cast<uintptr_t>(pNode);
492 EMIT_IF(CRIPPLINGLY_VIGILANT) {
493 if (m_pParentAllocator->getVigilance())
494 trackSlab(slab);
495 }
496 return slab;
497 }
498
499 return reinterpret_cast<uintptr_t>(N);
500}
501
502void SlamCache::free(uintptr_t object) {
503 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
504 // Free the slab in the address space, but don't return it to the allocator.
505 size_t numPages = m_SlabSize / getPageSize();
506 if (m_SlabSize % getPageSize()) {
507 ++numPages;
508 }
509 object = object & ~(getPageSize() - 1);
510 for (size_t i = 0; i < numPages; ++i) {
511 unmap(reinterpret_cast<void*>(object + (i * getPageSize())));
512 }
513
514 return;
515 }
516
517 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
518 if (m_ObjectSize >= getPageSize()) {
519 // just free the object directly, it's an entire slab
520 freeSlab(object);
521 return;
522 }
523 }
524
525 Node* N = reinterpret_cast<Node*>(object);
526
527 EMIT_IF(OVERRUN_CHECK) {
528 // Grab the footer and check it.
529 SlamAllocator::AllocFooter* pFoot = reinterpret_cast<SlamAllocator::AllocFooter*>(
530 object + m_ObjectSize - sizeof(SlamAllocator::AllocFooter));
531 assert(pFoot->magic == VIGILANT_MAGIC);
532 }
533
534 SlamAllocator::AllocHeader* pHeader = reinterpret_cast<SlamAllocator::AllocHeader*>(object);
535 assert(pHeader->cache == this);
536 pHeader->cache = nullptr;
537
538 EMIT_IF(USING_MAGIC) {
539 // Possible double free?
540 assert(N->magic != MAGIC_VALUE);
541 N->magic = MAGIC_VALUE;
542 }
543
544 if (m_ObjectSize >= getPageSize()) {
546 N->next = m_LargeFreeList;
547 m_LargeFreeList = N;
548 return;
549 }
550
551 Slab* slab = slabForObject(object);
552 const bool fastPath = beginFastPath();
553 if (fastPath) {
554 if (__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
555 pushFreeObject(slab, N);
556 endFastPath();
557 return;
558 }
559 endFastPath();
560 }
561
562 if (!fastPath) {
564 pushFreeObject(slab, N);
565 if (!slab->onList)
566 addSlab(slab, slab->list);
567 if (slab->list == currentList())
568 __atomic_store_n(&m_FastSlabs[slab->list], slab, __ATOMIC_RELEASE);
569 return;
570 }
571
572 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
574 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
575 pushFreeObject(slab, N);
576 if (!slab->onList)
577 addSlab(slab, slab->list);
578 if (slab->list == currentList())
579 __atomic_store_n(&m_FastSlabs[slab->list], slab, __ATOMIC_RELEASE);
580 return;
581 }
582 }
583 pushFreeObject(slab, N);
584}
585
586bool SlamCache::isPointerValid(uintptr_t object) const {
587 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
588 if (m_ObjectSize >= getPageSize()) {
590 return true;
591 }
592 }
593
594 Node* N = reinterpret_cast<Node*>(object);
595 EMIT_IF(OVERRUN_CHECK) {
596 // Grab the footer and check it.
597 SlamAllocator::AllocFooter* pFoot = reinterpret_cast<SlamAllocator::AllocFooter*>(
598 object + m_ObjectSize - sizeof(SlamAllocator::AllocFooter));
599 if (pFoot->magic != VIGILANT_MAGIC) {
600 return false;
601 }
602 }
603
604 EMIT_IF(USING_MAGIC) {
605 // Possible double free?
606 if (N->magic == MAGIC_VALUE) {
607 EMIT_IF(VERBOSE_ISPOINTERVALID) {
608 WARNING("SlamCache::isPointerValid: memory " << Hex << object << " has invalid magic ("
609 << N->magic << " != " << MAGIC_VALUE << ").");
610 }
611 return false;
612 }
613 }
614
615 return true;
616}
617
618uintptr_t SlamCache::getSlab() {
619 return m_pParentAllocator->getSlab(m_SlabSize);
620}
621
622void SlamCache::freeSlab(uintptr_t slab) {
623 m_pParentAllocator->freeSlab(slab, m_SlabSize);
624}
625
626size_t SlamCache::recovery(size_t maxSlabs) {
627 if (!maxSlabs) {
628 return 0;
629 }
630
631 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
632 return 0;
633 }
634
635 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
636 if (m_ObjectSize >= getPageSize()) {
637 // Caches with slabs page-sized or bigger don't hold onto freed regions
638 return 0;
639 }
640 }
641
642 constexpr size_t writer = static_cast<size_t>(1) << ((sizeof(size_t) * 8) - 1);
643 size_t expected = 0;
644 while (true) {
645 expected = __atomic_load_n(&m_FastPathState, __ATOMIC_ACQUIRE);
646 if (expected & writer) {
647 spin_pause();
648 continue;
649 }
650 const size_t requested = expected | writer;
651 if (__atomic_compare_exchange_n(&m_FastPathState, &expected, requested, false, __ATOMIC_ACQUIRE,
652 __ATOMIC_RELAXED))
653 break;
654 }
655 while (__atomic_load_n(&m_FastPathState, __ATOMIC_ACQUIRE) != writer)
656 spin_pause();
657
659
660 size_t freedSlabs = 0;
661 if (m_ObjectSize < getPageSize()) {
662 for (size_t list = 0; list < NUM_LISTS && freedSlabs < maxSlabs; ++list) {
663 Slab* slab = m_PartialLists[list];
664 while (slab && freedSlabs < maxSlabs) {
665 Slab* next = slab->next;
666 if (__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE) == slab->objectCount) {
667 if (__atomic_load_n(&m_FastSlabs[slab->list], __ATOMIC_ACQUIRE) == slab)
668 __atomic_store_n(&m_FastSlabs[slab->list], static_cast<Slab*>(nullptr),
669 __ATOMIC_RELEASE);
670 removeSlab(slab);
671 freeSlab(reinterpret_cast<uintptr_t>(slab));
672 ++freedSlabs;
673 }
674 slab = next;
675 }
676 }
677 } else {
678 while (m_LargeFreeList && freedSlabs < maxSlabs) {
679 Node* node = m_LargeFreeList;
680 m_LargeFreeList = node->next;
681 freeSlab(reinterpret_cast<uintptr_t>(node));
682 ++freedSlabs;
683 }
684 }
685
686 __atomic_store_n(&m_FastPathState, static_cast<size_t>(0), __ATOMIC_RELEASE);
687 return freedSlabs;
688}
689
690SlamCache::Node* SlamCache::initialiseSlab(uintptr_t slab) {
691 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
692 if (m_ObjectSize >= getPageSize()) {
693 return nullptr;
694 }
695 }
696
697 if (m_ObjectSize >= getPageSize()) {
699 EMIT_IF(USING_MAGIC) {
700 reinterpret_cast<Node*>(slab)->magic = TEMP_MAGIC;
701 }
702 reinterpret_cast<SlamAllocator::AllocHeader*>(slab)->cache = this;
703 m_pParentAllocator->markSlabReady(slab, m_SlabSize);
704 return reinterpret_cast<Node*>(slab);
705 }
706
707 const size_t nObjects = m_SlabObjectCount;
708 Slab* slabState = reinterpret_cast<Slab*>(slab);
709 slabState->freeHead = nullptr;
710 slabState->next = nullptr;
711 slabState->previous = nullptr;
712 slabState->cache = this;
713 slabState->freeObjects = nObjects - 1;
714 slabState->objectCount = nObjects;
715 slabState->onList = false;
716
717 Node* N = objectAt(slab, 0);
718 EMIT_IF(USING_MAGIC) {
719 N->magic = TEMP_MAGIC;
720 }
721 for (size_t i = 1; i < nObjects; i++) {
722 Node* pNode = objectAt(slab, i);
723 pNode->next = slabState->freeHead;
724 slabState->freeHead = next_tag(pNode, slabState->freeHead);
725 EMIT_IF(USING_MAGIC) {
726 pNode->magic = MAGIC_VALUE;
727 }
728 reinterpret_cast<SlamAllocator::AllocHeader*>(pNode)->cache = nullptr;
729 }
730
731 {
733 reinterpret_cast<SlamAllocator::AllocHeader*>(N)->cache = this;
734 // Even an initially full slab can be cached and later recovered.
735 slabState->list = currentList();
736 if (slabState->freeObjects)
737 addSlab(slabState, slabState->list);
738 __atomic_store_n(&m_FastSlabs[slabState->list], slabState, __ATOMIC_RELEASE);
739 m_pParentAllocator->markSlabReady(slab, m_SlabSize);
740 }
741
742 return N;
743}
744
745static Spinlock rarp;
746
747void SlamCache::check() {
748 if (m_ObjectSize >= getPageSize()) {
749 return;
750 }
751
752 EMIT_IF(!HOSTED) {
753 if (!Machine::instance().isInitialised() || Processor::m_Initialised != 2)
754 return;
755 }
756 if (m_ObjectSize == 0)
757 return;
758 rarp.acquire();
759
760 size_t nObjects = m_SlabObjectCount;
761
762 size_t maxPerSlab = (m_SlabSize / sizeof(uintptr_t)) - 2;
763
764 uintptr_t curSlab = m_FirstSlab;
765 while (true) {
766 if (!curSlab) {
767 rarp.release();
768 return;
769 }
770 uintptr_t numAlloced = *reinterpret_cast<uintptr_t*>(curSlab);
771 uintptr_t next = *reinterpret_cast<uintptr_t*>(curSlab + sizeof(uintptr_t));
772
773 for (size_t i = 0; i < numAlloced; i++) {
774 uintptr_t slab = *reinterpret_cast<uintptr_t*>(curSlab + sizeof(uintptr_t) * (i + 2));
775 for (size_t i = 0; i < nObjects; i++) {
776 uintptr_t addr = reinterpret_cast<uintptr_t>(objectAt(slab, i));
777 Node* pNode = reinterpret_cast<Node*>(addr);
778 if (pNode->magic == MAGIC_VALUE || pNode->magic == TEMP_MAGIC)
779 // Free, continue.
780 continue;
781 SlamAllocator::AllocHeader* pHead = reinterpret_cast<SlamAllocator::AllocHeader*>(addr);
782 SlamAllocator::AllocFooter* pFoot = reinterpret_cast<SlamAllocator::AllocFooter*>(
783 addr + m_ObjectSize - sizeof(SlamAllocator::AllocFooter));
784 if (pHead->magic != VIGILANT_MAGIC) {
785 ERROR("Possible heap underrun: object starts at "
786 << addr << ", size: " << m_ObjectSize
787 << ", block: " << (addr + sizeof(SlamAllocator::AllocHeader)));
788 }
789 if (pFoot->magic != VIGILANT_MAGIC) {
790 ERROR("Possible heap overrun: object starts at " << addr);
791 assert(false);
792 }
793 }
794 }
795 if (numAlloced == maxPerSlab)
796 curSlab = next;
797 else
798 break;
799 }
800 rarp.release();
801}
802
803void SlamCache::trackSlab(uintptr_t slab) {
804 EMIT_IF(!HOSTED) {
805 if (!Machine::instance().isInitialised() || Processor::m_Initialised != 2)
806 return;
807 }
808 if (m_ObjectSize == 0)
809 return;
810
811 if (!m_FirstSlab) {
812 m_FirstSlab = getSlab();
813 uintptr_t* numAlloced = reinterpret_cast<uintptr_t*>(m_FirstSlab);
814 uintptr_t* next = reinterpret_cast<uintptr_t*>(m_FirstSlab + sizeof(uintptr_t));
815 *numAlloced = 0;
816 *next = 0;
817 }
818
819 size_t maxPerSlab = (m_SlabSize / sizeof(uintptr_t)) - 2;
820
821 uintptr_t curSlab = m_FirstSlab;
822 while (true) {
823 uintptr_t* numAlloced = reinterpret_cast<uintptr_t*>(curSlab);
824 uintptr_t* next = reinterpret_cast<uintptr_t*>(curSlab + sizeof(uintptr_t));
825
826 if (*numAlloced < maxPerSlab) {
827 uintptr_t* p = reinterpret_cast<uintptr_t*>(curSlab + (*numAlloced + 2) * sizeof(uintptr_t));
828 *p = slab;
829 *numAlloced = *numAlloced + 1;
830 return;
831 }
832
833 if (*next)
834 curSlab = *next;
835 else {
836 uintptr_t newSlab = getSlab();
837 *next = newSlab;
838 curSlab = newSlab;
839
840 uintptr_t* numAlloced = reinterpret_cast<uintptr_t*>(curSlab);
841 uintptr_t* next = reinterpret_cast<uintptr_t*>(curSlab + sizeof(uintptr_t));
842 *numAlloced = 0;
843 *next = 0;
844 }
845 }
846}
847
848SlamAllocator::SlamAllocator()
849 : m_bInitialised(false),
850 m_bVigilant(false),
851 m_SlabRegionLock(false, true),
852 m_HeapPageCount(0),
853 m_SlabRegionBitmap(),
854 m_SlabRegionBitmapEntries(0),
855 m_SlabRegionPages(0),
856 m_Base(0) {}
857
858SlamAllocator::~SlamAllocator() {
859 if (m_bInitialised) {
860 // wipe();
861 }
862}
863
864void SlamAllocator::initialise() {
865 LockGuard<Spinlock> guard(m_SlabRegionLock);
866
867 if (m_bInitialised) {
868 return;
869 }
870
871 // We need to allocate our bitmap for this purpose.
872 uintptr_t bitmapBase = getHeapBase();
873 uintptr_t heapEnd = getHeapEnd();
874 size_t heapSize = heapEnd - bitmapBase;
875 size_t heapPages = heapSize / getPageSize();
876 size_t bitmapBytes = ((heapPages + 63) / 64) * sizeof(uint64_t) * 3;
877
878 // Ensure the bitmap size is now page-aligned before we allocate it.
879 if (bitmapBytes & (getPageSize() - 1)) {
880 bitmapBytes &= ~(getPageSize() - 1);
881 bitmapBytes += getPageSize();
882 }
883
884 // Keep reservation and mapping state adjacent. The bitmap is mostly CoW;
885 // interleaving ensures the first mapping-state write is in the same early,
886 // private page as its reservation state.
887 m_SlabRegionBitmap.useMemory(reinterpret_cast<void*>(bitmapBase), (heapPages + 63) / 64,
888 heapPages);
889 m_Base = bitmapBase + bitmapBytes;
890 m_SlabRegionPages = (heapEnd - m_Base) / getPageSize();
891 m_SlabRegionBitmapEntries = (m_SlabRegionPages + 63) / 64;
892
893 // Allocate bitmap.
894 size_t numPages = 0;
895 for (uintptr_t addr = bitmapBase; addr < m_Base; addr += getPageSize()) {
896 // Don't CoW the first 32 pages so we have some slabs on hand for
897 // startup before CoW is viable
898 bool cowOk = numPages++ >= 32;
899 allocateAndMapAt(reinterpret_cast<void*>(addr), cowOk);
900 if (!cowOk) {
901 ByteSet(reinterpret_cast<void*>(addr), 0, getPageSize());
902 }
903 }
904
905 EMIT_IF(!PEDIGREE_BENCHMARK) {
906 NOTICE("Kernel heap range prepared from " << Hex << m_Base << " to " << heapEnd
907 << ", size: " << (heapEnd - m_Base));
908 DEBUG_LOG(" -> kernel heap bitmap is " << Dec << (bitmapBytes / 1024) << Hex << "K");
909 }
910
911 for (size_t i = 0; i < 32; i++) {
912 m_Caches[i].initialise(this, 1ULL << i);
913 }
914
915 NOTICE("all caches init-ed");
916
917 m_bInitialised = true;
918}
919
920void SlamAllocator::clearAll() {
921 EMIT_IF(PEDIGREE_BENCHMARK) {
922 wipe();
923 initialise();
924 }
925}
926
928 if (!m_bInitialised) {
929 return;
930 }
931
932 if (!m_SlabRegionPages) {
933 return;
934 }
935
936 LockGuard<Spinlock> guard(m_SlabRegionLock);
937
938 m_bInitialised = false;
939
940 // Clean up all slabs we obtained.
941 for (size_t entry = 0; entry < m_SlabRegionBitmapEntries; ++entry) {
942 if (!m_SlabRegionBitmap.reservedBits(entry)) {
943 continue;
944 }
945
946 for (size_t bit = 0; bit < 64; ++bit) {
947 uint64_t test = 1ULL << bit;
948 if ((m_SlabRegionBitmap.reservedBits(entry) & test) == 0) {
949 continue;
950 }
951
952 uintptr_t slab = m_Base + (((entry * 64) + bit) * getPageSize());
953 freeSlabUnlocked(slab, getPageSize());
954 }
955 }
956
957 // about to destroy the bitmap mappings
958 m_SlabRegionBitmap.useMemory(nullptr, 0, 0);
959 m_SlabRegionBitmapEntries = 0;
960 m_SlabRegionPages = 0;
961
962 // Clean up the bitmap.
963 for (uintptr_t addr = getHeapBase(); addr < m_Base; addr += getPageSize()) {
964 unmap(reinterpret_cast<void*>(addr));
965 }
966}
967
968uintptr_t SlamAllocator::getSlab(size_t fullSize) {
969 if (fullSize < getPageSize() || (fullSize % getPageSize())) {
970 panic("Attempted to get a slab smaller than the native page size.");
971 }
972 size_t nPages = fullSize / getPageSize();
973
974 SlamBitmap& bitmap = m_SlabRegionBitmap;
975
976 auto findFreeRun = [&]() { return bitmap.findFreeRun(nPages); };
977
978#if X64 && !PEDIGREE_BENCHMARK
979 auto firstCowBitmapPage = [&](size_t pageIndex) {
980 const size_t firstEntry = pageIndex / 64;
981 const size_t lastEntry = (pageIndex + nPages - 1) / 64;
982 const uintptr_t firstAddress = bitmap.metadataAddress(firstEntry) & ~(getPageSize() - 1);
983 const uintptr_t lastEntryByte = bitmap.metadataAddress(lastEntry) + sizeof(uint64_t) * 3 - 1;
984 const uintptr_t lastAddress = lastEntryByte & ~(getPageSize() - 1);
986 for (uintptr_t address = firstAddress; address <= lastAddress; address += getPageSize()) {
987 physical_uintptr_t physical = 0;
988 size_t flags = 0;
989 va.getMapping(reinterpret_cast<void*>(address), physical, flags);
991 return address;
992 }
993 }
994 return static_cast<uintptr_t>(0);
995 };
996#endif
997
998 size_t pageIndex = ~0UL;
999 while (true) {
1000 m_SlabRegionLock.acquire();
1001 pageIndex = findFreeRun();
1002 if (pageIndex == ~0UL) {
1003 m_SlabRegionLock.release();
1004 panic("SlamAllocator cannot find contiguous virtual heap space.");
1005 }
1006
1007#if X64 && !PEDIGREE_BENCHMARK
1008 if (firstCowBitmapPage(pageIndex)) {
1009 m_SlabRegionLock.release();
1010 physical_uintptr_t replacement = PhysicalMemoryManager::instance().allocatePage();
1011
1012 m_SlabRegionLock.acquire();
1013 pageIndex = findFreeRun();
1014 if (pageIndex == ~0UL) {
1015 m_SlabRegionLock.release();
1017 panic("SlamAllocator cannot find contiguous virtual heap space.");
1018 }
1019
1020 const uintptr_t cowAddress = firstCowBitmapPage(pageIndex);
1021 if (cowAddress) {
1023 physical_uintptr_t oldPhysical = 0;
1024 size_t flags = 0;
1025 va.getMapping(reinterpret_cast<void*>(cowAddress), oldPhysical, flags);
1026 MemoryCopy(reinterpret_cast<void*>(physicalAddress(replacement)),
1027 reinterpret_cast<void*>(cowAddress), getPageSize());
1028 va.unmap(reinterpret_cast<void*>(cowAddress));
1030 flags &= ~VirtualAddressSpace::CopyOnWrite;
1031 if (!va.map(replacement, reinterpret_cast<void*>(cowAddress), flags)) {
1032 panic("SlamAllocator could not privatise bitmap metadata.");
1033 }
1035 m_SlabRegionLock.release();
1036 continue;
1037 }
1038
1039 bitmap.reserve(pageIndex, nPages);
1040 m_HeapPageCount += nPages;
1041 m_SlabRegionLock.release();
1043 break;
1044 }
1045#endif
1046
1047 bitmap.reserve(pageIndex, nPages);
1048 m_HeapPageCount += nPages;
1049 m_SlabRegionLock.release();
1050 break;
1051 }
1052
1053 const uintptr_t slab = m_Base + (pageIndex * getPageSize());
1054
1055#if defined(PEDIGREE_BUILDUTILS)
1056 if (m_SlabTransitionHook) {
1057 m_SlabTransitionHook(SlabTransitionForTest::Reserved, slab, m_SlabTransitionHookContext);
1058 }
1059#endif
1060
1061 for (size_t i = 0; i < nPages; ++i) {
1062 void* p = reinterpret_cast<void*>(slab + (i * getPageSize()));
1063 allocateAndMapAt(p);
1064 }
1065
1066 m_SlabRegionLock.acquire();
1067 for (size_t i = 0; i < nPages; ++i) {
1068 size_t currentPage = pageIndex + i;
1069 bitmap.setMapped(currentPage);
1070 }
1071 m_SlabRegionLock.release();
1072
1073#if defined(PEDIGREE_BUILDUTILS)
1074 if (m_SlabTransitionHook) {
1075 m_SlabTransitionHook(SlabTransitionForTest::Mapped, slab, m_SlabTransitionHookContext);
1076 }
1077#endif
1078
1079 return slab;
1080}
1081
1082void SlamAllocator::markSlabReady(uintptr_t address, size_t length) {
1083 if (length < getPageSize() || (length % getPageSize()) || (address % getPageSize()) ||
1084 address < m_Base || address >= getHeapEnd() || length > (getHeapEnd() - address)) {
1085 panic("Attempted to publish an invalid slab.");
1086 }
1087
1088 const size_t firstPage = (address - m_Base) / getPageSize();
1089 const size_t nPages = length / getPageSize();
1090 LockGuard<Spinlock> guard(m_SlabRegionLock);
1091 for (size_t i = 0; i < nPages; ++i) {
1092 const size_t currentPage = firstPage + i;
1093 const uint64_t bit = 1ULL << (currentPage % 64);
1094 if (!m_SlabRegionBitmap.isReserved(currentPage) || !m_SlabRegionBitmap.isMapped(currentPage)) {
1095 panic("Attempted to publish an unmapped slab.");
1096 }
1097 m_SlabRegionBitmap.setReady(currentPage);
1098 }
1099}
1100
1101void SlamAllocator::freeSlab(uintptr_t address, size_t length) {
1102 LockGuard<Spinlock> guard(m_SlabRegionLock);
1103
1104 freeSlabUnlocked(address, length);
1105}
1106
1107void SlamAllocator::freeSlabUnlocked(uintptr_t address, size_t length) {
1108 if (length < getPageSize() || (length % getPageSize()) || (address % getPageSize()) ||
1109 address < m_Base || address >= getHeapEnd() || length > (getHeapEnd() - address)) {
1110 panic("Attempted to free an invalid slab.");
1111 }
1112 size_t nPages = length / getPageSize();
1113 size_t firstPage = (address - m_Base) / getPageSize();
1114 SlamBitmap& bitmap = m_SlabRegionBitmap;
1115 if (firstPage >= m_SlabRegionPages || nPages > (m_SlabRegionPages - firstPage)) {
1116 panic("Attempted to free a slab outside the allocator bitmap.");
1117 }
1118
1119 for (size_t i = 0; i < nPages; ++i) {
1120 size_t currentPage = firstPage + i;
1121 const uint64_t bit = 1ULL << (currentPage % 64);
1122 if (!bitmap.isReserved(currentPage) || !bitmap.isMapped(currentPage)) {
1123 panic("Attempted to free an unallocated slab.");
1124 }
1125 }
1126
1127 // Perform unmapping first (so we can just modify 'address').
1128 for (uintptr_t base = address; base < (address + length); base += getPageSize()) {
1129 void* p = reinterpret_cast<void*>(base);
1130 unmap(p);
1131 }
1132
1133#if defined(PEDIGREE_BUILDUTILS)
1134 if (m_SlabTransitionHook) {
1135 m_SlabTransitionHook(SlabTransitionForTest::Unmapped, address, m_SlabTransitionHookContext);
1136 }
1137#endif
1138
1139 // Clear the reservation while validation remains excluded by
1140 // m_SlabRegionLock.
1141 for (size_t i = 0; i < nPages; ++i) {
1142 size_t currentPage = firstPage + i;
1143 const uint64_t bit = 1ULL << (currentPage % 64);
1144 bitmap.release(currentPage, 1);
1145 }
1146
1147 m_HeapPageCount -= nPages;
1148}
1149
1150size_t SlamAllocator::recovery(size_t maxSlabs) {
1151 size_t nSlabs = 0;
1152 size_t nPages = 0;
1153
1154 for (size_t i = 0; i < 32; ++i) {
1155 // Things without slabs don't get recovered.
1156 if (!m_Caches[i].slabSize())
1157 continue;
1158
1159 size_t thisSlabs = m_Caches[i].recovery(maxSlabs - nSlabs);
1160 nPages += (thisSlabs * m_Caches[i].slabSize()) / getPageSize();
1161 nSlabs += thisSlabs;
1162 if (nSlabs >= maxSlabs) {
1163 break;
1164 }
1165 }
1166
1167 return nPages;
1168}
1169
1170uintptr_t SlamAllocator::allocate(size_t nBytes) {
1171 EMIT_IF(!PEDIGREE_BENCHMARK) {
1172 if (!Processor::guardDeviceHardIrqOperation(DeviceHardIrqOperation::HeapAllocate)) {
1173 return 0;
1174 }
1175 }
1176
1177 EMIT_IF(HOSTED_SYSTEM_MALLOC) {
1178 FATAL_NOLOCK("SlamAllocator::allocate() called when HOSTED_SYSTEM_MALLOC == 1");
1179 }
1180
1181 EMIT_IF(DEBUGGING_SLAB_ALLOCATOR) {
1182 NOTICE_NOLOCK("SlabAllocator::allocate(" << Dec << nBytes << Hex << ")");
1183 }
1184
1186
1187 if (UNLIKELY(!m_bInitialised))
1188 initialise();
1189
1190 EMIT_IF(CRIPPLINGLY_VIGILANT) {
1191 if (m_bVigilant) {
1192 for (int i = 0; i < 32; i++) {
1193 m_Caches[i].check();
1194 }
1195 }
1196 }
1197
1198 size_t origSize = nBytes;
1199
1200 // Return value.
1201 uintptr_t ret = 0;
1202
1203 // Don't allow huge allocations.
1206 const size_t framing = sizeof(AllocHeader) + sizeof(AllocFooter);
1207 if (nBytes >= (1ULL << 31) || nBytes > ((1ULL << 31) - 1 - framing)) {
1208 ERROR("SlamAllocator: massive allocation: " << origSize);
1209 panic("SlamAllocator allocation size is too large.");
1210 }
1211
1212 nBytes += framing;
1213
1214 // Default to minimum object size if we must.
1215 size_t lg2 = 0;
1216 if (UNLIKELY(nBytes < OBJECT_MINIMUM_SIZE)) {
1217 nBytes = OBJECT_MINIMUM_SIZE;
1218 }
1219
1220 // log2 of nBytes, where nBytes is rounded up to the next power-of-two.
1221 lg2 = 32 - __builtin_clz(static_cast<unsigned int>(nBytes - 1));
1222 nBytes = 1ULL << lg2; // Round up nBytes now.
1223 ret = m_Caches[lg2].allocate();
1224
1225 EMIT_IF(WARN_PAGE_SIZE_OR_LARGER) {
1226 // Does the allocation fit inside a slab?
1227 // NOTE: use something else to allocate 4K or more.
1228 if (nBytes >= getPageSize()) {
1229#if __GNUC__
1230#pragma GCC diagnostic push
1231#pragma GCC diagnostic ignored "-Wframe-address"
1232#endif
1233 // return address of operator new()
1234 void* ret0 = __builtin_return_address(0);
1235 void* ret1 = __builtin_return_address(1);
1236 ERROR("alloc of " << origSize << " rounded to " << nBytes << " exceeds page size [at " << ret0
1237 << " " << ret1 << "]!");
1238#if __GNUC__
1239#pragma GCC diagnostic pop
1240#endif
1241 }
1242 }
1243
1244 EMIT_IF(DEBUGGING_SLAB_ALLOCATOR) {
1245 if (UNLIKELY(!ret)) {
1246 ERROR_NOLOCK("SlabAllocator::allocate: Allocation failed (" << Dec << nBytes << Hex
1247 << " bytes)");
1248 return ret;
1249 }
1250 }
1251 else {
1252 assert(ret != 0);
1253 }
1254
1255 // Shove some data on the front that we'll use later
1256 AllocHeader* head = reinterpret_cast<AllocHeader*>(ret);
1257 AllocFooter* foot = reinterpret_cast<AllocFooter*>(ret + nBytes - sizeof(AllocFooter));
1258 ret += sizeof(AllocHeader);
1259
1260 // Set up the header
1261 head->cache = &m_Caches[lg2];
1262 EMIT_IF(OVERRUN_CHECK) {
1263 head->magic = VIGILANT_MAGIC;
1264 foot->magic = VIGILANT_MAGIC;
1265
1266 EMIT_IF(VIGILANT_OVERRUN_CHECK) {
1267 // safe cast in this case as there's an inheritance here
1268 auto vigilantHead = reinterpret_cast<AllocHeader_VigilantOverrunCheck*>(head);
1269 if (Processor::m_Initialised == 2) {
1270 Backtrace bt;
1271 bt.performBpBacktrace(0, 0);
1272 MemoryCopy(&vigilantHead->backtrace, bt.m_pReturnAddresses,
1273 SLAM_BT_FRAMES * sizeof(uintptr_t));
1274 vigilantHead->requested = nBytes;
1275 g_SlamCommand.addAllocation(vigilantHead->backtrace, vigilantHead->requested);
1276 }
1277 }
1278 }
1279
1280 EMIT_IF(THREADS) {
1281 if (Processor::m_Initialised == 2) {
1282 Thread* pThread = Processor::information().getCurrentThread();
1283 if (pThread) {
1284 pThread->getParent()->trackHeap(nBytes);
1285 }
1286 }
1287 }
1288
1289 EMIT_IF(MEMORY_TRACING) {
1290 traceAllocation(reinterpret_cast<void*>(ret), MemoryTracing::Allocation, origSize);
1291 }
1292
1293 return ret;
1294}
1295
1296#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1297uintptr_t SlamAllocator::guardedAllocateForTest(size_t nBytes) {
1298 return instance().allocate(nBytes);
1299}
1300
1301void SlamAllocator::guardedFreeForTest(uintptr_t mem) {
1302 instance().free(mem);
1303}
1304#endif
1305
1306size_t SlamAllocator::allocSize(uintptr_t mem) {
1307 if (!mem)
1308 return 0;
1309
1310 // Grab the header
1311 AllocHeader* head = reinterpret_cast<AllocHeader*>(mem - sizeof(AllocHeader));
1312
1313 // If the cache is null, then the pointer is corrupted.
1314 assert(head->cache != 0);
1315 size_t result = head->cache->objectSize();
1316
1317 // Remove size of header/footer.
1318 // This is important as we're returning the size of each object itself,
1319 // but we return memory framed by headers and footers. So, the "true" size
1320 // of memory pointed to by 'mem' is not the true object size.
1321 return result - (sizeof(AllocHeader) + sizeof(AllocFooter));
1322}
1323
1324bool SlamAllocator::isAllocatedPage(uintptr_t address) const {
1325 if (!m_bInitialised || !m_SlabRegionPages || address < m_Base || address >= getHeapEnd()) {
1326 return false;
1327 }
1328
1329 size_t page = (address - m_Base) / getPageSize();
1330 if (page >= m_SlabRegionPages) {
1331 return false;
1332 }
1333
1334 const uint64_t bit = 1ULL << (page % 64);
1335 return m_SlabRegionBitmap.isReserved(page) && m_SlabRegionBitmap.isMapped(page) &&
1336 m_SlabRegionBitmap.isReady(page);
1337}
1338
1339#if defined(PEDIGREE_BUILDUTILS)
1340void SlamAllocator::setSlabTransitionHookForTest(SlabTransitionHookForTest hook, void* context) {
1341 LockGuard<Spinlock> guard(m_SlabRegionLock);
1342 m_SlabTransitionHook = hook;
1343 m_SlabTransitionHookContext = context;
1344}
1345#endif
1346
1347SlamAllocator& SlamAllocator::instance() {
1348 EMIT_IF(PEDIGREE_BENCHMARK) {
1349 static SlamAllocator instance;
1350 return instance;
1351 }
1352 else {
1353 return m_Instance;
1354 }
1355}
1356
1357void SlamAllocator::free(uintptr_t mem) {
1358 EMIT_IF(!PEDIGREE_BENCHMARK) {
1359 if (!Processor::guardDeviceHardIrqOperation(DeviceHardIrqOperation::HeapFree)) {
1360 return;
1361 }
1362 }
1363
1364#if DEBUGGING_SLAB_ALLOCATOR
1365 NOTICE_NOLOCK("SlabAllocator::free");
1366#endif
1367
1368#if SLAM_LOCKED
1369 LockGuard<Spinlock> guard(m_Lock);
1370#endif
1371
1372 // If we're not initialised, fix that
1373 if (UNLIKELY(!m_bInitialised))
1374 initialise();
1375 if (UNLIKELY(!mem))
1376 return;
1377
1378#if CRIPPLINGLY_VIGILANT
1379 if (m_bVigilant)
1380 for (int i = 0; i < 32; i++)
1381 m_Caches[i].check();
1382#endif
1383
1384// Ensure this pointer is even on the heap...
1385#if !PEDIGREE_BENCHMARK
1386 if (!Processor::information().getVirtualAddressSpace().memIsInKernelHeap(
1387 reinterpret_cast<void*>(mem)))
1388 FATAL_NOLOCK("SlamAllocator::free - given pointer '" << mem << "' was completely invalid.");
1389#endif
1390
1391 // Grab the header
1392 AllocHeader* head = reinterpret_cast<AllocHeader*>(mem - sizeof(AllocHeader));
1393
1394 // If the cache is null, then the pointer is corrupted.
1395 assert(head->cache != 0);
1396#if OVERRUN_CHECK
1397 assert(head->magic == VIGILANT_MAGIC);
1398 // Footer gets checked in SlamCache::free, as we don't know the object size.
1399
1400#if VIGILANT_OVERRUN_CHECK
1401 if (Processor::m_Initialised == 2)
1402 g_SlamCommand.removeAllocation(head->backtrace, head->requested);
1403#endif
1404#endif
1405
1406 SlamCache* pCache = head->cache;
1407
1408// Scribble the freed buffer (both to avoid leaking information, and also
1409// to ensure anything using a freed object will absolutely fail).
1410#if SCRIBBLE_FREED_BLOCKS
1411 size_t size = pCache->objectSize() - sizeof(AllocHeader) - sizeof(AllocFooter);
1412 ByteSet(reinterpret_cast<void*>(mem), 0xAB, size);
1413#endif
1414
1415#if THREADS
1416 if (Processor::m_Initialised == 2) {
1417 Thread* pThread = Processor::information().getCurrentThread();
1418 if (pThread) {
1419 pThread->getParent()->trackHeap(-pCache->objectSize());
1420 }
1421 }
1422#endif
1423
1424 // Free now.
1425 pCache->free(mem - sizeof(AllocHeader));
1426
1427#if MEMORY_TRACING
1428 traceAllocation(reinterpret_cast<void*>(mem), MemoryTracing::Free, 0);
1429#endif
1430}
1431
1432bool SlamAllocator::isPointerValid(uintptr_t mem)
1433#if !SLAM_LOCKED
1434 const
1435#endif
1436{
1437#if DEBUGGING_SLAB_ALLOCATOR
1438 NOTICE_NOLOCK("SlabAllocator::isPointerValid");
1439#endif
1440
1441#if SLAM_LOCKED
1442 LockGuard<Spinlock> guard(m_Lock);
1443#endif
1444
1445 // Pin the slab mapping until all header, cache, and footer reads complete.
1446 // freeSlab takes the same lock across unmapping and bitmap retirement.
1447 LockGuard<Spinlock> slabGuard(m_SlabRegionLock);
1448
1449 // If we're not initialised, fix that
1450 if (UNLIKELY(!m_bInitialised)) {
1451 return false;
1452 }
1453
1454 // 0 is fine to free.
1455 if (!mem) {
1456 return true;
1457 }
1458
1459// On the heap?
1460#if !PEDIGREE_BENCHMARK
1461 if (!Processor::information().getVirtualAddressSpace().memIsInKernelHeap(
1462 reinterpret_cast<void*>(mem))) {
1463#if VERBOSE_ISPOINTERVALID
1464 WARNING("SlamAllocator::isPointerValid: memory " << Hex << mem
1465 << " is not in the heap region.");
1466#endif
1467 return false;
1468 }
1469#endif
1470
1471 if (mem < (m_Base + sizeof(AllocHeader))) {
1472 return false;
1473 }
1474
1475 uintptr_t headerAddress = mem - sizeof(AllocHeader);
1476 if (!isAllocatedPage(headerAddress)) {
1477 return false;
1478 }
1479
1480#if CRIPPLINGLY_VIGILANT
1481 if (m_bVigilant)
1482 for (int i = 0; i < 32; i++)
1483 m_Caches[i].check();
1484#endif
1485
1486 // Grab the header
1487 AllocHeader* head = reinterpret_cast<AllocHeader*>(headerAddress);
1488
1489#if OVERRUN_CHECK
1490 if (head->magic != VIGILANT_MAGIC) {
1491#if VERBOSE_ISPOINTERVALID
1492 WARNING("SlamAllocator::isPointerValid: memory " << Hex << mem << " failed magic check ("
1493 << head->magic << " != " << VIGILANT_MAGIC
1494 << ").");
1495#endif
1496 return false;
1497 }
1498// Footer gets checked in SlamCache::free, as we don't know the object size.
1499#endif
1500
1501 // If the cache is null, then the pointer is corrupted.
1502 if (head->cache == 0) {
1503#if VERBOSE_ISPOINTERVALID
1504 WARNING("SlamAllocator::isPointerValid: memory " << Hex << mem
1505 << " does not reference a valid SlamCache.");
1506#endif
1507 return false;
1508 }
1509
1510 // Check for a valid cache
1511 bool bValid = false;
1512 for (int i = 0; i < 32; i++) {
1513 if (head->cache == &m_Caches[i]) {
1514 bValid = true;
1515 break;
1516 }
1517 }
1518
1519 if (!bValid) {
1520 WARNING_NOLOCK("SlamAllocator::isPointerValid - cache pointer '"
1521 << reinterpret_cast<uintptr_t>(head->cache) << "' is invalid.");
1522 return false;
1523 }
1524
1525 // Final validation.
1526 return head->cache->isPointerValid(mem - sizeof(AllocHeader));
1527}
1528
1529bool SlamAllocator::isWithinHeap(uintptr_t mem) const {
1530#if !PEDIGREE_BENCHMARK
1531 if (!Processor::information().getVirtualAddressSpace().memIsInKernelHeap(
1532 reinterpret_cast<void*>(mem))) {
1533#if VERBOSE_ISPOINTERVALID
1534 WARNING("SlamAllocator::isWithinHeap: memory " << Hex << mem << " is not in the heap region.");
1535#endif
1536 return false;
1537 }
1538#endif
1539
1540 return true;
1541}
1542
1543bool _assert_ptr_valid(uintptr_t ptr) {
1544 return SlamAllocator::instance().isPointerValid(ptr);
1545}
1546
1547#endif // !SLAM_USE_DEBUG_ALLOCATOR
uintptr_t m_pReturnAddresses[MAX_STACK_FRAMES]
Definition Backtrace.h:84
void performBpBacktrace(uintptr_t base, uintptr_t instruction)
Definition Backtrace.cc:82
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
virtual void pin(physical_uintptr_t page)=0
static ProcessorId id()
static ProcessorInformation & information()
static void pause()
static size_t m_Initialised
Definition Processor.h:483
static bool guardDeviceHardIrqOperation(DeviceHardIrqOperation operation)
Definition Processor.h:585
void markSlabReady(uintptr_t address, size_t length)
void freeSlabUnlocked(uintptr_t address, size_t length)
bool isAllocatedPage(uintptr_t address) const
uintptr_t allocate(size_t nBytes)
void free(uintptr_t object)
virtual ~SlamCache()
bool isPointerValid(uintptr_t object) const
Spinlock m_RecoveryLock
void initialise(SlamAllocator *parent, size_t objectSize)
size_t recovery(size_t maxSlabs)
uintptr_t allocate()
SlamAllocator * m_pParentAllocator
void release()
Definition Spinlock.cc:168
bool acquire(bool recurse=false, bool safe=true)
Definition Spinlock.cc:36
static constexpr size_t getPageSize() noexcept
Definition TargetInfo.h:40
Process * getParent() const
Definition Thread.h:340
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
virtual uintptr_t getKernelHeapStart() const =0
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 uintptr_t getKernelHeapEnd() const =0
virtual void unmap(void *virtualAddress)=0
void EXPORTED_PUBLIC panic(const char *msg) NORETURN
Definition panic.cc:118
@ Dec
Definition Log.h:126
@ Hex
Definition Log.h:124
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition utils.h:39
Definition mem.c:283