20#if !SLAM_USE_DEBUG_ALLOCATOR
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"
39#if X64 && !PEDIGREE_BENCHMARK
40#include "system/kernel/core/processor/x64/utils.h"
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
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
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;
62static constexpr uintptr_t POINTER_MASK = 0x0000FFFFFFFFFFFFULL;
63static constexpr uintptr_t POINTER_TAG_MASK = ~POINTER_MASK;
64static constexpr uintptr_t POINTER_TAG_INCREMENT = 0x0001000000000000ULL;
68inline T* untagged(T* p)
PURE;
71inline T* tagged(T* p)
PURE;
74inline T* next_tag(T* p, T* currentHead)
PURE;
77inline T* untagged(T* p) {
83 uintptr_t ptr =
reinterpret_cast<uintptr_t
>(p);
84 EMIT_IF(PEDIGREE_BENCHMARK || HOSTED) {
90 ptr |= 0xFFFF000000000000ULL;
92 return reinterpret_cast<T*
>(ptr);
97inline T* tagged(T* p) {
101 uintptr_t ptr =
reinterpret_cast<uintptr_t
>(p);
103 return reinterpret_cast<T*
>(ptr);
108inline T* next_tag(T* p, T* currentHead) {
113 uintptr_t ptr =
reinterpret_cast<uintptr_t
>(p) & POINTER_MASK;
115 (
reinterpret_cast<uintptr_t
>(currentHead) + POINTER_TAG_INCREMENT) & POINTER_TAG_MASK;
116 return reinterpret_cast<T*
>(ptr | tag);
120inline void spin_pause() {
121#if PEDIGREE_BENCHMARK
122#if defined(__i386__) || defined(__x86_64__)
124#elif defined(__aarch64__)
132inline uintptr_t getHeapBase() {
133#if PEDIGREE_BENCHMARK
134 return SlamSupport::getHeapBase();
140inline uintptr_t getHeapEnd() {
141#if PEDIGREE_BENCHMARK
142 return SlamSupport::getHeapEnd();
148inline size_t getPageSize() {
149#if PEDIGREE_BENCHMARK
156inline void allocateAndMapAt(
void* addr,
bool cowOk =
false) {
157#if PEDIGREE_BENCHMARK
158 SlamSupport::getPageAt(addr);
162 static physical_uintptr_t physZero = 0;
163 bool needZeroPage =
false;
164 size_t extraFlags = 0;
166 physical_uintptr_t phys = 0;
191 if (!va.
map(phys, addr, standardFlags | extraFlags)) {
192 FATAL(
"SlamAllocator: failed to allocate and map at " << addr);
208inline void unmap(
void* addr) {
209#if PEDIGREE_BENCHMARK
210 SlamSupport::unmapPage(addr);
217 physical_uintptr_t phys;
229 m_LargeFreeList(nullptr),
232 m_SlabObjectOffset(0),
233 m_SlabObjectCount(0),
236 m_RecoveryLock(false, true)
247 if (objectSize < OBJECT_MINIMUM_SIZE)
250 m_ObjectSize = objectSize;
251 if (m_ObjectSize > SLAB_MINIMUM_SIZE)
252 m_SlabSize = m_ObjectSize;
254 m_SlabSize = SLAB_MINIMUM_SIZE;
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;
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;
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);
278size_t SlamCache::currentList()
const {
279#if defined(PEDIGREE_BUILDUTILS)
286 assert(list < NUM_LISTS);
291#if defined(PEDIGREE_BUILDUTILS)
292void SlamCache::setListForTest(
size_t list) {
293 assert(list < NUM_LISTS);
298void SlamCache::addSlab(Slab* slab,
size_t list) {
299 assert(!slab->onList);
301 slab->previous =
nullptr;
302 slab->next = m_PartialLists[list];
304 slab->next->previous = slab;
305 m_PartialLists[list] = slab;
309void SlamCache::removeSlab(Slab* slab) {
310 assert(slab->onList);
312 slab->previous->next = slab->next;
314 m_PartialLists[slab->list] = slab->next;
316 slab->next->previous = slab->previous;
317 slab->next =
nullptr;
318 slab->previous =
nullptr;
319 slab->onList =
false;
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,
333void SlamCache::endFastPath() {
334 __atomic_fetch_sub(&m_FastPathState,
static_cast<size_t>(1), __ATOMIC_RELEASE);
340 Node* head = slab->freeHead;
342 slab->freeHead = head->next;
343 __atomic_fetch_sub(&slab->freeObjects,
static_cast<size_t>(1), __ATOMIC_ACQ_REL);
347 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
350 Node* taggedHead = __atomic_load_n(&slab->freeHead, ATOMIC_POP_MEMORY_ORDER);
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);
361 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
369void SlamCache::pushFreeObject(Slab* slab, Node* node) {
372 node->next = slab->freeHead;
373 slab->freeHead = node;
374 __atomic_fetch_add(&slab->freeObjects,
static_cast<size_t>(1), __ATOMIC_RELEASE);
376 Node* head = __atomic_load_n(&slab->freeHead, __ATOMIC_RELAXED);
379 }
while (!__atomic_compare_exchange_n(&slab->freeHead, &head, next_tag(node, head),
380 ATOMIC_CAS_WEAK, ATOMIC_PUSH_MEMORY_ORDER,
382 __atomic_fetch_add(&slab->freeObjects,
static_cast<size_t>(1), __ATOMIC_RELEASE);
386SlamCache::Node* SlamCache::objectAt(uintptr_t slab,
size_t index)
const {
387 return reinterpret_cast<Node*
>(slab + m_SlabObjectOffset + (index * m_ObjectSize));
391 assert(m_ObjectSize < getPageSize());
392 return reinterpret_cast<Slab*
>(
object & ~(getPageSize() - 1));
396 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
400 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
401 if (m_ObjectSize >= getPageSize()) {
407 if (m_ObjectSize >= getPageSize()) {
410 if (m_LargeFreeList) {
411 Node* node = m_LargeFreeList;
412 m_LargeFreeList = node->next;
414 assert(node->magic == MAGIC_VALUE);
415 node->magic = TEMP_MAGIC;
418 return reinterpret_cast<uintptr_t
>(node);
421 return reinterpret_cast<uintptr_t
>(initialiseSlab(getSlab()));
424 const size_t thisList = currentList();
426 Slab* fastSlab =
nullptr;
427 const bool fastPath = beginFastPath();
429 fastSlab = __atomic_load_n(&m_FastSlabs[thisList], __ATOMIC_ACQUIRE);
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);
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);
445 assert(N->next !=
reinterpret_cast<Node*
>(VIGILANT_MAGIC));
447 assert(N->magic == TEMP_MAGIC || N->magic == MAGIC_VALUE);
448 N->magic = TEMP_MAGIC;
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))
459 for (
Slab* slab = m_PartialLists[list]; slab; slab = slab->next) {
460 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
462 N = popFreeObject(slab);
465 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE))
467 else if (list == thisList)
468 __atomic_store_n(&m_FastSlabs[thisList], slab, __ATOMIC_RELEASE);
477 assert(N->next !=
reinterpret_cast<Node*
>(VIGILANT_MAGIC));
479 assert(N->magic == TEMP_MAGIC || N->magic == MAGIC_VALUE);
480 N->magic = TEMP_MAGIC;
483 header->cache =
this;
490 Node* pNode = initialiseSlab(getSlab());
491 uintptr_t slab =
reinterpret_cast<uintptr_t
>(pNode);
492 EMIT_IF(CRIPPLINGLY_VIGILANT) {
499 return reinterpret_cast<uintptr_t
>(N);
503 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
505 size_t numPages = m_SlabSize / getPageSize();
506 if (m_SlabSize % getPageSize()) {
509 object =
object & ~(getPageSize() - 1);
510 for (
size_t i = 0; i < numPages; ++i) {
511 unmap(
reinterpret_cast<void*
>(
object + (i * getPageSize())));
517 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
518 if (m_ObjectSize >= getPageSize()) {
525 Node* N =
reinterpret_cast<Node*
>(object);
531 assert(pFoot->magic == VIGILANT_MAGIC);
535 assert(pHeader->cache ==
this);
536 pHeader->cache =
nullptr;
540 assert(N->magic != MAGIC_VALUE);
541 N->magic = MAGIC_VALUE;
544 if (m_ObjectSize >= getPageSize()) {
546 N->next = m_LargeFreeList;
551 Slab* slab = slabForObject(
object);
552 const bool fastPath = beginFastPath();
554 if (__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
555 pushFreeObject(slab, N);
564 pushFreeObject(slab, N);
566 addSlab(slab, slab->list);
567 if (slab->list == currentList())
568 __atomic_store_n(&m_FastSlabs[slab->list], slab, __ATOMIC_RELEASE);
572 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
574 if (!__atomic_load_n(&slab->freeObjects, __ATOMIC_ACQUIRE)) {
575 pushFreeObject(slab, N);
577 addSlab(slab, slab->list);
578 if (slab->list == currentList())
579 __atomic_store_n(&m_FastSlabs[slab->list], slab, __ATOMIC_RELEASE);
583 pushFreeObject(slab, N);
587 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
588 if (m_ObjectSize >= getPageSize()) {
594 Node* N =
reinterpret_cast<Node*
>(object);
599 if (pFoot->magic != VIGILANT_MAGIC) {
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 <<
").");
618uintptr_t SlamCache::getSlab() {
622void SlamCache::freeSlab(uintptr_t slab) {
631 EMIT_IF(EVERY_ALLOCATION_IS_A_SLAB) {
635 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
636 if (m_ObjectSize >= getPageSize()) {
642 constexpr size_t writer =
static_cast<size_t>(1) << ((
sizeof(
size_t) * 8) - 1);
645 expected = __atomic_load_n(&m_FastPathState, __ATOMIC_ACQUIRE);
646 if (expected & writer) {
650 const size_t requested = expected | writer;
651 if (__atomic_compare_exchange_n(&m_FastPathState, &expected, requested,
false, __ATOMIC_ACQUIRE,
655 while (__atomic_load_n(&m_FastPathState, __ATOMIC_ACQUIRE) != writer)
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),
671 freeSlab(
reinterpret_cast<uintptr_t
>(slab));
678 while (m_LargeFreeList && freedSlabs < maxSlabs) {
679 Node* node = m_LargeFreeList;
680 m_LargeFreeList = node->next;
681 freeSlab(
reinterpret_cast<uintptr_t
>(node));
686 __atomic_store_n(&m_FastPathState,
static_cast<size_t>(0), __ATOMIC_RELEASE);
691 EMIT_IF(SLABS_FOR_HUGE_ALLOCS) {
692 if (m_ObjectSize >= getPageSize()) {
697 if (m_ObjectSize >= getPageSize()) {
700 reinterpret_cast<Node*
>(slab)->magic = TEMP_MAGIC;
704 return reinterpret_cast<Node*
>(slab);
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;
717 Node* N = objectAt(slab, 0);
719 N->magic = TEMP_MAGIC;
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);
726 pNode->magic = MAGIC_VALUE;
735 slabState->list = currentList();
736 if (slabState->freeObjects)
737 addSlab(slabState, slabState->list);
738 __atomic_store_n(&m_FastSlabs[slabState->list], slabState, __ATOMIC_RELEASE);
747void SlamCache::check() {
748 if (m_ObjectSize >= getPageSize()) {
756 if (m_ObjectSize == 0)
760 size_t nObjects = m_SlabObjectCount;
762 size_t maxPerSlab = (m_SlabSize /
sizeof(uintptr_t)) - 2;
764 uintptr_t curSlab = m_FirstSlab;
770 uintptr_t numAlloced = *
reinterpret_cast<uintptr_t*
>(curSlab);
771 uintptr_t next = *
reinterpret_cast<uintptr_t*
>(curSlab +
sizeof(uintptr_t));
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)
784 if (pHead->magic != VIGILANT_MAGIC) {
785 ERROR(
"Possible heap underrun: object starts at "
786 << addr <<
", size: " << m_ObjectSize
789 if (pFoot->magic != VIGILANT_MAGIC) {
790 ERROR(
"Possible heap overrun: object starts at " << addr);
795 if (numAlloced == maxPerSlab)
803void SlamCache::trackSlab(uintptr_t slab) {
808 if (m_ObjectSize == 0)
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));
819 size_t maxPerSlab = (m_SlabSize /
sizeof(uintptr_t)) - 2;
821 uintptr_t curSlab = m_FirstSlab;
823 uintptr_t* numAlloced =
reinterpret_cast<uintptr_t*
>(curSlab);
824 uintptr_t* next =
reinterpret_cast<uintptr_t*
>(curSlab +
sizeof(uintptr_t));
826 if (*numAlloced < maxPerSlab) {
827 uintptr_t* p =
reinterpret_cast<uintptr_t*
>(curSlab + (*numAlloced + 2) *
sizeof(uintptr_t));
829 *numAlloced = *numAlloced + 1;
836 uintptr_t newSlab = getSlab();
840 uintptr_t* numAlloced =
reinterpret_cast<uintptr_t*
>(curSlab);
841 uintptr_t* next =
reinterpret_cast<uintptr_t*
>(curSlab +
sizeof(uintptr_t));
848SlamAllocator::SlamAllocator()
849 : m_bInitialised(false),
851 m_SlabRegionLock(false, true),
853 m_SlabRegionBitmap(),
854 m_SlabRegionBitmapEntries(0),
855 m_SlabRegionPages(0),
858SlamAllocator::~SlamAllocator() {
859 if (m_bInitialised) {
864void SlamAllocator::initialise() {
867 if (m_bInitialised) {
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;
879 if (bitmapBytes & (getPageSize() - 1)) {
880 bitmapBytes &= ~(getPageSize() - 1);
881 bitmapBytes += getPageSize();
887 m_SlabRegionBitmap.useMemory(
reinterpret_cast<void*
>(bitmapBase), (heapPages + 63) / 64,
889 m_Base = bitmapBase + bitmapBytes;
890 m_SlabRegionPages = (heapEnd - m_Base) / getPageSize();
891 m_SlabRegionBitmapEntries = (m_SlabRegionPages + 63) / 64;
895 for (uintptr_t addr = bitmapBase; addr < m_Base; addr += getPageSize()) {
898 bool cowOk = numPages++ >= 32;
899 allocateAndMapAt(
reinterpret_cast<void*
>(addr), cowOk);
901 ByteSet(
reinterpret_cast<void*
>(addr), 0, getPageSize());
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");
911 for (
size_t i = 0; i < 32; i++) {
915 NOTICE(
"all caches init-ed");
917 m_bInitialised =
true;
920void SlamAllocator::clearAll() {
928 if (!m_bInitialised) {
932 if (!m_SlabRegionPages) {
938 m_bInitialised =
false;
941 for (
size_t entry = 0; entry < m_SlabRegionBitmapEntries; ++entry) {
942 if (!m_SlabRegionBitmap.reservedBits(entry)) {
946 for (
size_t bit = 0; bit < 64; ++bit) {
947 uint64_t test = 1ULL << bit;
948 if ((m_SlabRegionBitmap.reservedBits(entry) & test) == 0) {
952 uintptr_t slab = m_Base + (((entry * 64) + bit) * getPageSize());
958 m_SlabRegionBitmap.useMemory(
nullptr, 0, 0);
959 m_SlabRegionBitmapEntries = 0;
960 m_SlabRegionPages = 0;
963 for (uintptr_t addr = getHeapBase(); addr < m_Base; addr += getPageSize()) {
964 unmap(
reinterpret_cast<void*
>(addr));
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.");
972 size_t nPages = fullSize / getPageSize();
976 auto findFreeRun = [&]() {
return bitmap.findFreeRun(nPages); };
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;
989 va.
getMapping(
reinterpret_cast<void*
>(address), physical, flags);
994 return static_cast<uintptr_t
>(0);
998 size_t pageIndex = ~0UL;
1001 pageIndex = findFreeRun();
1002 if (pageIndex == ~0UL) {
1004 panic(
"SlamAllocator cannot find contiguous virtual heap space.");
1007#if X64 && !PEDIGREE_BENCHMARK
1008 if (firstCowBitmapPage(pageIndex)) {
1013 pageIndex = findFreeRun();
1014 if (pageIndex == ~0UL) {
1017 panic(
"SlamAllocator cannot find contiguous virtual heap space.");
1020 const uintptr_t cowAddress = firstCowBitmapPage(pageIndex);
1023 physical_uintptr_t oldPhysical = 0;
1025 va.
getMapping(
reinterpret_cast<void*
>(cowAddress), oldPhysical, flags);
1027 reinterpret_cast<void*
>(cowAddress), getPageSize());
1028 va.
unmap(
reinterpret_cast<void*
>(cowAddress));
1031 if (!va.
map(replacement,
reinterpret_cast<void*
>(cowAddress), flags)) {
1032 panic(
"SlamAllocator could not privatise bitmap metadata.");
1039 bitmap.reserve(pageIndex, nPages);
1040 m_HeapPageCount += nPages;
1047 bitmap.reserve(pageIndex, nPages);
1048 m_HeapPageCount += nPages;
1053 const uintptr_t slab = m_Base + (pageIndex * getPageSize());
1055#if defined(PEDIGREE_BUILDUTILS)
1056 if (m_SlabTransitionHook) {
1057 m_SlabTransitionHook(SlabTransitionForTest::Reserved, slab, m_SlabTransitionHookContext);
1061 for (
size_t i = 0; i < nPages; ++i) {
1062 void* p =
reinterpret_cast<void*
>(slab + (i * getPageSize()));
1063 allocateAndMapAt(p);
1067 for (
size_t i = 0; i < nPages; ++i) {
1068 size_t currentPage = pageIndex + i;
1069 bitmap.setMapped(currentPage);
1073#if defined(PEDIGREE_BUILDUTILS)
1074 if (m_SlabTransitionHook) {
1075 m_SlabTransitionHook(SlabTransitionForTest::Mapped, slab, m_SlabTransitionHookContext);
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.");
1088 const size_t firstPage = (address - m_Base) / getPageSize();
1089 const size_t nPages = length / getPageSize();
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.");
1097 m_SlabRegionBitmap.setReady(currentPage);
1101void SlamAllocator::freeSlab(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.");
1112 size_t nPages = length / getPageSize();
1113 size_t firstPage = (address - m_Base) / getPageSize();
1115 if (firstPage >= m_SlabRegionPages || nPages > (m_SlabRegionPages - firstPage)) {
1116 panic(
"Attempted to free a slab outside the allocator bitmap.");
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.");
1128 for (uintptr_t base = address; base < (address + length); base += getPageSize()) {
1129 void* p =
reinterpret_cast<void*
>(base);
1133#if defined(PEDIGREE_BUILDUTILS)
1134 if (m_SlabTransitionHook) {
1135 m_SlabTransitionHook(SlabTransitionForTest::Unmapped, address, m_SlabTransitionHookContext);
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);
1147 m_HeapPageCount -= nPages;
1150size_t SlamAllocator::recovery(
size_t maxSlabs) {
1154 for (
size_t i = 0; i < 32; ++i) {
1156 if (!m_Caches[i].slabSize())
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) {
1171 EMIT_IF(!PEDIGREE_BENCHMARK) {
1177 EMIT_IF(HOSTED_SYSTEM_MALLOC) {
1178 FATAL_NOLOCK(
"SlamAllocator::allocate() called when HOSTED_SYSTEM_MALLOC == 1");
1181 EMIT_IF(DEBUGGING_SLAB_ALLOCATOR) {
1182 NOTICE_NOLOCK(
"SlabAllocator::allocate(" <<
Dec << nBytes <<
Hex <<
")");
1190 EMIT_IF(CRIPPLINGLY_VIGILANT) {
1192 for (
int i = 0; i < 32; i++) {
1193 m_Caches[i].check();
1198 size_t origSize = nBytes;
1207 if (nBytes >= (1ULL << 31) || nBytes > ((1ULL << 31) - 1 - framing)) {
1208 ERROR(
"SlamAllocator: massive allocation: " << origSize);
1209 panic(
"SlamAllocator allocation size is too large.");
1216 if (
UNLIKELY(nBytes < OBJECT_MINIMUM_SIZE)) {
1217 nBytes = OBJECT_MINIMUM_SIZE;
1221 lg2 = 32 - __builtin_clz(
static_cast<unsigned int>(nBytes - 1));
1222 nBytes = 1ULL << lg2;
1225 EMIT_IF(WARN_PAGE_SIZE_OR_LARGER) {
1228 if (nBytes >= getPageSize()) {
1230#pragma GCC diagnostic push
1231#pragma GCC diagnostic ignored "-Wframe-address"
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 <<
"]!");
1239#pragma GCC diagnostic pop
1244 EMIT_IF(DEBUGGING_SLAB_ALLOCATOR) {
1246 ERROR_NOLOCK(
"SlabAllocator::allocate: Allocation failed (" <<
Dec << nBytes <<
Hex
1261 head->cache = &m_Caches[lg2];
1263 head->magic = VIGILANT_MAGIC;
1264 foot->magic = VIGILANT_MAGIC;
1266 EMIT_IF(VIGILANT_OVERRUN_CHECK) {
1273 SLAM_BT_FRAMES *
sizeof(uintptr_t));
1274 vigilantHead->requested = nBytes;
1275 g_SlamCommand.addAllocation(vigilantHead->backtrace, vigilantHead->requested);
1284 pThread->
getParent()->trackHeap(nBytes);
1290 traceAllocation(
reinterpret_cast<void*
>(ret), MemoryTracing::Allocation, origSize);
1296#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1297uintptr_t SlamAllocator::guardedAllocateForTest(
size_t nBytes) {
1298 return instance().
allocate(nBytes);
1301void SlamAllocator::guardedFreeForTest(uintptr_t
mem) {
1302 instance().free(
mem);
1306size_t SlamAllocator::allocSize(uintptr_t
mem) {
1311 AllocHeader* head =
reinterpret_cast<AllocHeader*
>(
mem -
sizeof(AllocHeader));
1314 assert(head->cache != 0);
1315 size_t result = head->cache->objectSize();
1321 return result - (
sizeof(AllocHeader) +
sizeof(AllocFooter));
1325 if (!m_bInitialised || !m_SlabRegionPages || address < m_Base || address >= getHeapEnd()) {
1329 size_t page = (address - m_Base) / getPageSize();
1330 if (page >= m_SlabRegionPages) {
1334 const uint64_t bit = 1ULL << (page % 64);
1335 return m_SlabRegionBitmap.isReserved(page) && m_SlabRegionBitmap.isMapped(page) &&
1336 m_SlabRegionBitmap.isReady(page);
1339#if defined(PEDIGREE_BUILDUTILS)
1340void SlamAllocator::setSlabTransitionHookForTest(SlabTransitionHookForTest hook,
void* context) {
1342 m_SlabTransitionHook = hook;
1343 m_SlabTransitionHookContext = context;
1357void SlamAllocator::free(uintptr_t
mem) {
1358 EMIT_IF(!PEDIGREE_BENCHMARK) {
1364#if DEBUGGING_SLAB_ALLOCATOR
1365 NOTICE_NOLOCK(
"SlabAllocator::free");
1378#if CRIPPLINGLY_VIGILANT
1380 for (
int i = 0; i < 32; i++)
1381 m_Caches[i].check();
1385#if !PEDIGREE_BENCHMARK
1387 reinterpret_cast<void*
>(
mem)))
1388 FATAL_NOLOCK(
"SlamAllocator::free - given pointer '" <<
mem <<
"' was completely invalid.");
1392 AllocHeader* head =
reinterpret_cast<AllocHeader*
>(
mem -
sizeof(AllocHeader));
1395 assert(head->cache != 0);
1397 assert(head->magic == VIGILANT_MAGIC);
1400#if VIGILANT_OVERRUN_CHECK
1402 g_SlamCommand.removeAllocation(head->backtrace, head->requested);
1410#if SCRIBBLE_FREED_BLOCKS
1411 size_t size = pCache->objectSize() -
sizeof(AllocHeader) -
sizeof(AllocFooter);
1412 ByteSet(
reinterpret_cast<void*
>(
mem), 0xAB, size);
1419 pThread->
getParent()->trackHeap(-pCache->objectSize());
1425 pCache->
free(
mem -
sizeof(AllocHeader));
1428 traceAllocation(
reinterpret_cast<void*
>(
mem), MemoryTracing::Free, 0);
1432bool SlamAllocator::isPointerValid(uintptr_t
mem)
1437#if DEBUGGING_SLAB_ALLOCATOR
1438 NOTICE_NOLOCK(
"SlabAllocator::isPointerValid");
1460#if !PEDIGREE_BENCHMARK
1462 reinterpret_cast<void*
>(
mem))) {
1463#if VERBOSE_ISPOINTERVALID
1464 WARNING(
"SlamAllocator::isPointerValid: memory " <<
Hex <<
mem
1465 <<
" is not in the heap region.");
1471 if (
mem < (m_Base +
sizeof(AllocHeader))) {
1475 uintptr_t headerAddress =
mem -
sizeof(AllocHeader);
1476 if (!isAllocatedPage(headerAddress)) {
1480#if CRIPPLINGLY_VIGILANT
1482 for (
int i = 0; i < 32; i++)
1483 m_Caches[i].check();
1487 AllocHeader* head =
reinterpret_cast<AllocHeader*
>(headerAddress);
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
1502 if (head->cache == 0) {
1503#if VERBOSE_ISPOINTERVALID
1504 WARNING(
"SlamAllocator::isPointerValid: memory " <<
Hex <<
mem
1505 <<
" does not reference a valid SlamCache.");
1511 bool bValid =
false;
1512 for (
int i = 0; i < 32; i++) {
1513 if (head->cache == &m_Caches[i]) {
1520 WARNING_NOLOCK(
"SlamAllocator::isPointerValid - cache pointer '"
1521 <<
reinterpret_cast<uintptr_t
>(head->cache) <<
"' is invalid.");
1526 return head->cache->isPointerValid(
mem -
sizeof(AllocHeader));
1529bool SlamAllocator::isWithinHeap(uintptr_t
mem)
const {
1530#if !PEDIGREE_BENCHMARK
1532 reinterpret_cast<void*
>(
mem))) {
1533#if VERBOSE_ISPOINTERVALID
1534 WARNING(
"SlamAllocator::isWithinHeap: memory " <<
Hex <<
mem <<
" is not in the heap region.");
1543bool _assert_ptr_valid(uintptr_t ptr) {
1544 return SlamAllocator::instance().isPointerValid(ptr);
uintptr_t m_pReturnAddresses[MAX_STACK_FRAMES]
void performBpBacktrace(uintptr_t base, uintptr_t instruction)
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
static constexpr size_t getPageSize() PURE
virtual void freePage(physical_uintptr_t page)=0
virtual void pin(physical_uintptr_t page)=0
static ProcessorInformation & information()
static size_t m_Initialised
static bool guardDeviceHardIrqOperation(DeviceHardIrqOperation operation)
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)
bool isPointerValid(uintptr_t object) const
void initialise(SlamAllocator *parent, size_t objectSize)
size_t recovery(size_t maxSlabs)
SlamAllocator * m_pParentAllocator
bool acquire(bool recurse=false, bool safe=true)
static constexpr size_t getPageSize() noexcept
Process * getParent() const
virtual void setFlags(void *virtualAddress, size_t newFlags)=0
static const size_t CopyOnWrite
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
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t Write
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
uintptr_t physicalAddress(physical_uintptr_t address) PURE