The Pedigree Project 0.1
Cache.h
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#ifndef CACHE_H
21#define CACHE_H
22#include "pedigree/kernel/Atomic.h"
23#include "pedigree/kernel/Spinlock.h"
24#include "pedigree/kernel/compiler.h"
25#include "pedigree/kernel/machine/TimerHandler.h"
26#include "pedigree/kernel/process/Mutex.h"
27#include "pedigree/kernel/process/OperationBarrier.h"
28#include "pedigree/kernel/process/TerminationDeferral.h"
29#include "pedigree/kernel/processor/state_forward.h"
30#include "pedigree/kernel/processor/types.h"
31#include "pedigree/kernel/utilities/BloomFilter.h"
32#include "pedigree/kernel/utilities/CacheConstants.h"
33#include "pedigree/kernel/utilities/List.h"
34#include "pedigree/kernel/utilities/MemoryAllocator.h"
35#include "pedigree/kernel/utilities/Pointers.h"
36#include "pedigree/kernel/utilities/RequestQueue.h"
37#include "pedigree/kernel/utilities/Tree.h"
38#include "pedigree/kernel/utilities/new"
39#include "pedigree/kernel/utilities/utility.h"
40
41#include <config.h>
42
43class Thread;
44class Timer;
45class UnlikelyLock;
46
47#ifndef STANDALONE_CACHE
48#define STANDALONE_CACHE 0
49#endif
50
53#define CACHE_AGE_THRESHOLD 10
54
57#define CACHE_NUM_THRESHOLD 2
58
60#define CACHE_WRITEBACK_PERIOD 500
61
62// Forward declaration of Cache so CacheManager can be defined first
63class Cache;
64
66class EXPORTED_PUBLIC CacheManager :
67#if !STANDALONE_CACHE
68 public TimerHandler,
69#endif
70 public RequestQueue {
71 friend class Cache;
72 friend class CacheManagerTestPeer;
73
74 public:
76 virtual ~CacheManager();
77
78 static CacheManager& instance() {
79 if (!m_Instance) {
80 m_Instance = new CacheManager;
81 }
82 return *m_Instance;
83 }
84
85 static void destroyInstance() {
86 delete m_Instance;
87 m_Instance = nullptr;
88 }
89
90 void initialise();
91
98 MUST_USE_RESULT bool shutdown();
99
100 void registerCache(Cache* pCache);
101 void unregisterCache(Cache* pCache);
102
106 bool trimAll(size_t count = 1);
107
108 virtual void timer(uint64_t delta);
109
110#if THREADS
111 void trimThread();
112#endif
113
114 private:
115 void stopPeriodicWork();
116 void timerTick(uint64_t delta, bool memoryPressure);
117
118 struct TimerStamp {
119 uint64_t elapsed = 0;
120 uint64_t wraps = 0;
121
122 void advance(uint64_t delta);
123 uint64_t since(const TimerStamp& previous) const;
124 };
125
127 bool findNextCache(uint64_t afterId, uint64_t maximumId, Cache*& cache, uint64_t& cacheId,
128 bool timersOnly = false);
130 bool takeTimerStamp(TimerStamp& stamp);
131 void dispatchTimer(Cache* cache, const TimerStamp& stamp);
132
133#if THREADS
135 CacheRequest(Cache* requestCache, OperationBarrier::Lease&& requestLease)
136 : cache(requestCache), lease(pedigree_std::move(requestLease)) {}
137
138 Cache* cache;
140 };
141
142 bool callbackContext() const {
144 }
145
147 bool acquireCache(Cache* cache, uint64_t& generation, OperationBarrier::Lease& lease);
148
150 bool acquireNextCache(uint64_t afterId, uint64_t maximumId, Cache*& cache, uint64_t& cacheId,
151 OperationBarrier::Lease& lease, bool timersOnly = false);
152
154 uint64_t cacheGenerationWatermark();
155#endif
156
158 uint64_t addCacheRequest(Cache* cache, bool asynchronous, CacheConstants::CallbackCause cause,
159 uintptr_t key, uintptr_t location = 0, bool transferredPin = false,
160 bool batch = false);
161
166 virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
167 uint64_t p6, uint64_t p7, uint64_t p8);
168 virtual void cancelRequest(const Request& request);
169
175 virtual bool compareRequests(const Request& a, const Request& b) {
176 // p1 = Cache, p2 = CallbackCause, p3 = key in m_Pages.
177 return (a.p1 == b.p1) && (a.p2 == b.p2) && (a.p3 == b.p3) && (a.p6 == b.p6);
178 }
179
180 static CacheManager* m_Instance;
181
182 Tree<uint64_t, Cache*> m_Caches;
183
186 TimerStamp m_TimerClock;
187 uint64_t m_TrimDelta;
188
189#if THREADS
192
193 Thread* m_pTrimThread;
194 WaitQueue m_TrimWaiters;
195 bool m_bTrimRequested;
196#endif
197
200
201 Timer* m_pTimer;
204};
205
207class EXPORTED_PUBLIC Cache {
208 friend class CacheManager;
209 friend class CacheMemoryTestPeer;
210
211 private:
212 struct CachePage {
214 uintptr_t key;
215
217 uintptr_t location;
218
221 size_t refcnt;
222 size_t writebackPins;
223 size_t mutableLoans;
224 bool directWriteback;
225
226 bool callbackActive;
227#if THREADS
228 Thread* callbackOwner;
229#endif
230
231 enum class EvictionState {
232 None,
233 WriteBack,
234 Draining,
235 Retiring,
236 } evictionState;
237
239 uint64_t checksum[2];
240
243
244 bool writebackFailed;
245 bool externallyWritable;
246 bool writebackIndexed;
247 uint64_t mutationGeneration;
248 uint64_t writtenGeneration;
249 uint64_t writebackEpoch;
250
252 enum Status {
253 // The page is being edited and should not be considered for any
254 // writeback operation.
255 Editing,
256 // The page has been marked as no longer being edited and should
257 // only have a checksum calculated, but no writeback.
258 EditTransition,
259 // The checksum is in flux.
260 ChecksumChanging,
261 // The checksum was in flux but is now stable. A transition into
262 // this state will trigger a writeback.
263 ChecksumStable
264 } status;
265
268 CachePage* pPrev;
269
271 bool checkChecksum(uint64_t other[2]) const;
272
274 bool checkZeroChecksum() const;
275 };
276
277 public:
289 typedef bool (*writeback_t)(CacheConstants::CallbackCause cause, uintptr_t loc, uintptr_t page,
290 void* meta);
291
293 typedef bool (*retirement_writeback_t)(uintptr_t key, uintptr_t page, void* meta);
294
295#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
296 typedef void (*writeback_admission_hook_t)(Cache* cache, uintptr_t key, void* meta);
297#endif
298
299 Cache(size_t pageConstraints = 0);
300 virtual ~Cache();
301
302 enum class ShutdownMode { WriteBack, Discard, DiscardDeferred };
303
317 bool shutdown(ShutdownMode mode = ShutdownMode::WriteBack);
318
325 void setCallback(writeback_t newCallback, void* meta);
326
327 enum class DirtyTracking { Checksum, Explicit };
328
332 void setDirtyTracking(DirtyTracking tracking);
333
335 uintptr_t lookup(uintptr_t key);
336
343 size_t read(uintptr_t offset, size_t length, uintptr_t buffer,
344 bool (*prepare)(uintptr_t, size_t) = nullptr);
345
351 MUST_USE_RESULT bool lookupStable(uintptr_t key, uintptr_t& location, bool wait = false);
352
367 uintptr_t insert(uintptr_t key, bool* alreadyExisted = nullptr);
368
385 uintptr_t insert(uintptr_t key, size_t size, bool* alreadyExisted = nullptr);
386
388 bool exists(uintptr_t key, size_t length);
389
396 bool evict(uintptr_t key);
397
405 MUST_USE_RESULT bool discardEditing(uintptr_t key);
406
417 MUST_USE_RESULT bool retireWriteback(uintptr_t key, retirement_writeback_t callback, void* meta);
418
419#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
421 void setWritebackAdmissionHookForTest(writeback_admission_hook_t hook, void* meta);
422#endif
423
430 bool empty();
431
433 uintptr_t key;
434 size_t references;
435 };
436 enum class DiscardStatus { Ready, NoMemory, Busy, Invalid, Closed };
437
438 class EXPORTED_PUBLIC PreparedDiscard {
439 public:
444 MUST_USE_RESULT bool writeback(retirement_writeback_t callback, void* context);
445 void commit();
446
447 private:
448 friend class Cache;
449 struct Entry;
450 explicit PreparedDiscard(Cache& cache);
451 NOT_COPYABLE_OR_ASSIGNABLE(PreparedDiscard);
452 TerminationDeferral m_TerminationDeferral;
453 Cache& m_Cache;
454 UniqueArray<Entry> m_Entries;
455 size_t m_Count;
456 bool m_Committed;
457#if THREADS
459#endif
460 };
461
466 DiscardStatus prepareDiscardFrom(uintptr_t cutoff, const DiscardReference* references,
467 size_t count, UniquePointer<PreparedDiscard>& result);
468
470 void release(uintptr_t key);
471
484 MUST_USE_RESULT bool pin(uintptr_t key);
485
497 size_t trim(size_t count = 1);
498
505 bool sync(uintptr_t key, bool async);
506
512 MUST_USE_RESULT bool syncAll();
513
514 static constexpr size_t MaxWritebackPages = 64;
516 uintptr_t key;
517 uintptr_t location;
518 };
523 public:
524 DirectWritebackLease() : m_Cache(nullptr), m_Page(nullptr), m_Physical(0) {}
526 release();
527 }
529 DirectWritebackLease& operator=(const DirectWritebackLease&) = delete;
530
531 bool acquire(Cache& cache, uintptr_t key, uintptr_t location);
532 void release();
533 physical_uintptr_t physical() const {
534 return m_Physical;
535 }
536
537 private:
538 Cache* m_Cache;
539 CachePage* m_Page;
540 physical_uintptr_t m_Physical;
541 };
542 using writeback_batch_t = bool (*)(const WritebackPage*, size_t, void*);
544 void setBackgroundWriteback(writeback_batch_t callback);
549 MUST_USE_RESULT bool syncAll(writeback_batch_t callback, void* metadata);
550
559 MUST_USE_RESULT bool syncBatch(const uintptr_t* keys, size_t count, writeback_batch_t callback,
560 void* metadata);
561
567 void triggerChecksum(uintptr_t key);
568
570 void markDirty(uintptr_t key);
571
575 void markExternallyWritable(uintptr_t key);
576
582 MUST_USE_RESULT bool beginMutableLoan(uintptr_t key);
583 void endMutableLoan(uintptr_t key);
584
594 void startAtomic() {
595 if (!ensureUsable("startAtomic")) {
596 return;
597 }
598 m_bInCritical = 1;
599 }
600
604 void endAtomic() {
605 if (!ensureUsable("endAtomic")) {
606 return;
607 }
608 m_bInCritical = 0;
609 }
610
621 void markEditing(uintptr_t key, size_t length = 0);
622
628 void markNoLongerEditing(uintptr_t key, size_t length = 0);
629
630 private:
631 enum class EvictionMode {
632 Ordinary,
633 DiscardBaseReference,
634 DiscardEditing,
635 DiscardDirty,
636 };
637
639 bool map(uintptr_t virt) const;
640
642 bool evict(uintptr_t key, EvictionMode mode, size_t* discardedDirtyPages = nullptr);
643 bool empty(EvictionMode mode, size_t* discardedDirtyPages, bool waitForPins = true);
644
646 bool finishRetirement(CachePage* page, writeback_t callback, void* callbackMeta);
647
648 void releaseWriteback(uintptr_t key);
649
651 void waitForPageEviction(uintptr_t key);
652
654 bool ensureUsable(const char* operation) const;
655
662 size_t lruEvict(bool force = false);
663
667 void linkPage(CachePage* pPage);
668
674 void promotePage(CachePage* pPage);
675
679 void unlinkPage(CachePage* pPage);
680
684 void calculateChecksum(CachePage* pPage);
685
689 bool verifyChecksum(CachePage* pPage, bool replace = false);
690
691 bool tracksChecksum(const CachePage* page) const;
692 bool needsWriteback(CachePage* page);
693 void updateWritebackIndex(CachePage* page);
694 void recordMutation(CachePage* page);
695
699 void checksum(const void* data, size_t len, uint64_t out[2]);
700
702 CacheConstants::CallbackCause cause;
703 writeback_t callback;
704 uintptr_t loc;
705 uintptr_t page;
706 void* meta;
707 UnlikelyLock* cacheLock;
708 };
709
710 public:
715 virtual bool needsPeriodicTimer() const {
716 return __atomic_load_n(&m_PeriodicTimerEnabled, __ATOMIC_ACQUIRE);
717 }
718
726 virtual void timer(uint64_t delta);
727
731 virtual uint64_t executeRequest(uint64_t p1, uint64_t p2, uint64_t p3, uint64_t p4, uint64_t p5,
732 uint64_t p6, uint64_t p7, uint64_t p8);
733
734 private:
736 size_t count = 0;
737 uintptr_t keys[MaxWritebackPages];
738 };
739 void releaseBackgroundWriteback(BackgroundWriteback* batch);
740 bool syncBatchInternal(const uintptr_t* keys, size_t count, writeback_batch_t callback,
741 void* metadata, bool snapshot);
743 bool writebackPage(uintptr_t key, uintptr_t location, bool wait);
744
747
750 DirtyTracking m_DirtyTracking;
751
754
759 CachePage* m_pLruTail;
760
764
767
770
771#if THREADS
774
777#endif
778
780 uint64_t m_ManagerId;
781 CacheManager::TimerStamp m_ManagerTimerStamp;
782 bool m_PeriodicTimerEnabled;
783
785 writeback_t m_Callback;
786 writeback_batch_t m_BackgroundWriteback;
787
791 uint64_t m_WritebackEpoch;
792
795
798
802
805
806#if HOSTED && PEDIGREE_HOSTED_SMOKE_TESTS
807 writeback_admission_hook_t m_WritebackAdmissionHook;
808 void* m_WritebackAdmissionHookMeta;
809#endif
810
811#ifdef STANDALONE_CACHE
813 static void discover_range(uintptr_t& start, uintptr_t& end);
814#endif
815};
816
823class EXPORTED_PUBLIC CachePageGuard {
824 public:
825 CachePageGuard(Cache& cache, uintptr_t location);
826 virtual ~CachePageGuard();
827
828 private:
829 Cache& m_Cache;
830 uintptr_t m_Location;
831};
832
833#endif
Mutex m_CachesLock
Definition Cache.h:191
uint64_t m_NextCacheId
Definition Cache.h:185
virtual bool compareRequests(const Request &a, const Request &b)
Definition Cache.h:175
Atomic< size_t > m_TerminalState
Definition Cache.h:203
bool m_bActive
Definition Cache.h:199
Definition Cache.h:207
static void discover_range(uintptr_t &start, uintptr_t &end)
OperationBarrier m_ManagerOperations
Definition Cache.h:776
static Spinlock m_AllocatorLock
Definition Cache.h:766
void endAtomic()
Definition Cache.h:604
Tree< uintptr_t, CachePage * > m_Pages
Definition Cache.h:746
uint64_t m_Nanoseconds
Definition Cache.h:790
virtual bool needsPeriodicTimer() const
Definition Cache.h:715
WaitQueue m_EvictionWaiters
Definition Cache.h:773
writeback_t m_Callback
Definition Cache.h:785
void startAtomic()
Definition Cache.h:594
size_t m_PageConstraints
Definition Cache.h:804
CachePage * m_pLruHead
Definition Cache.h:758
BloomFilter< uintptr_t > m_PageFilter
Definition Cache.h:753
Atomic< size_t > m_bInCritical
Definition Cache.h:797
static MemoryAllocator m_Allocator
Definition Cache.h:763
Atomic< size_t > m_ShutdownState
Definition Cache.h:801
Tree< uintptr_t, CachePage * > m_WritebackPages
Definition Cache.h:749
Spinlock m_Lock
Definition Cache.h:769
void * m_CallbackMeta
Definition Cache.h:794
uint64_t m_ManagerId
Definition Cache.h:780
Definition Mutex.h:56
virtual void initialise()
virtual void cancelRequest(const Request &request)
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)=0
bool callbackActiveOnCurrentThread() const
virtual void timer(uint64_t delta)=0
A key/value dictionary.
Definition Tree.h:33
bool checksumChanging
Marker to check that a page's contents are in flux.
Definition Cache.h:242
size_t refcnt
Definition Cache.h:221
uintptr_t key
Key for this page.
Definition Cache.h:214
CachePage * pNext
Linked list components for LRU.
Definition Cache.h:267
uintptr_t location
The location of this page in memory.
Definition Cache.h:217
Status
Current page status.
Definition Cache.h:252