1 /* 2 * Copyright 2008-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2003-2007, Axel Dörfler, axeld@pinc-software.de. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 #ifndef _KERNEL_VM_VM_CACHE_H 10 #define _KERNEL_VM_VM_CACHE_H 11 12 13 #include <debug.h> 14 #include <kernel.h> 15 #include <util/DoublyLinkedList.h> 16 #include <vm/vm.h> 17 #include <vm/vm_types.h> 18 19 #include "kernel_debug_config.h" 20 21 22 struct kernel_args; 23 struct ObjectCache; 24 25 26 enum { 27 CACHE_TYPE_RAM = 0, 28 CACHE_TYPE_VNODE, 29 CACHE_TYPE_DEVICE, 30 CACHE_TYPE_NULL 31 }; 32 33 enum { 34 PAGE_EVENT_NOT_BUSY = 0x01 // page not busy anymore 35 }; 36 37 38 extern ObjectCache* gCacheRefObjectCache; 39 extern ObjectCache* gAnonymousCacheObjectCache; 40 extern ObjectCache* gAnonymousNoSwapCacheObjectCache; 41 extern ObjectCache* gVnodeCacheObjectCache; 42 extern ObjectCache* gDeviceCacheObjectCache; 43 extern ObjectCache* gNullCacheObjectCache; 44 45 46 struct VMCachePagesTreeDefinition { 47 typedef page_num_t KeyType; 48 typedef vm_page NodeType; 49 50 static page_num_t GetKey(const NodeType* node) 51 { 52 return node->cache_offset; 53 } 54 55 static SplayTreeLink<NodeType>* GetLink(NodeType* node) 56 { 57 return &node->cache_link; 58 } 59 60 static int Compare(page_num_t key, const NodeType* node) 61 { 62 return key == node->cache_offset ? 0 63 : (key < node->cache_offset ? -1 : 1); 64 } 65 66 static NodeType** GetListLink(NodeType* node) 67 { 68 return &node->cache_next; 69 } 70 }; 71 72 typedef IteratableSplayTree<VMCachePagesTreeDefinition> VMCachePagesTree; 73 74 75 struct VMCache : public DoublyLinkedListLinkImpl<VMCache> { 76 public: 77 typedef DoublyLinkedList<VMCache> ConsumerList; 78 79 public: 80 VMCache(); 81 virtual ~VMCache(); 82 83 status_t Init(uint32 cacheType, uint32 allocationFlags); 84 85 virtual void Delete(); 86 87 inline bool Lock(); 88 inline bool TryLock(); 89 inline bool SwitchLock(mutex* from); 90 inline bool SwitchFromReadLock(rw_lock* from); 91 void Unlock(bool consumerLocked = false); 92 inline void AssertLocked(); 93 94 inline void AcquireRefLocked(); 95 inline void AcquireRef(); 96 inline void ReleaseRefLocked(); 97 inline void ReleaseRef(); 98 inline void ReleaseRefAndUnlock( 99 bool consumerLocked = false); 100 101 inline VMCacheRef* CacheRef() const { return fCacheRef; } 102 103 void WaitForPageEvents(vm_page* page, uint32 events, 104 bool relock); 105 void NotifyPageEvents(vm_page* page, uint32 events) 106 { if (fPageEventWaiters != NULL) 107 _NotifyPageEvents(page, events); } 108 inline void MarkPageUnbusy(vm_page* page); 109 110 vm_page* LookupPage(off_t offset); 111 void InsertPage(vm_page* page, off_t offset); 112 void RemovePage(vm_page* page); 113 void MovePage(vm_page* page, off_t offset); 114 void MovePage(vm_page* page); 115 void MoveAllPages(VMCache* fromCache); 116 117 inline page_num_t WiredPagesCount() const; 118 inline void IncrementWiredPagesCount(); 119 inline void DecrementWiredPagesCount(); 120 121 virtual int32 GuardSize() { return 0; } 122 123 void AddConsumer(VMCache* consumer); 124 125 status_t InsertAreaLocked(VMArea* area); 126 status_t RemoveArea(VMArea* area); 127 void TransferAreas(VMCache* fromCache); 128 uint32 CountWritableAreas(VMArea* ignoreArea) const; 129 130 status_t WriteModified(); 131 status_t SetMinimalCommitment(off_t commitment, 132 int priority); 133 virtual status_t Resize(off_t newSize, int priority); 134 virtual status_t Rebase(off_t newBase, int priority); 135 virtual status_t Adopt(VMCache* source, off_t offset, off_t size, 136 off_t newOffset); 137 138 status_t FlushAndRemoveAllPages(); 139 140 void* UserData() { return fUserData; } 141 void SetUserData(void* data) { fUserData = data; } 142 // Settable by the lock owner and valid as 143 // long as the lock is owned. 144 145 // for debugging only 146 int32 RefCount() const 147 { return fRefCount; } 148 149 // backing store operations 150 virtual status_t Commit(off_t size, int priority); 151 virtual bool HasPage(off_t offset); 152 153 virtual status_t Read(off_t offset, const generic_io_vec *vecs, 154 size_t count, uint32 flags, 155 generic_size_t *_numBytes); 156 virtual status_t Write(off_t offset, const generic_io_vec *vecs, 157 size_t count, uint32 flags, 158 generic_size_t *_numBytes); 159 virtual status_t WriteAsync(off_t offset, 160 const generic_io_vec* vecs, size_t count, 161 generic_size_t numBytes, uint32 flags, 162 AsyncIOCallback* callback); 163 virtual bool CanWritePage(off_t offset); 164 165 virtual int32 MaxPagesPerWrite() const 166 { return -1; } // no restriction 167 virtual int32 MaxPagesPerAsyncWrite() const 168 { return -1; } // no restriction 169 170 virtual status_t Fault(struct VMAddressSpace *aspace, 171 off_t offset); 172 173 virtual void Merge(VMCache* source); 174 175 virtual status_t AcquireUnreferencedStoreRef(); 176 virtual void AcquireStoreRef(); 177 virtual void ReleaseStoreRef(); 178 179 virtual bool DebugHasPage(off_t offset); 180 vm_page* DebugLookupPage(off_t offset); 181 182 virtual void Dump(bool showPages) const; 183 184 protected: 185 virtual void DeleteObject() = 0; 186 187 public: 188 VMArea* areas; 189 ConsumerList consumers; 190 // list of caches that use this cache as a source 191 VMCachePagesTree pages; 192 VMCache* source; 193 off_t virtual_base; 194 off_t virtual_end; 195 off_t committed_size; 196 // TODO: Remove! 197 uint32 page_count; 198 uint32 temporary : 1; 199 uint32 type : 6; 200 201 #if DEBUG_CACHE_LIST 202 VMCache* debug_previous; 203 VMCache* debug_next; 204 #endif 205 206 private: 207 struct PageEventWaiter; 208 friend struct VMCacheRef; 209 210 private: 211 void _NotifyPageEvents(vm_page* page, uint32 events); 212 213 inline bool _IsMergeable() const; 214 215 void _MergeWithOnlyConsumer(); 216 void _RemoveConsumer(VMCache* consumer); 217 218 private: 219 int32 fRefCount; 220 mutex fLock; 221 PageEventWaiter* fPageEventWaiters; 222 void* fUserData; 223 VMCacheRef* fCacheRef; 224 page_num_t fWiredPagesCount; 225 }; 226 227 228 #if DEBUG_CACHE_LIST 229 extern VMCache* gDebugCacheList; 230 #endif 231 232 233 class VMCacheFactory { 234 public: 235 static status_t CreateAnonymousCache(VMCache*& cache, 236 bool canOvercommit, int32 numPrecommittedPages, 237 int32 numGuardPages, bool swappable, 238 int priority); 239 static status_t CreateVnodeCache(VMCache*& cache, 240 struct vnode* vnode); 241 static status_t CreateDeviceCache(VMCache*& cache, 242 addr_t baseAddress); 243 static status_t CreateNullCache(int priority, VMCache*& cache); 244 }; 245 246 247 248 bool 249 VMCache::Lock() 250 { 251 return mutex_lock(&fLock) == B_OK; 252 } 253 254 255 bool 256 VMCache::TryLock() 257 { 258 return mutex_trylock(&fLock) == B_OK; 259 } 260 261 262 bool 263 VMCache::SwitchLock(mutex* from) 264 { 265 return mutex_switch_lock(from, &fLock) == B_OK; 266 } 267 268 269 bool 270 VMCache::SwitchFromReadLock(rw_lock* from) 271 { 272 return mutex_switch_from_read_lock(from, &fLock) == B_OK; 273 } 274 275 276 void 277 VMCache::AssertLocked() 278 { 279 ASSERT_LOCKED_MUTEX(&fLock); 280 } 281 282 283 void 284 VMCache::AcquireRefLocked() 285 { 286 ASSERT_LOCKED_MUTEX(&fLock); 287 288 fRefCount++; 289 } 290 291 292 void 293 VMCache::AcquireRef() 294 { 295 Lock(); 296 fRefCount++; 297 Unlock(); 298 } 299 300 301 void 302 VMCache::ReleaseRefLocked() 303 { 304 ASSERT_LOCKED_MUTEX(&fLock); 305 306 fRefCount--; 307 } 308 309 310 void 311 VMCache::ReleaseRef() 312 { 313 Lock(); 314 fRefCount--; 315 Unlock(); 316 } 317 318 319 void 320 VMCache::ReleaseRefAndUnlock(bool consumerLocked) 321 { 322 ReleaseRefLocked(); 323 Unlock(consumerLocked); 324 } 325 326 327 void 328 VMCache::MarkPageUnbusy(vm_page* page) 329 { 330 ASSERT(page->busy); 331 page->busy = false; 332 NotifyPageEvents(page, PAGE_EVENT_NOT_BUSY); 333 } 334 335 336 page_num_t 337 VMCache::WiredPagesCount() const 338 { 339 return fWiredPagesCount; 340 } 341 342 343 void 344 VMCache::IncrementWiredPagesCount() 345 { 346 ASSERT(fWiredPagesCount < page_count); 347 348 fWiredPagesCount++; 349 } 350 351 352 void 353 VMCache::DecrementWiredPagesCount() 354 { 355 ASSERT(fWiredPagesCount > 0); 356 357 fWiredPagesCount--; 358 } 359 360 361 // vm_page methods implemented here to avoid VMCache.h inclusion in vm_types.h 362 363 inline void 364 vm_page::IncrementWiredCount() 365 { 366 if (fWiredCount++ == 0) 367 cache_ref->cache->IncrementWiredPagesCount(); 368 } 369 370 371 inline void 372 vm_page::DecrementWiredCount() 373 { 374 ASSERT(fWiredCount > 0); 375 376 if (--fWiredCount == 0) 377 cache_ref->cache->DecrementWiredPagesCount(); 378 } 379 380 381 #ifdef __cplusplus 382 extern "C" { 383 #endif 384 385 status_t vm_cache_init(struct kernel_args *args); 386 void vm_cache_init_post_heap(); 387 struct VMCache *vm_cache_acquire_locked_page_cache(struct vm_page *page, 388 bool dontWait); 389 390 #ifdef __cplusplus 391 } 392 #endif 393 394 395 #endif /* _KERNEL_VM_VM_CACHE_H */ 396