20#include "pedigree/kernel/LockGuard.h"
21#include "pedigree/kernel/Log.h"
22#include "pedigree/kernel/Metrics.h"
23#include "pedigree/kernel/TargetInfo.h"
24#include "pedigree/kernel/machine/Machine.h"
25#include "pedigree/kernel/machine/Timer.h"
26#include "pedigree/kernel/process/MemoryPressureManager.h"
27#include "pedigree/kernel/process/TerminationDeferral.h"
28#include "pedigree/kernel/processor/PhysicalMemoryManager.h"
29#include "pedigree/kernel/processor/VirtualAddressSpace.h"
30#include "pedigree/kernel/utilities/Cache.h"
31#include "pedigree/kernel/utilities/Iterator.h"
32#include "pedigree/kernel/utilities/Vector.h"
33#include "pedigree/kernel/utilities/assert.h"
34#include "pedigree/kernel/utilities/utility.h"
37#include "pedigree/kernel/process/Scheduler.h"
38#include "pedigree/kernel/process/Thread.h"
39#include "pedigree/kernel/processor/Processor.h"
40#include "pedigree/kernel/processor/ProcessorInformation.h"
43#include "pedigree/kernel/utilities/smhasher/MurmurHash3.h"
55static bool g_AllocatorInited =
false;
60static int trimTrampoline(
void* p) {
61 CacheManager::instance().trimThread();
66CacheManager::CacheManager()
76 m_bTrimRequested(false),
83CacheManager::~CacheManager() {
89 if (m_Caches.
begin() != m_Caches.
end()) {
90 FATAL(
"CacheManager destroyed while Cache objects remain registered");
94 if (m_Caches.
begin() != m_Caches.
end()) {
95 FATAL(
"CacheManager destroyed while Cache objects remain registered");
102void CacheManager::stopPeriodicWork() {
105 if (!m_pTimer->unregisterHandler(
this)) {
106 FATAL(
"CacheManager could not drain its timer callback");
114 auto guard = m_TrimWaiters.acquire();
120 m_pTrimThread =
nullptr;
132 FATAL(
"Concurrent CacheManager terminal shutdown is not permitted");
146 size_t remainingPasses = 0;
151 remainingPasses = m_Caches.
count() + 1;
153 while (remainingPasses--) {
154 bool succeeded =
true;
156 for (
size_t scan = 0; scan < 2; ++scan) {
157 uint64_t afterId = 0;
158 Cache* cache =
nullptr;
159 uint64_t cacheId = 0;
170 succeeded = cache->syncAll() && succeeded;
173 if (cache->m_Callback) {
174 for (
auto page = cache->m_Pages.begin(); page != cache->m_Pages.end(); ++page)
175 dirty |= page.value()->status == Cache::CachePage::Editing ||
176 cache->needsWriteback(page.value());
181 if (!succeeded || !dirty) {
188 ERROR(
"CacheManager: terminal writeback did not settle");
196 if (t && t->registerHandler(
this)) {
198 const Time::Timestamp interval = CACHE_WRITEBACK_PERIOD * Time::Multiplier::Millisecond;
200 FATAL(
"CacheManager could not arm its timer callback");
203 FATAL(
"CacheManager could not register its timer callback");
214 auto guard = m_TrimWaiters.acquire();
216 m_bTrimRequested =
true;
218 m_pTrimThread =
new Thread(pParent, trimTrampoline, 0);
219 m_pTrimThread->setName(
"CacheManager trim thread");
223void CacheManager::registerCache(
Cache* pCache) {
228 FATAL(
"Cache registered after terminal CacheManager shutdown began");
231 FATAL(
"CacheManager exhausted its stable cache identity space");
236 auto timerGuard = m_TrimWaiters.acquire();
237 pCache->m_ManagerTimerStamp = m_TimerClock;
240 pCache->m_ManagerTimerStamp = m_TimerClock;
245void CacheManager::unregisterCache(
Cache* pCache) {
251 FATAL(
"CacheManager could not unregister an unknown Cache");
264 size_t totalEvicted = 0;
266 uint64_t afterId = 0;
269 Cache* cache =
nullptr;
270 uint64_t cacheId = 0;
277 size_t evicted = cache->trim(count);
278 totalEvicted += evicted;
282 uint64_t afterId = 0;
284 Cache* cache =
nullptr;
285 uint64_t cacheId = 0;
286 while (count &&
findNextCache(afterId, maximumId, cache, cacheId)) {
288 size_t evicted = cache->trim(count);
289 totalEvicted += evicted;
294 return totalEvicted != 0;
301 const Time::Timestamp interval = CACHE_WRITEBACK_PERIOD * Time::Multiplier::Millisecond;
303 !m_pTimer->
armHandler(
this, Time::getTicks() + interval)) {
304 FATAL(
"CacheManager could not rearm its timer callback");
307 bool memoryPressure =
false;
314 timerTick(delta, memoryPressure);
317void CacheManager::timerTick(uint64_t delta,
bool memoryPressure) {
319 auto guard = m_TrimWaiters.acquire();
321 m_TimerClock.advance(delta);
322 const uint64_t maximum = ~static_cast<uint64_t>(0);
323 m_TrimDelta = delta > (maximum - m_TrimDelta) ? maximum : m_TrimDelta + delta;
325 if (!m_bTrimRequested && (memoryPressure || m_TrimDelta >= CACHE_WRITEBACK_PERIOD * 1000000ULL)) {
327 m_bTrimRequested =
true;
331 (void)memoryPressure;
335 uint64_t afterId = 0;
337 Cache* cache =
nullptr;
338 uint64_t cacheId = 0;
339 while (
findNextCache(afterId, maximumId, cache, cacheId,
true)) {
341 dispatchTimer(cache, stamp);
346void CacheManager::TimerStamp::advance(uint64_t delta) {
347 const uint64_t previous = elapsed;
349 if (elapsed < previous)
353uint64_t CacheManager::TimerStamp::since(
const TimerStamp& previous)
const {
354 if (wraps < previous.wraps || (wraps == previous.wraps && elapsed < previous.elapsed))
356 if (wraps == previous.wraps || (wraps - previous.wraps == 1 && elapsed < previous.elapsed))
357 return elapsed - previous.elapsed;
362 if (m_TrimDelta < CACHE_WRITEBACK_PERIOD * 1000000ULL)
367 stamp = m_TimerClock;
371void CacheManager::dispatchTimer(
Cache* cache,
const TimerStamp& stamp) {
372 const uint64_t delta = stamp.since(cache->m_ManagerTimerStamp);
375 cache->m_ManagerTimerStamp = stamp;
380 uint64_t& cacheId,
bool timersOnly) {
381 while (afterId < maximumId && m_Caches.
lowerBound(afterId + 1, cacheId, cache) &&
382 cacheId <= maximumId) {
383 if (!timersOnly || cache->needsPeriodicTimer())
397 for (
auto it = m_Caches.
begin(); it != m_Caches.
end(); ++it) {
398 if (it.value() == cache) {
399 if (cache->m_ManagerOperations.tryAcquire(lease)) {
400 generation = cache->m_ManagerId;
416 if (!
findNextCache(afterId, maximumId, cache, cacheId, timersOnly)) {
421 if (!cache->m_ManagerOperations.tryAcquire(lease)) {
422 FATAL(
"CacheManager found a closing Cache still registered");
434 CacheConstants::CallbackCause cause, uintptr_t key,
435 uintptr_t location,
bool transferredPin,
bool batch) {
440 cache->releaseWriteback(key);
450 cache->releaseWriteback(key);
453 uint64_t generation = 0;
458 if (transferredPin) {
459 cache->releaseWriteback(key);
468 if (transferredPin) {
469 cache->releaseWriteback(key);
473 const uint64_t requestToken =
reinterpret_cast<uint64_t
>(request);
475 const uint64_t generation = 0;
476 const uint64_t requestToken = 0;
481 return addAsyncRequest(1,
reinterpret_cast<uint64_t
>(cache), cause, key, location,
482 transferredPin ? 1 : 0, generation, batch ? 2 : 0, requestToken);
485 return addRequest(1, RequestQueue::NewRequest,
reinterpret_cast<uint64_t
>(cache), cause, key,
486 location, transferredPin ? 1 : 0, generation, batch ? 2 : 0, requestToken);
490 uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8) {
493 if (!request || !request->cache || request->cache !=
reinterpret_cast<Cache*
>(p1)) {
494 FATAL(
"CacheManager received a request without lifetime ownership");
497 Cache* pCache = request->cache;
503 bool cacheFound =
false;
504 for (
auto it = m_Caches.
begin(); it != m_Caches.
end(); ++it) {
505 if (it.value() == pCache) {
515 uint64_t result = pCache->
executeRequest(p1, p2, p3, p4, p5, p6, p7, 0);
526 FATAL(
"CacheManager cancelled a request without lifetime ownership");
530 cacheRequest->cache->releaseBackgroundWriteback(
533 cacheRequest->cache->releaseWriteback(request.p3);
537 if (request.p1 && request.p7 == 2)
538 reinterpret_cast<Cache*
>(request.p1)
540 if (request.p1 && request.p5) {
541 Cache* cache =
reinterpret_cast<Cache*
>(request.p1);
542 cache->releaseWriteback(request.p3);
548void CacheManager::trimThread() {
551 bool timerDue =
false;
553 auto guard = m_TrimWaiters.acquire();
557 if (!m_bTrimRequested) {
558 const WaitQueue::WakeReason reason = guard.
wait(
560 if (reason == WaitQueue::WakeReason::Unwinding ||
561 reason == WaitQueue::WakeReason::Terminating) {
566 m_bTrimRequested =
false;
572 size_t lowMark = MemoryPressureManager::getLowWatermark();
573 if (
UNLIKELY(currFree <= lowMark)) {
577 "trimThread: free page count nears high watermark, "
578 "automatically trimming");
581 size_t trimCount = (lowMark - currFree) + 1;
586 uint64_t afterId = 0;
589 Cache* cache =
nullptr;
590 uint64_t cacheId = 0;
592 if (!
acquireNextCache(afterId, maximumId, cache, cacheId, cacheLease,
true)) {
597 dispatchTimer(cache, stamp);
604Cache::Cache(
size_t pageConstraints)
607 m_DirtyTracking(DirtyTracking::Checksum),
610 m_PageFilter(4096, 4),
616 m_ManagerOperations(),
619 m_ManagerTimerStamp(),
620 m_PeriodicTimerEnabled(false),
622 m_BackgroundWriteback(nullptr),
625 m_CallbackMeta(nullptr),
628 m_PageConstraints(pageConstraints)
629#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
631 m_WritebackAdmissionHook(nullptr),
632 m_WritebackAdmissionHookMeta(nullptr)
637 if (!g_AllocatorInited) {
641 discover_range(start, end);
646 m_Allocator.free(start, end - start);
647 g_AllocatorInited =
true;
656 CacheManager::instance().registerCache(
this);
665 if (state == 2 || state == 3) {
668 const bool deferred = state == 4;
669 if (deferred && mode == ShutdownMode::DiscardDeferred) {
672 if (!
m_ShutdownState.compareAndSwap(deferred ? 4 : 0, deferred ? 5 : 1)) {
673 FATAL(
"Concurrent Cache shutdown is not permitted");
681 CacheManager::instance().unregisterCache(
this);
683 size_t discardedDirtyPages = 0;
684 const bool waitForPins = mode != ShutdownMode::DiscardDeferred;
685 const bool discard = deferred || mode != ShutdownMode::WriteBack;
686 const bool succeeded =
687 empty(discard ? EvictionMode::DiscardDirty : EvictionMode::DiscardBaseReference,
688 &discardedDirtyPages, waitForPins);
689 if (discardedDirtyPages) {
690 WARNING(
"Cache: discarded " <<
Dec << discardedDirtyPages
691 <<
" dirty pages after backing device removal");
694 ERROR(
"Cache: backend teardown left unwritten pages resident");
705 if (state == 4 || state == 5) {
709 FATAL(
"Cache::" << operation <<
" called after terminal shutdown");
723 if (!page || page->evictionState == CachePage::EvictionState::None) {
728 const WaitQueue::WakeReason reason =
746 Metrics::increment(Metrics::CacheLookupMiss);
752 Metrics::increment(Metrics::CacheLookupMiss);
755 if (pPage->evictionState == CachePage::EvictionState::Draining ||
756 pPage->evictionState == CachePage::EvictionState::Retiring) {
757 Metrics::increment(Metrics::CacheLookupMiss);
764 Metrics::increment(Metrics::CacheLookupHit);
769size_t Cache::read(uintptr_t offset,
size_t length, uintptr_t buffer,
770 bool (*prepare)(uintptr_t,
size_t)) {
771 if (!length || length - 1 > ~uintptr_t(0) - offset || !
ensureUsable(
"read")) {
777 constexpr size_t MaxPages = 32;
779 bool evict[MaxPages];
780 const size_t within = offset % CachePageSize;
781 const uintptr_t first = offset - within;
782 const size_t limit = MaxPages * CachePageSize - within;
783 if (length > limit) {
786 const size_t wanted = (within + length - 1) / CachePageSize + 1;
790 for (; count < wanted; ++count) {
793 if (!page || page->status == CachePage::Editing ||
794 page->evictionState == CachePage::EvictionState::Draining ||
795 page->evictionState == CachePage::EvictionState::Retiring || page->
refcnt == ~
size_t(0)) {
806 const size_t available = count * CachePageSize - within;
807 size_t copied = length < available ? length : available;
808 if (prepare && !prepare(buffer, copied)) {
811 if (buffer && copied) {
812 size_t remaining = copied;
813 for (
size_t i = 0; i < count; ++i) {
814 const size_t start = i ? 0 : within;
815 const size_t bytes = remaining < CachePageSize - start ? remaining : CachePageSize - start;
816 ForwardMemoryCopy(
reinterpret_cast<void*
>(buffer),
817 reinterpret_cast<void*
>(pages[i]->location + start), bytes);
824 for (
size_t i = 0; i < count; ++i) {
825 assert(pages[i]->refcnt);
830 for (
size_t i = 0; i < count; ++i) {
836 CacheManager::instance().
addCacheRequest(
this,
true, CacheConstants::PleaseEvict,
837 first + i * CachePageSize);
840 Metrics::add(Metrics::CacheReadBytes, copied);
854 const bool canWait = wait && currentThread && !CacheManager::instance().callbackContext();
868 if (page->status == CachePage::Editing ||
869 page->evictionState == CachePage::EvictionState::Draining || page->
refcnt == ~
size_t{0})
871 if (!page->callbackActive && page->evictionState == CachePage::EvictionState::None) {
878 if (!canWait || page->callbackOwner == currentThread)
906 bool triedLookup =
false;
909 if (pPage && pPage->evictionState != CachePage::EvictionState::None) {
913 if (alreadyExisted) {
914 *alreadyExisted =
true;
922 if (alreadyExisted) {
923 *alreadyExisted =
false;
928 if ((!triedLookup) &&
m_Pages.lookup(key)) {
929 FATAL(
"Cache: bloom filter lied!");
933 uintptr_t location = 0;
938 FATAL(
"Cache: out of address space [have " <<
m_Pages.count() <<
" items].");
942 if (!
map(location)) {
943 FATAL(
"Map failed in Cache::insert())");
953 pPage->status = CachePage::Editing;
955 updateWritebackIndex(pPage);
968 if (size % CachePageSize) {
969 WARNING(
"Cache::insert called with a size that isn't page-aligned");
970 if (alreadyExisted) {
971 *alreadyExisted =
false;
976 size_t nPages = size / CachePageSize;
983 for (
size_t page = 0; page < nPages; ++page) {
988 for (
size_t page = 0; page < nPages; ++page) {
993 bool evictionPending =
false;
994 for (
size_t page = 0; page < nPages; ++page) {
996 if (pageEntry && pageEntry->evictionState != CachePage::EvictionState::None) {
997 evictionPending =
true;
1001 if (evictionPending) {
1011 size_t existingPages = 0;
1012 bool contiguousExtent =
true;
1013 for (
size_t page = 0; page < nPages; ++page) {
1014 pPage =
m_Pages.lookup(key + (page * CachePageSize));
1021 contiguousExtent =
false;
1022 }
else if (page > 0 && pPage->
location != firstPage->
location + (page * CachePageSize)) {
1023 contiguousExtent =
false;
1027 if (existingPages) {
1028 if (existingPages != nPages || !firstPage || firstPage->
key != key || !contiguousExtent) {
1029 if (alreadyExisted) {
1030 *alreadyExisted =
false;
1035 if (alreadyExisted) {
1036 *alreadyExisted =
true;
1041 if (alreadyExisted) {
1042 *alreadyExisted =
false;
1052 ERROR(
"Cache: can't allocate " <<
Dec << size <<
Hex <<
" bytes.");
1056 uintptr_t returnLocation = location;
1057 for (
size_t page = 0; page < nPages; page++) {
1058 if (!
map(location)) {
1059 FATAL(
"Map failed in Cache::insert())");
1064 pPage->
key = key + (page * CachePageSize);
1069 pPage->evictionState = CachePage::EvictionState::None;
1072 pPage->status = CachePage::Editing;
1074 m_Pages.insert(key + (page * CachePageSize), pPage);
1075 updateWritebackIndex(pPage);
1079 location += CachePageSize;
1082 return returnLocation;
1093 phys,
reinterpret_cast<void*
>(virt),
1106 for (
size_t i = 0; i < length; i += CachePageSize) {
1113 if (!pPage || pPage->evictionState == CachePage::EvictionState::Retiring) {
1126 return evict(key, EvictionMode::Ordinary);
1133 return evict(key, EvictionMode::DiscardEditing);
1136bool Cache::evict(uintptr_t key, EvictionMode mode,
size_t* discardedDirtyPages) {
1140 uintptr_t location = 0;
1142 bool discardedDirty =
false;
1143 uint64_t submittedGeneration = 0;
1144 uint64_t submittedChecksum[2] = {};
1145 bool submittedChecksumTracking =
false;
1153 NOTICE(
"Cache::evict didn't evict " << key <<
" as it didn't actually exist");
1156 if (page->evictionState != CachePage::EvictionState::None) {
1160 if (mode == EvictionMode::Ordinary && page->status == CachePage::Editing) {
1167 if (mode == EvictionMode::DiscardEditing) {
1168 if (page->status != CachePage::Editing || page->
refcnt != 1) {
1171 page->evictionState = CachePage::EvictionState::Retiring;
1172 }
else if (mode == EvictionMode::DiscardDirty) {
1176 discardedDirty = callback && page->status != CachePage::Editing && needsWriteback(page);
1177 page->evictionState = CachePage::EvictionState::Retiring;
1181 const size_t permittedReferences =
1182 (callback || mode == EvictionMode::DiscardBaseReference) ? 1 : 0;
1183 if (page->
refcnt > permittedReferences) {
1187 page->evictionState = CachePage::EvictionState::WriteBack;
1188 dirty = callback && needsWriteback(page);
1189 submittedGeneration = page->mutationGeneration;
1190 submittedChecksumTracking = dirty && tracksChecksum(page);
1191 page->callbackActive = dirty;
1201 if (submittedChecksumTracking)
1202 checksum(
reinterpret_cast<const void*
>(location), CachePageSize, submittedChecksum);
1204 Metrics::increment(Metrics::CacheWritebackPages);
1206 if (dirty && !callback(CacheConstants::WriteBack, key, location,
callbackMeta)) {
1207 Metrics::increment(Metrics::CacheWritebackFailures);
1210 page->writebackFailed =
true;
1211 page->callbackActive =
false;
1213 page->callbackOwner =
nullptr;
1215 page->evictionState = CachePage::EvictionState::None;
1216 updateWritebackIndex(page);
1224 if (mode != EvictionMode::DiscardEditing && mode != EvictionMode::DiscardDirty) {
1225 bool pinnedAgain =
false;
1230 current =
m_Pages.lookup(key);
1232 if (current != page) {
1233 FATAL(
"Cache page changed identity during eviction");
1237 page->callbackActive =
false;
1239 page->callbackOwner =
nullptr;
1242 page->writebackFailed =
false;
1243 page->writtenGeneration = submittedGeneration;
1244 if (submittedChecksumTracking) {
1245 page->
checksum[0] = submittedChecksum[0];
1246 page->
checksum[1] = submittedChecksum[1];
1248 if (page->status == CachePage::ChecksumChanging)
1249 page->status = CachePage::ChecksumStable;
1254 const size_t permittedReferences =
1255 (callback || mode == EvictionMode::DiscardBaseReference) ? 1 : 0;
1256 if (page->
refcnt > permittedReferences ||
1257 (callback && (page->mutationGeneration != page->writtenGeneration ||
1258 (dirty && needsWriteback(page))))) {
1259 page->evictionState = CachePage::EvictionState::None;
1262 page->evictionState = CachePage::EvictionState::Retiring;
1264 updateWritebackIndex(page);
1276 if (retired && discardedDirty && discardedDirtyPages) {
1277 ++*discardedDirtyPages;
1283 const uintptr_t key = page->
key;
1284 const uintptr_t location = page->
location;
1294 callback(CacheConstants::Eviction, key, location,
callbackMeta);
1301 current =
m_Pages.lookup(key);
1303 if (current != page || page->evictionState != CachePage::EvictionState::Retiring) {
1304 FATAL(
"Cache page changed identity during retirement");
1308 if (page->writebackIndexed)
1317#if !STANDALONE_CACHE
1319 void* mappedLocation =
reinterpret_cast<void*
>(location);
1320 physical_uintptr_t physicalLocation = 0;
1322 va.
getMapping(mappedLocation, physicalLocation, flags);
1323 va.
unmap(mappedLocation);
1332 Metrics::increment(Metrics::CacheEvictedPages);
1347 void* evictionCallbackMeta =
nullptr;
1356 if (!callback || page->evictionState != CachePage::EvictionState::None ||
1357 page->status == CachePage::Editing) {
1361 page->evictionState = CachePage::EvictionState::Draining;
1362 status = page->status;
1370 bool invalidated =
false;
1371 bool reopened =
false;
1378 current =
m_Pages.lookup(key);
1380 if (current != page || page->evictionState != CachePage::EvictionState::Draining ||
1381 page->status != status) {
1383 if (current == page && page->evictionState == CachePage::EvictionState::Draining) {
1384 page->evictionState = CachePage::EvictionState::None;
1388 ready = page->
refcnt == 1;
1402 const WaitQueue::WakeReason reason =
1410 page->evictionState = CachePage::EvictionState::None;
1418 page->callbackActive =
true;
1423 Metrics::increment(Metrics::CacheWritebackPages);
1424 const bool writebackSucceeded = callback(key, page->
location, meta);
1425 if (!writebackSucceeded) {
1426 Metrics::increment(Metrics::CacheWritebackFailures);
1428 bool retire =
false;
1432 page->callbackActive =
false;
1434 page->callbackOwner =
nullptr;
1438 current =
m_Pages.lookup(key);
1440 if (writebackSucceeded && current == page &&
1441 page->evictionState == CachePage::EvictionState::Draining && page->
refcnt == 1 &&
1442 page->status == status) {
1443 page->evictionState = CachePage::EvictionState::Retiring;
1445 }
else if (current == page && page->evictionState == CachePage::EvictionState::Draining) {
1446 page->writebackFailed = page->writebackFailed || !writebackSucceeded;
1447 page->evictionState = CachePage::EvictionState::None;
1448 updateWritebackIndex(page);
1471 return empty(EvictionMode::DiscardBaseReference,
nullptr);
1474bool Cache::empty(EvictionMode mode,
size_t* discardedDirtyPages,
bool waitForPins) {
1482 CachePage* page = it.value();
1483 if (page->evictionState == CachePage::EvictionState::None && page->refcnt <= 1) {
1495 CachePage* waitPage =
nullptr;
1507 CachePage* page = it.value();
1508 if (page->evictionState != CachePage::EvictionState::None || page->refcnt > 1) {
1514 const WaitQueue::WakeReason reason =
1515 waitGuard.waitForCompletion(
WaitQueue::Channel(waitPage), Thread::CallbackDrain, key);
1534 if (!
evict(key, mode, discardedDirtyPages)) {
1536 CachePage* page =
m_Pages.lookup(key);
1537 if (mode != EvictionMode::DiscardDirty && page && page->writebackFailed) {
1559 if (pPage->evictionState == CachePage::EvictionState::Draining ||
1560 pPage->evictionState == CachePage::EvictionState::Retiring) {
1572 const bool deferred = state == 4 || state == 5;
1577 bool shouldEvict =
false;
1587 if (!pPage || pPage->evictionState == CachePage::EvictionState::Retiring) {
1593 releasedPage = pPage;
1594 shouldEvict = !pPage->
refcnt;
1604 CacheManager::instance().
addCacheRequest(
this,
true, CacheConstants::PleaseEvict, key);
1620 while ((nPages < count) && ((n =
lruEvict(
true)) > 0)) {
1638 uintptr_t location = 0;
1639#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1640 writeback_admission_hook_t admissionHook =
nullptr;
1641 void* admissionHookMeta =
nullptr;
1651 if (!pPage || pPage->evictionState == CachePage::EvictionState::Draining ||
1652 pPage->evictionState == CachePage::EvictionState::Retiring) {
1656 if (pPage->status != CachePage::Editing && !pPage->callbackActive &&
1657 pPage->evictionState == CachePage::EvictionState::None && !needsWriteback(pPage)) {
1662 ++pPage->writebackPins;
1665#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1666 admissionHook = m_WritebackAdmissionHook;
1667 admissionHookMeta = m_WritebackAdmissionHookMeta;
1671#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
1672 if (admissionHook) {
1673 admissionHook(
this, key, admissionHookMeta);
1678 return CacheManager::instance().
addCacheRequest(
this,
true, CacheConstants::WriteBack, key,
1679 location,
true) != 0;
1682 this,
false, CacheConstants::WriteBack, key, location,
true);
1688 return syncAll(
nullptr,
nullptr);
1699 const bool canWait = currentThread && !CacheManager::instance().callbackContext();
1709 bool snapshotted =
false;
1712 for (
size_t attempt = 0; attempt < 4 && !snapshotted; ++attempt) {
1722 count = candidates.count();
1724 if (!entries.tryReserve(count) || (callback && !keys.tryReserve(count))) {
1729 if (candidates.count() > entries.size() || (callback && candidates.count() > keys.
size())) {
1732 for (
auto it = candidates.begin(); it != candidates.end(); ++it) {
1734 if ((it.value()->callbackOwner && it.value()->callbackOwner == currentThread) ||
1735 (!currentThread && it.value()->callbackActive)) {
1737 if (it.value()->callbackActive) {
1742 if (page->evictionState != CachePage::EvictionState::Draining &&
1743 page->evictionState != CachePage::EvictionState::Retiring &&
1744 (page->
refcnt == ~
size_t{0} || page->writebackPins == ~size_t{0})) {
1748 for (
auto it = candidates.begin(); it != candidates.end(); ++it) {
1750 const bool pinned = page->evictionState != CachePage::EvictionState::Draining &&
1751 page->evictionState != CachePage::EvictionState::Retiring;
1754 ++page->writebackPins;
1756 const Entry entry = {page->
key, page->
location, pinned};
1757 entries.pushBack(entry);
1766 bool succeeded =
true;
1767 for (
size_t i = 0; i < entries.count(); ++i) {
1768 Entry& entry = entries[i];
1771 while (!entry.pinned) {
1779 page =
m_Pages.lookup(entry.key);
1783 busy = page->evictionState == CachePage::EvictionState::Draining ||
1784 page->evictionState == CachePage::EvictionState::Retiring;
1786 if (page->
refcnt == ~
size_t{0} || page->writebackPins == ~
size_t{0}) {
1791 ++page->writebackPins;
1793 entry.pinned =
true;
1796 else if (!canWait || page->callbackOwner == currentThread) {
1804 const WaitQueue::WakeReason reason =
1805 waitGuard.waitForCompletion(
WaitQueue::Channel(page), Thread::CallbackDrain, entry.key);
1817 const bool written =
writebackPage(entry.key, entry.location,
true);
1818 succeeded = written && succeeded;
1819 releaseWriteback(entry.key);
1821 entry.pinned =
false;
1824 if (callback && keys.
count()) {
1826 succeeded = syncBatchInternal(&keys[0], keys.
count(), callback, metadata,
true) && succeeded;
1827 for (uintptr_t key : keys) {
1828 releaseWriteback(key);
1836 return syncBatchInternal(keys, count, callback, metadata,
false);
1839bool Cache::DirectWritebackLease::acquire(
Cache& cache, uintptr_t key, uintptr_t location) {
1840 if (m_Cache || !cache.ensureUsable(
"DirectWritebackLease")) {
1845 CachePage* page = cache.m_Pages.lookup(key);
1846 if (!page || page->location != location || !page->callbackActive ||
1847 page->status == CachePage::Editing || page->mutableLoans || page->externallyWritable ||
1848 page->directWriteback) {
1851 page->directWriteback =
true;
1856#if !STANDALONE_CACHE
1857 physical_uintptr_t physical = 0;
1860 if (addressSpace.getMapping(
reinterpret_cast<void*
>(location), physical, flags) && physical &&
1861 !(physical & (CachePageSize - 1)) &&
1866 m_Physical = physical;
1874void Cache::DirectWritebackLease::release() {
1878 Cache* cache = m_Cache;
1879 CachePage* page = m_Page;
1882 assert(page->directWriteback);
1883 page->directWriteback =
false;
1889 cache->m_EvictionWaiters.wakeAll(WaitQueue::WakeReason::Signalled,
WaitQueue::Channel(page));
1894bool Cache::syncBatchInternal(
const uintptr_t* keys,
size_t count, writeback_batch_t callback,
1895 void* metadata,
bool snapshot) {
1896 if (!
ensureUsable(
"syncBatch") || (!snapshot && count > MaxWritebackPages) ||
1897 (count && (!keys || !callback))) {
1903 for (
size_t i = 0; !snapshot && i < count; ++i) {
1904 for (
size_t j = 0; j < i; ++j) {
1905 if (keys[i] == keys[j])
1915 const bool canWait = currentThread && !CacheManager::instance().callbackContext();
1919 uint64_t checksum[2];
1920 uint64_t generation;
1921 bool checksumTracking;
1923 Submission smallSubmissions[MaxWritebackPages] = {};
1924 WritebackPage smallWrites[MaxWritebackPages] = {};
1927 Submission* submissions = smallSubmissions;
1928 WritebackPage* writes = smallWrites;
1929 if (count > MaxWritebackPages) {
1932 if (!submissionsOwner || !writesOwner) {
1935 submissions = submissionsOwner.get();
1936 writes = writesOwner.get();
1938 size_t writeCount = 0;
1943 CachePage* busy =
nullptr;
1950 for (
size_t i = 0; i < count; ++i) {
1951 CachePage* page =
m_Pages.lookup(keys[i]);
1952 if (!page || page->status == CachePage::Editing ||
1953 (page->evictionState == CachePage::EvictionState::Draining && !snapshot) ||
1954 page->evictionState == CachePage::EvictionState::Retiring ||
1955 page->refcnt == ~
size_t{0} || page->writebackPins == ~
size_t{0}) {
1958 if (page->callbackActive || page->evictionState == CachePage::EvictionState::WriteBack) {
1960 if (!canWait || page->callbackOwner == currentThread)
1967 submissions[i].page = page;
1971 for (
size_t i = 0; i < count; ++i) {
1972 CachePage* page = submissions[i].page;
1973 if (!needsWriteback(page)) {
1977 ++page->writebackPins;
1978 page->callbackActive =
true;
1980 page->callbackOwner = currentThread;
1982 submissions[writeCount].page = page;
1983 writes[writeCount] = {keys[i], page->location};
1984 submissions[writeCount].generation = page->mutationGeneration;
1985 submissions[writeCount].checksumTracking = tracksChecksum(page);
1987 updateWritebackIndex(page);
1995 const auto reason = waitGuard.waitForCompletion(
WaitQueue::Channel(busy), Thread::CallbackDrain,
1996 reinterpret_cast<uintptr_t
>(busy));
2002 for (
size_t i = 0; i < writeCount; ++i) {
2003 if (submissions[i].checksumTracking)
2004 checksum(
reinterpret_cast<const void*
>(writes[i].location), CachePageSize,
2005 submissions[i].checksum);
2007 Metrics::add(Metrics::CacheWritebackPages, writeCount);
2008 const bool succeeded = callback(writes, writeCount, metadata);
2010 Metrics::add(Metrics::CacheWritebackFailures, writeCount);
2014 for (
size_t i = 0; i < writeCount; ++i) {
2015 CachePage* page = submissions[i].page;
2016 page->writebackFailed = !succeeded;
2018 page->writtenGeneration = submissions[i].generation;
2019 if (submissions[i].checksumTracking) {
2020 page->checksum[0] = submissions[i].checksum[0];
2021 page->checksum[1] = submissions[i].checksum[1];
2023 if (page->status == CachePage::ChecksumChanging)
2024 page->status = CachePage::ChecksumStable;
2026 page->callbackActive =
false;
2028 page->callbackOwner =
nullptr;
2030 updateWritebackIndex(page);
2033 for (
size_t i = 0; i < writeCount; ++i) {
2038 releaseWriteback(writes[i].key);
2047 uint64_t submittedGeneration = 0;
2048 bool submittedChecksumTracking =
false;
2051 const bool canWait = wait && currentThread && !CacheManager::instance().callbackContext();
2065 if (page->status == CachePage::Editing) {
2068 if (!page->callbackActive && page->evictionState != CachePage::EvictionState::WriteBack) {
2070 if (!needsWriteback(page)) {
2075 page->callbackActive =
true;
2077 page->callbackOwner = currentThread;
2081 submittedGeneration = page->mutationGeneration;
2082 submittedChecksumTracking = tracksChecksum(page);
2083 updateWritebackIndex(page);
2087 if (!canWait || page->callbackOwner == currentThread) {
2095 const WaitQueue::WakeReason reason =
2101 uint64_t submittedChecksum[2] = {};
2102 if (submittedChecksumTracking)
2103 checksum(
reinterpret_cast<const void*
>(location), CachePageSize, submittedChecksum);
2104 Metrics::increment(Metrics::CacheWritebackPages);
2105 const bool succeeded = callback(CacheConstants::WriteBack, key, location,
callbackMeta);
2107 Metrics::increment(Metrics::CacheWritebackFailures);
2111 page->writebackFailed = !succeeded;
2113 page->writtenGeneration = submittedGeneration;
2114 if (submittedChecksumTracking) {
2115 page->
checksum[0] = submittedChecksum[0];
2116 page->
checksum[1] = submittedChecksum[1];
2119 if (page->status == CachePage::ChecksumChanging) {
2120 page->status = CachePage::ChecksumStable;
2123 page->callbackActive =
false;
2125 page->callbackOwner =
nullptr;
2127 updateWritebackIndex(page);
2142 recordMutation(page);
2151 if (!page || page->externallyWritable)
2153 const bool tracked = tracksChecksum(page);
2154 page->externallyWritable =
true;
2157 updateWritebackIndex(page);
2172 if (!page || page->evictionState == CachePage::EvictionState::Retiring ||
2173 page->mutableLoans == ~
size_t{0}) {
2176 if (!page->directWriteback) {
2177 const bool tracked = tracksChecksum(page);
2178 ++page->mutableLoans;
2182 if (page->callbackActive) {
2183 recordMutation(page);
2186 updateWritebackIndex(page);
2191 if (!current || page->callbackOwner == current) {
2206void Cache::endMutableLoan(uintptr_t key) {
2208 if (state != 4 && state != 5 && !
ensureUsable(
"endMutableLoan")) {
2212 CachePage* page =
m_Pages.lookup(key);
2213 assert(page && page->mutableLoans);
2214 if (!page || !page->mutableLoans)
2217 if (page->mutableLoans == 1) {
2220 if (page->callbackActive ||
2221 (!page->writebackFailed && page->mutationGeneration == page->writtenGeneration &&
2223 recordMutation(page);
2225 --page->mutableLoans;
2226 updateWritebackIndex(page);
2258 const uint64_t maximum = ~static_cast<uint64_t>(0);
2278 auto submitBatch = [&] {
2280 CacheManager::instance().
addCacheRequest(
this,
true, CacheConstants::WriteBack,
2281 reinterpret_cast<uintptr_t
>(batch), 0,
false,
true);
2285 uintptr_t nextKey = 0;
2287 bool finished =
false;
2289 bool queueWriteback =
false;
2291 uintptr_t location = 0;
2292#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2293 writeback_admission_hook_t admissionHook =
nullptr;
2294 void* admissionHookMeta =
nullptr;
2303 if (!candidates.lowerBound(nextKey, key, page)) {
2306 finished = key == ~uintptr_t{0};
2310 if (page->writebackEpoch == m_WritebackEpoch) {
2313 page->writebackEpoch = m_WritebackEpoch;
2314 if (page->evictionState != CachePage::EvictionState::None) {
2319 if (page->writebackPins) {
2322 if (page->status == CachePage::Editing) {
2325 if (page->status == CachePage::EditTransition) {
2327 page->status = CachePage::ChecksumStable;
2330 if (page->writebackFailed || page->mutationGeneration != page->writtenGeneration) {
2332 }
else if (!tracksChecksum(page)) {
2334 }
else if (page->status == CachePage::ChecksumChanging) {
2338 page->status = CachePage::ChecksumStable;
2339 }
else if (page->status == CachePage::ChecksumStable) {
2341 page->status = CachePage::ChecksumChanging;
2342 recordMutation(page);
2346 ERROR(
"Unknown page status!");
2352 ++page->writebackPins;
2354 queueWriteback =
true;
2355#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2356 admissionHook = m_WritebackAdmissionHook;
2357 admissionHookMeta = m_WritebackAdmissionHookMeta;
2361 if (!queueWriteback) {
2365#if defined(CACHE_TRACE_WRITEBACK) && CACHE_TRACE_WRITEBACK
2366 NOTICE(
"Cache " <<
Hex <<
reinterpret_cast<uintptr_t
>(
this) <<
": queue writeback key=" << key
2367 <<
", page=" << location);
2369#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2370 if (admissionHook) {
2371 admissionHook(
this, key, admissionHookMeta);
2374 if (m_BackgroundWriteback) {
2378 batch->keys[batch->count++] = key;
2379 if (batch->count == MaxWritebackPages)
2384 CacheManager::instance().
addCacheRequest(
this,
true, CacheConstants::WriteBack, key, location,
2392 FATAL(
"Cache callback installation requires an active Cache");
2398 FATAL(
"Cache callbacks cannot be cleared after publication");
2402 FATAL(
"Cache callbacks are immutable after installation");
2406 FATAL(
"Cache callbacks must be installed before inserting pages");
2411 __atomic_store_n(&m_PeriodicTimerEnabled,
true, __ATOMIC_RELEASE);
2417 m_BackgroundWriteback || !callback) {
2418 FATAL(
"Background writeback must be installed before publishing cache pages");
2421 m_BackgroundWriteback = callback;
2424void Cache::releaseBackgroundWriteback(BackgroundWriteback* batch) {
2425 for (
size_t i = 0; i < batch->count; ++i)
2426 releaseWriteback(batch->keys[i]);
2435 FATAL(
"Cache dirty tracking must be selected before inserting pages");
2438 m_DirtyTracking = tracking;
2441#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
2442void Cache::setWritebackAdmissionHookForTest(writeback_admission_hook_t hook,
void* meta) {
2444 m_WritebackAdmissionHook = hook;
2445 m_WritebackAdmissionHookMeta = meta;
2450 uint64_t p6, uint64_t p7, uint64_t p8) {
2453 const bool succeeded =
2454 syncBatchInternal(batch->keys, batch->count, m_BackgroundWriteback,
m_CallbackMeta,
true);
2455 releaseBackgroundWriteback(batch);
2456 return succeeded ? 2 : 0;
2460 if (
static_cast<CacheConstants::CallbackCause
>(p2) == CacheConstants::PleaseEvict) {
2470 if (!page || page->evictionState == CachePage::EvictionState::Draining ||
2471 page->evictionState == CachePage::EvictionState::Retiring) {
2475 ++page->writebackPins;
2483 releaseWriteback(p3);
2485 return succeeded ? 2 : 0;
2494 MemoryPressureManager::getLowWatermark()) {
2504 key = m_pLruTail->
key;
2517 if (page && page->evictionState == CachePage::EvictionState::None) {
2546 pPage->
pNext->pPrev = pPage->pPrev;
2547 if (pPage == m_pLruTail)
2548 m_pLruTail = pPage->pPrev;
2554 if (!tracksChecksum(pPage))
2556 void* buffer =
reinterpret_cast<void*
>(pPage->
location);
2561 if (!tracksChecksum(pPage))
2563 void* buffer =
reinterpret_cast<void*
>(pPage->
location);
2565 uint64_t new_checksum[2];
2566 checksum(buffer, CachePageSize, new_checksum);
2570 pPage->
checksum[0] = new_checksum[0];
2571 pPage->
checksum[1] = new_checksum[1];
2577bool Cache::tracksChecksum(
const CachePage* page)
const {
2578 return m_DirtyTracking == DirtyTracking::Checksum || page->externallyWritable ||
2582bool Cache::needsWriteback(CachePage* page) {
2583 return page->writebackFailed || page->mutationGeneration != page->writtenGeneration ||
2587void Cache::updateWritebackIndex(CachePage* page) {
2588 if (m_DirtyTracking != DirtyTracking::Explicit)
2590 const bool candidate = page->externallyWritable || page->mutableLoans || page->writebackFailed ||
2591 page->mutationGeneration != page->writtenGeneration ||
2592 page->status == CachePage::Editing || page->callbackActive;
2593 if (candidate && !page->writebackIndexed) {
2595 page->writebackIndexed =
true;
2596 }
else if (!candidate && page->writebackIndexed) {
2598 page->writebackIndexed =
false;
2602void Cache::recordMutation(CachePage* page) {
2603 ++page->mutationGeneration;
2604 if (page->mutationGeneration == page->writtenGeneration)
2605 ++page->mutationGeneration;
2606 updateWritebackIndex(page);
2610 MurmurHash3_x64_128(data, len, 0, out);
2618 if (length && (length % CachePageSize)) {
2619 WARNING(
"Cache::markEditing called with a length that isn't page-aligned");
2624 length = CachePageSize;
2629 size_t nPages = length / CachePageSize;
2631 for (
size_t page = 0; page < nPages; page++) {
2632 if (!
m_PageFilter.contains(key + (page * CachePageSize))) {
2641 pPage->status = CachePage::Editing;
2642 updateWritebackIndex(pPage);
2651 if (length && (length % CachePageSize)) {
2652 WARNING(
"Cache::markNoLongerEditing called with a length that isn't page-aligned");
2657 length = CachePageSize;
2662 size_t nPages = length / CachePageSize;
2664 for (
size_t page = 0; page < nPages; page++) {
2665 if (!
m_PageFilter.contains(key + (page * CachePageSize))) {
2674 pPage->status = tracksChecksum(pPage) ? CachePage::EditTransition : CachePage::ChecksumStable;
2680 updateWritebackIndex(pPage);
2684CachePageGuard::CachePageGuard(
Cache& cache, uintptr_t location)
2685 : m_Cache(cache), m_Location(location) {}
2687CachePageGuard::~CachePageGuard() {
uint64_t cacheGenerationWatermark()
bool acquireNextCache(uint64_t afterId, uint64_t maximumId, Cache *&cache, uint64_t &cacheId, OperationBarrier::Lease &lease, bool timersOnly=false)
bool acquireCache(Cache *cache, uint64_t &generation, OperationBarrier::Lease &lease)
MUST_USE_RESULT bool shutdown()
bool takeTimerStamp(TimerStamp &stamp)
Atomic< size_t > m_TerminalState
virtual void cancelRequest(const Request &request)
bool trimAll(size_t count=1)
virtual void timer(uint64_t delta)
uint64_t addCacheRequest(Cache *cache, bool asynchronous, CacheConstants::CallbackCause cause, uintptr_t key, uintptr_t location=0, bool transferredPin=false, bool batch=false)
virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
bool findNextCache(uint64_t afterId, uint64_t maximumId, Cache *&cache, uint64_t &cacheId, bool timersOnly=false)
void linkPage(CachePage *pPage)
bool exists(uintptr_t key, size_t length)
OperationBarrier m_ManagerOperations
size_t lruEvict(bool force=false)
MUST_USE_RESULT bool discardEditing(uintptr_t key)
void promotePage(CachePage *pPage)
size_t read(uintptr_t offset, size_t length, uintptr_t buffer, bool(*prepare)(uintptr_t, size_t)=nullptr)
void setBackgroundWriteback(writeback_batch_t callback)
static Spinlock m_AllocatorLock
MUST_USE_RESULT bool retireWriteback(uintptr_t key, retirement_writeback_t callback, void *meta)
virtual void timer(uint64_t delta)
bool shutdown(ShutdownMode mode=ShutdownMode::WriteBack)
bool writebackPage(uintptr_t key, uintptr_t location, bool wait)
MUST_USE_RESULT bool syncAll()
void setCallback(writeback_t newCallback, void *meta)
bool evict(uintptr_t key)
void release(uintptr_t key)
void unlinkPage(CachePage *pPage)
MUST_USE_RESULT bool lookupStable(uintptr_t key, uintptr_t &location, bool wait=false)
Tree< uintptr_t, CachePage * > m_Pages
bool(* writeback_t)(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page, void *meta)
bool verifyChecksum(CachePage *pPage, bool replace=false)
uintptr_t insert(uintptr_t key, bool *alreadyExisted=nullptr)
WaitQueue m_EvictionWaiters
virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5, uint64_t p6, uint64_t p7, uint64_t p8)
void markEditing(uintptr_t key, size_t length=0)
size_t trim(size_t count=1)
bool finishRetirement(CachePage *page, writeback_t callback, void *callbackMeta)
void waitForPageEviction(uintptr_t key)
MUST_USE_RESULT bool beginMutableLoan(uintptr_t key)
MUST_USE_RESULT bool syncBatch(const uintptr_t *keys, size_t count, writeback_batch_t callback, void *metadata)
BloomFilter< uintptr_t > m_PageFilter
Atomic< size_t > m_bInCritical
static MemoryAllocator m_Allocator
void markExternallyWritable(uintptr_t key)
void markDirty(uintptr_t key)
void triggerChecksum(uintptr_t key)
Atomic< size_t > m_ShutdownState
bool sync(uintptr_t key, bool async)
Tree< uintptr_t, CachePage * > m_WritebackPages
bool ensureUsable(const char *operation) const
void markNoLongerEditing(uintptr_t key, size_t length=0)
uintptr_t lookup(uintptr_t key)
void checksum(const void *data, size_t len, uint64_t out[2])
void setDirtyTracking(DirtyTracking tracking)
bool map(uintptr_t virt) const
MUST_USE_RESULT bool pin(uintptr_t key)
void calculateChecksum(CachePage *pPage)
virtual Timer * getTimer()=0
bool isOwnedByCurrentThread() const
MUST_USE_RESULT bool tryAcquire(Lease &lease)
virtual size_t freePageCount() const
virtual physical_uintptr_t allocatePage(size_t pageConstraints=0)=0
static PhysicalMemoryManager & instance()
virtual void freePage(physical_uintptr_t page)=0
static ProcessorInformation & information()
bool allocate(T length, T &address)
void free(T address, T length, bool merge=true)
virtual void initialise()
MUST_USE_RESULT uint64_t addRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
uint64_t addAsyncRequest(size_t priority, uint64_t p1=0, uint64_t p2=0, uint64_t p3=0, uint64_t p4=0, uint64_t p5=0, uint64_t p6=0, uint64_t p7=0, uint64_t p8=0)
bool callbackActiveOnCurrentThread() const
bool acquire(bool recurse=false, bool safe=true)
static constexpr size_t getPageSize() noexcept
virtual bool supportsDeadlines() const
virtual bool armHandler(TimerHandler *, uint64_t)
An iterator applicable for many data structures.
void remove(const K &key)
bool lowerBound(const K &key, K &foundKey, E &foundValue) const
E lookup(const K &key) const
void insert(const K &key, const E &value)
A vector / dynamic array.
static const size_t CopyOnWrite
virtual uintptr_t getKernelCacheEnd() const =0
static const size_t Borrowed
virtual uintptr_t getKernelCacheStart() const =0
static const size_t KernelMode
virtual bool getMapping(void *virtualAddress, physical_uintptr_t &physicalAddress, size_t &flags)=0
static const size_t NoAccess
static const size_t Write
static EXPORTED_PUBLIC VirtualAddressSpace & getKernelAddressSpace()
static const size_t Swapped
virtual void unmap(void *virtualAddress)=0
MUST_USE_RESULT WakeReason wait(const Channel &channel=Channel(), size_t debugState=0, uintptr_t debugAddress=0, StackDiscardCleanup onStackDiscard=nullptr, void *stackDiscardContext=nullptr)
size_t wakeAllIfWaiting(WakeReason reason=WakeReason::Signalled, const Channel &channel=Channel())
void pushBack(const T &value)
uint64_t checksum[2]
Checksum of the page's contents (for dirty detection).
bool checkZeroChecksum() const
Check for an unset checksum.
bool checkChecksum(uint64_t other[2]) const
Check the checksum against another.
uintptr_t key
Key for this page.
CachePage * pNext
Linked list components for LRU.
uintptr_t location
The location of this page in memory.
Status
Current page status.