1 /* 2 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2008, 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 10 11 #include <vm/VMCache.h> 12 13 #include <stddef.h> 14 #include <stdlib.h> 15 16 #include <algorithm> 17 18 #include <arch/cpu.h> 19 #include <condition_variable.h> 20 #include <heap.h> 21 #include <int.h> 22 #include <kernel.h> 23 #include <slab/Slab.h> 24 #include <smp.h> 25 #include <thread.h> 26 #include <tracing.h> 27 #include <util/AutoLock.h> 28 #include <vfs.h> 29 #include <vm/vm.h> 30 #include <vm/vm_page.h> 31 #include <vm/vm_priv.h> 32 #include <vm/vm_types.h> 33 #include <vm/VMAddressSpace.h> 34 #include <vm/VMArea.h> 35 36 // needed for the factory only 37 #include "VMAnonymousCache.h" 38 #include "VMAnonymousNoSwapCache.h" 39 #include "VMDeviceCache.h" 40 #include "VMNullCache.h" 41 #include "../cache/vnode_store.h" 42 43 44 //#define TRACE_VM_CACHE 45 #ifdef TRACE_VM_CACHE 46 # define TRACE(x) dprintf x 47 #else 48 # define TRACE(x) ; 49 #endif 50 51 52 #if DEBUG_CACHE_LIST 53 VMCache* gDebugCacheList; 54 #endif 55 static rw_lock sCacheListLock = RW_LOCK_INITIALIZER("global VMCache list"); 56 // The lock is also needed when the debug feature is disabled. 57 58 ObjectCache* gCacheRefObjectCache; 59 #if ENABLE_SWAP_SUPPORT 60 ObjectCache* gAnonymousCacheObjectCache; 61 #endif 62 ObjectCache* gAnonymousNoSwapCacheObjectCache; 63 ObjectCache* gVnodeCacheObjectCache; 64 ObjectCache* gDeviceCacheObjectCache; 65 ObjectCache* gNullCacheObjectCache; 66 67 68 struct VMCache::PageEventWaiter { 69 Thread* thread; 70 PageEventWaiter* next; 71 vm_page* page; 72 uint32 events; 73 }; 74 75 76 #if VM_CACHE_TRACING 77 78 namespace VMCacheTracing { 79 80 class VMCacheTraceEntry : public AbstractTraceEntry { 81 public: 82 VMCacheTraceEntry(VMCache* cache) 83 : 84 fCache(cache) 85 { 86 #if VM_CACHE_TRACING_STACK_TRACE 87 fStackTrace = capture_tracing_stack_trace( 88 VM_CACHE_TRACING_STACK_TRACE, 0, true); 89 // Don't capture userland stack trace to avoid potential 90 // deadlocks. 91 #endif 92 } 93 94 #if VM_CACHE_TRACING_STACK_TRACE 95 virtual void DumpStackTrace(TraceOutput& out) 96 { 97 out.PrintStackTrace(fStackTrace); 98 } 99 #endif 100 101 VMCache* Cache() const 102 { 103 return fCache; 104 } 105 106 protected: 107 VMCache* fCache; 108 #if VM_CACHE_TRACING_STACK_TRACE 109 tracing_stack_trace* fStackTrace; 110 #endif 111 }; 112 113 114 class Create : public VMCacheTraceEntry { 115 public: 116 Create(VMCache* cache) 117 : 118 VMCacheTraceEntry(cache) 119 { 120 Initialized(); 121 } 122 123 virtual void AddDump(TraceOutput& out) 124 { 125 out.Print("vm cache create: -> cache: %p", fCache); 126 } 127 }; 128 129 130 class Delete : public VMCacheTraceEntry { 131 public: 132 Delete(VMCache* cache) 133 : 134 VMCacheTraceEntry(cache) 135 { 136 Initialized(); 137 } 138 139 virtual void AddDump(TraceOutput& out) 140 { 141 out.Print("vm cache delete: cache: %p", fCache); 142 } 143 }; 144 145 146 class SetMinimalCommitment : public VMCacheTraceEntry { 147 public: 148 SetMinimalCommitment(VMCache* cache, off_t commitment) 149 : 150 VMCacheTraceEntry(cache), 151 fOldCommitment(cache->committed_size), 152 fCommitment(commitment) 153 { 154 Initialized(); 155 } 156 157 virtual void AddDump(TraceOutput& out) 158 { 159 out.Print("vm cache set min commitment: cache: %p, " 160 "commitment: %" B_PRIdOFF " -> %" B_PRIdOFF, fCache, 161 fOldCommitment, fCommitment); 162 } 163 164 private: 165 off_t fOldCommitment; 166 off_t fCommitment; 167 }; 168 169 170 class Resize : public VMCacheTraceEntry { 171 public: 172 Resize(VMCache* cache, off_t size) 173 : 174 VMCacheTraceEntry(cache), 175 fOldSize(cache->virtual_end), 176 fSize(size) 177 { 178 Initialized(); 179 } 180 181 virtual void AddDump(TraceOutput& out) 182 { 183 out.Print("vm cache resize: cache: %p, size: %" B_PRIdOFF " -> %" 184 B_PRIdOFF, fCache, fOldSize, fSize); 185 } 186 187 private: 188 off_t fOldSize; 189 off_t fSize; 190 }; 191 192 193 class Rebase : public VMCacheTraceEntry { 194 public: 195 Rebase(VMCache* cache, off_t base) 196 : 197 VMCacheTraceEntry(cache), 198 fOldBase(cache->virtual_base), 199 fBase(base) 200 { 201 Initialized(); 202 } 203 204 virtual void AddDump(TraceOutput& out) 205 { 206 out.Print("vm cache rebase: cache: %p, base: %lld -> %lld", fCache, 207 fOldBase, fBase); 208 } 209 210 private: 211 off_t fOldBase; 212 off_t fBase; 213 }; 214 215 216 class AddConsumer : public VMCacheTraceEntry { 217 public: 218 AddConsumer(VMCache* cache, VMCache* consumer) 219 : 220 VMCacheTraceEntry(cache), 221 fConsumer(consumer) 222 { 223 Initialized(); 224 } 225 226 virtual void AddDump(TraceOutput& out) 227 { 228 out.Print("vm cache add consumer: cache: %p, consumer: %p", fCache, 229 fConsumer); 230 } 231 232 VMCache* Consumer() const 233 { 234 return fConsumer; 235 } 236 237 private: 238 VMCache* fConsumer; 239 }; 240 241 242 class RemoveConsumer : public VMCacheTraceEntry { 243 public: 244 RemoveConsumer(VMCache* cache, VMCache* consumer) 245 : 246 VMCacheTraceEntry(cache), 247 fConsumer(consumer) 248 { 249 Initialized(); 250 } 251 252 virtual void AddDump(TraceOutput& out) 253 { 254 out.Print("vm cache remove consumer: cache: %p, consumer: %p", 255 fCache, fConsumer); 256 } 257 258 private: 259 VMCache* fConsumer; 260 }; 261 262 263 class Merge : public VMCacheTraceEntry { 264 public: 265 Merge(VMCache* cache, VMCache* consumer) 266 : 267 VMCacheTraceEntry(cache), 268 fConsumer(consumer) 269 { 270 Initialized(); 271 } 272 273 virtual void AddDump(TraceOutput& out) 274 { 275 out.Print("vm cache merge with consumer: cache: %p, consumer: %p", 276 fCache, fConsumer); 277 } 278 279 private: 280 VMCache* fConsumer; 281 }; 282 283 284 class InsertArea : public VMCacheTraceEntry { 285 public: 286 InsertArea(VMCache* cache, VMArea* area) 287 : 288 VMCacheTraceEntry(cache), 289 fArea(area) 290 { 291 Initialized(); 292 } 293 294 virtual void AddDump(TraceOutput& out) 295 { 296 out.Print("vm cache insert area: cache: %p, area: %p", fCache, 297 fArea); 298 } 299 300 VMArea* Area() const 301 { 302 return fArea; 303 } 304 305 private: 306 VMArea* fArea; 307 }; 308 309 310 class RemoveArea : public VMCacheTraceEntry { 311 public: 312 RemoveArea(VMCache* cache, VMArea* area) 313 : 314 VMCacheTraceEntry(cache), 315 fArea(area) 316 { 317 Initialized(); 318 } 319 320 virtual void AddDump(TraceOutput& out) 321 { 322 out.Print("vm cache remove area: cache: %p, area: %p", fCache, 323 fArea); 324 } 325 326 private: 327 VMArea* fArea; 328 }; 329 330 } // namespace VMCacheTracing 331 332 # define T(x) new(std::nothrow) VMCacheTracing::x; 333 334 # if VM_CACHE_TRACING >= 2 335 336 namespace VMCacheTracing { 337 338 class InsertPage : public VMCacheTraceEntry { 339 public: 340 InsertPage(VMCache* cache, vm_page* page, off_t offset) 341 : 342 VMCacheTraceEntry(cache), 343 fPage(page), 344 fOffset(offset) 345 { 346 Initialized(); 347 } 348 349 virtual void AddDump(TraceOutput& out) 350 { 351 out.Print("vm cache insert page: cache: %p, page: %p, offset: %" 352 B_PRIdOFF, fCache, fPage, fOffset); 353 } 354 355 private: 356 vm_page* fPage; 357 off_t fOffset; 358 }; 359 360 361 class RemovePage : public VMCacheTraceEntry { 362 public: 363 RemovePage(VMCache* cache, vm_page* page) 364 : 365 VMCacheTraceEntry(cache), 366 fPage(page) 367 { 368 Initialized(); 369 } 370 371 virtual void AddDump(TraceOutput& out) 372 { 373 out.Print("vm cache remove page: cache: %p, page: %p", fCache, 374 fPage); 375 } 376 377 private: 378 vm_page* fPage; 379 }; 380 381 } // namespace VMCacheTracing 382 383 # define T2(x) new(std::nothrow) VMCacheTracing::x; 384 # else 385 # define T2(x) ; 386 # endif 387 #else 388 # define T(x) ; 389 # define T2(x) ; 390 #endif 391 392 393 // #pragma mark - debugger commands 394 395 396 #if VM_CACHE_TRACING 397 398 399 static void* 400 cache_stack_find_area_cache(const TraceEntryIterator& baseIterator, void* area) 401 { 402 using namespace VMCacheTracing; 403 404 // find the previous "insert area" entry for the given area 405 TraceEntryIterator iterator = baseIterator; 406 TraceEntry* entry = iterator.Current(); 407 while (entry != NULL) { 408 if (InsertArea* insertAreaEntry = dynamic_cast<InsertArea*>(entry)) { 409 if (insertAreaEntry->Area() == area) 410 return insertAreaEntry->Cache(); 411 } 412 413 entry = iterator.Previous(); 414 } 415 416 return NULL; 417 } 418 419 420 static void* 421 cache_stack_find_consumer(const TraceEntryIterator& baseIterator, void* cache) 422 { 423 using namespace VMCacheTracing; 424 425 // find the previous "add consumer" or "create" entry for the given cache 426 TraceEntryIterator iterator = baseIterator; 427 TraceEntry* entry = iterator.Current(); 428 while (entry != NULL) { 429 if (Create* createEntry = dynamic_cast<Create*>(entry)) { 430 if (createEntry->Cache() == cache) 431 return NULL; 432 } else if (AddConsumer* addEntry = dynamic_cast<AddConsumer*>(entry)) { 433 if (addEntry->Consumer() == cache) 434 return addEntry->Cache(); 435 } 436 437 entry = iterator.Previous(); 438 } 439 440 return NULL; 441 } 442 443 444 static int 445 command_cache_stack(int argc, char** argv) 446 { 447 if (argc < 3 || argc > 4) { 448 print_debugger_command_usage(argv[0]); 449 return 0; 450 } 451 452 bool isArea = false; 453 454 int argi = 1; 455 if (argc == 4) { 456 if (strcmp(argv[argi], "area") != 0) { 457 print_debugger_command_usage(argv[0]); 458 return 0; 459 } 460 461 argi++; 462 isArea = true; 463 } 464 465 uint64 addressValue; 466 uint64 debugEntryIndex; 467 if (!evaluate_debug_expression(argv[argi++], &addressValue, false) 468 || !evaluate_debug_expression(argv[argi++], &debugEntryIndex, false)) { 469 return 0; 470 } 471 472 TraceEntryIterator baseIterator; 473 if (baseIterator.MoveTo((int32)debugEntryIndex) == NULL) { 474 kprintf("Invalid tracing entry index %" B_PRIu64 "\n", debugEntryIndex); 475 return 0; 476 } 477 478 void* address = (void*)(addr_t)addressValue; 479 480 kprintf("cache stack for %s %p at %" B_PRIu64 ":\n", 481 isArea ? "area" : "cache", address, debugEntryIndex); 482 if (isArea) { 483 address = cache_stack_find_area_cache(baseIterator, address); 484 if (address == NULL) { 485 kprintf(" cache not found\n"); 486 return 0; 487 } 488 } 489 490 while (address != NULL) { 491 kprintf(" %p\n", address); 492 address = cache_stack_find_consumer(baseIterator, address); 493 } 494 495 return 0; 496 } 497 498 499 #endif // VM_CACHE_TRACING 500 501 502 // #pragma mark - 503 504 505 status_t 506 vm_cache_init(kernel_args* args) 507 { 508 // Create object caches for the structures we allocate here. 509 gCacheRefObjectCache = create_object_cache("cache refs", sizeof(VMCacheRef), 510 0, NULL, NULL, NULL); 511 #if ENABLE_SWAP_SUPPORT 512 gAnonymousCacheObjectCache = create_object_cache("anon caches", 513 sizeof(VMAnonymousCache), 0, NULL, NULL, NULL); 514 #endif 515 gAnonymousNoSwapCacheObjectCache = create_object_cache( 516 "anon no-swap caches", sizeof(VMAnonymousNoSwapCache), 0, NULL, NULL, 517 NULL); 518 gVnodeCacheObjectCache = create_object_cache("vnode caches", 519 sizeof(VMVnodeCache), 0, NULL, NULL, NULL); 520 gDeviceCacheObjectCache = create_object_cache("device caches", 521 sizeof(VMDeviceCache), 0, NULL, NULL, NULL); 522 gNullCacheObjectCache = create_object_cache("null caches", 523 sizeof(VMNullCache), 0, NULL, NULL, NULL); 524 525 if (gCacheRefObjectCache == NULL 526 #if ENABLE_SWAP_SUPPORT 527 || gAnonymousCacheObjectCache == NULL 528 #endif 529 || gAnonymousNoSwapCacheObjectCache == NULL 530 || gVnodeCacheObjectCache == NULL 531 || gDeviceCacheObjectCache == NULL 532 || gNullCacheObjectCache == NULL) { 533 panic("vm_cache_init(): Failed to create object caches!"); 534 return B_NO_MEMORY; 535 } 536 537 return B_OK; 538 } 539 540 541 void 542 vm_cache_init_post_heap() 543 { 544 #if VM_CACHE_TRACING 545 add_debugger_command_etc("cache_stack", &command_cache_stack, 546 "List the ancestors (sources) of a VMCache at the time given by " 547 "tracing entry index", 548 "[ \"area\" ] <address> <tracing entry index>\n" 549 "All ancestors (sources) of a given VMCache at the time given by the\n" 550 "tracing entry index are listed. If \"area\" is given the supplied\n" 551 "address is an area instead of a cache address. The listing will\n" 552 "start with the area's cache at that point.\n", 553 0); 554 #endif // VM_CACHE_TRACING 555 } 556 557 558 VMCache* 559 vm_cache_acquire_locked_page_cache(vm_page* page, bool dontWait) 560 { 561 rw_lock_read_lock(&sCacheListLock); 562 563 while (true) { 564 VMCacheRef* cacheRef = page->CacheRef(); 565 if (cacheRef == NULL) { 566 rw_lock_read_unlock(&sCacheListLock); 567 return NULL; 568 } 569 570 VMCache* cache = cacheRef->cache; 571 if (dontWait) { 572 if (!cache->TryLock()) { 573 rw_lock_read_unlock(&sCacheListLock); 574 return NULL; 575 } 576 } else { 577 if (!cache->SwitchFromReadLock(&sCacheListLock)) { 578 // cache has been deleted 579 rw_lock_read_lock(&sCacheListLock); 580 continue; 581 } 582 rw_lock_read_lock(&sCacheListLock); 583 } 584 585 if (cache == page->Cache()) { 586 rw_lock_read_unlock(&sCacheListLock); 587 cache->AcquireRefLocked(); 588 return cache; 589 } 590 591 // the cache changed in the meantime 592 cache->Unlock(); 593 } 594 } 595 596 597 // #pragma mark - VMCache 598 599 600 VMCacheRef::VMCacheRef(VMCache* cache) 601 : 602 cache(cache), 603 ref_count(1) 604 { 605 } 606 607 608 // #pragma mark - VMCache 609 610 611 bool 612 VMCache::_IsMergeable() const 613 { 614 return areas == NULL && temporary && !unmergeable 615 && !consumers.IsEmpty() && consumers.Head() == consumers.Tail(); 616 } 617 618 619 VMCache::VMCache() 620 : 621 fCacheRef(NULL) 622 { 623 } 624 625 626 VMCache::~VMCache() 627 { 628 object_cache_delete(gCacheRefObjectCache, fCacheRef); 629 } 630 631 632 status_t 633 VMCache::Init(uint32 cacheType, uint32 allocationFlags) 634 { 635 mutex_init(&fLock, "VMCache"); 636 637 areas = NULL; 638 fRefCount = 1; 639 source = NULL; 640 virtual_base = 0; 641 virtual_end = 0; 642 committed_size = 0; 643 temporary = 0; 644 unmergeable = 0; 645 page_count = 0; 646 fWiredPagesCount = 0; 647 type = cacheType; 648 fPageEventWaiters = NULL; 649 650 #if DEBUG_CACHE_LIST 651 debug_previous = NULL; 652 debug_next = NULL; 653 // initialize in case the following fails 654 #endif 655 656 fCacheRef = new(gCacheRefObjectCache, allocationFlags) VMCacheRef(this); 657 if (fCacheRef == NULL) 658 return B_NO_MEMORY; 659 660 #if DEBUG_CACHE_LIST 661 rw_lock_write_lock(&sCacheListLock); 662 663 if (gDebugCacheList != NULL) 664 gDebugCacheList->debug_previous = this; 665 debug_next = gDebugCacheList; 666 gDebugCacheList = this; 667 668 rw_lock_write_unlock(&sCacheListLock); 669 #endif 670 671 return B_OK; 672 } 673 674 675 void 676 VMCache::Delete() 677 { 678 if (areas != NULL) 679 panic("cache %p to be deleted still has areas", this); 680 if (!consumers.IsEmpty()) 681 panic("cache %p to be deleted still has consumers", this); 682 683 T(Delete(this)); 684 685 // free all of the pages in the cache 686 while (vm_page* page = pages.Root()) { 687 if (!page->mappings.IsEmpty() || page->WiredCount() != 0) { 688 panic("remove page %p from cache %p: page still has mappings!\n" 689 "@!page %p; cache %p", page, this, page, this); 690 } 691 692 // remove it 693 pages.Remove(page); 694 page->SetCacheRef(NULL); 695 696 TRACE(("vm_cache_release_ref: freeing page 0x%lx\n", 697 page->physical_page_number)); 698 DEBUG_PAGE_ACCESS_START(page); 699 vm_page_free(this, page); 700 } 701 702 // remove the ref to the source 703 if (source) 704 source->_RemoveConsumer(this); 705 706 // We lock and unlock the sCacheListLock, even if the DEBUG_CACHE_LIST is 707 // not enabled. This synchronization point is needed for 708 // vm_cache_acquire_locked_page_cache(). 709 rw_lock_write_lock(&sCacheListLock); 710 711 #if DEBUG_CACHE_LIST 712 if (debug_previous) 713 debug_previous->debug_next = debug_next; 714 if (debug_next) 715 debug_next->debug_previous = debug_previous; 716 if (this == gDebugCacheList) 717 gDebugCacheList = debug_next; 718 #endif 719 720 mutex_destroy(&fLock); 721 722 rw_lock_write_unlock(&sCacheListLock); 723 724 DeleteObject(); 725 } 726 727 728 void 729 VMCache::Unlock(bool consumerLocked) 730 { 731 while (fRefCount == 1 && _IsMergeable()) { 732 VMCache* consumer = consumers.Head(); 733 if (consumerLocked) { 734 _MergeWithOnlyConsumer(); 735 } else if (consumer->TryLock()) { 736 _MergeWithOnlyConsumer(); 737 consumer->Unlock(); 738 } else { 739 // Someone else has locked the consumer ATM. Unlock this cache and 740 // wait for the consumer lock. Increment the cache's ref count 741 // temporarily, so that no one else will try what we are doing or 742 // delete the cache. 743 fRefCount++; 744 bool consumerLockedTemp = consumer->SwitchLock(&fLock); 745 Lock(); 746 fRefCount--; 747 748 if (consumerLockedTemp) { 749 if (fRefCount == 1 && _IsMergeable() 750 && consumer == consumers.Head()) { 751 // nothing has changed in the meantime -- merge 752 _MergeWithOnlyConsumer(); 753 } 754 755 consumer->Unlock(); 756 } 757 } 758 } 759 760 if (fRefCount == 0) { 761 // delete this cache 762 Delete(); 763 } else 764 mutex_unlock(&fLock); 765 } 766 767 768 vm_page* 769 VMCache::LookupPage(off_t offset) 770 { 771 AssertLocked(); 772 773 vm_page* page = pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 774 775 #if KDEBUG 776 if (page != NULL && page->Cache() != this) 777 panic("page %p not in cache %p\n", page, this); 778 #endif 779 780 return page; 781 } 782 783 784 void 785 VMCache::InsertPage(vm_page* page, off_t offset) 786 { 787 TRACE(("VMCache::InsertPage(): cache %p, page %p, offset %" B_PRIdOFF "\n", 788 this, page, offset)); 789 AssertLocked(); 790 791 if (page->CacheRef() != NULL) { 792 panic("insert page %p into cache %p: page cache is set to %p\n", 793 page, this, page->Cache()); 794 } 795 796 T2(InsertPage(this, page, offset)); 797 798 page->cache_offset = (page_num_t)(offset >> PAGE_SHIFT); 799 page_count++; 800 page->SetCacheRef(fCacheRef); 801 802 #if KDEBUG 803 vm_page* otherPage = pages.Lookup(page->cache_offset); 804 if (otherPage != NULL) { 805 panic("VMCache::InsertPage(): there's already page %p with cache " 806 "offset %" B_PRIuPHYSADDR " in cache %p; inserting page %p", 807 otherPage, page->cache_offset, this, page); 808 } 809 #endif // KDEBUG 810 811 pages.Insert(page); 812 813 if (page->WiredCount() > 0) 814 IncrementWiredPagesCount(); 815 } 816 817 818 /*! Removes the vm_page from this cache. Of course, the page must 819 really be in this cache or evil things will happen. 820 The cache lock must be held. 821 */ 822 void 823 VMCache::RemovePage(vm_page* page) 824 { 825 TRACE(("VMCache::RemovePage(): cache %p, page %p\n", this, page)); 826 AssertLocked(); 827 828 if (page->Cache() != this) { 829 panic("remove page %p from cache %p: page cache is set to %p\n", page, 830 this, page->Cache()); 831 } 832 833 T2(RemovePage(this, page)); 834 835 pages.Remove(page); 836 page_count--; 837 page->SetCacheRef(NULL); 838 839 if (page->WiredCount() > 0) 840 DecrementWiredPagesCount(); 841 } 842 843 844 /*! Moves the given page from its current cache inserts it into this cache 845 at the given offset. 846 Both caches must be locked. 847 */ 848 void 849 VMCache::MovePage(vm_page* page, off_t offset) 850 { 851 VMCache* oldCache = page->Cache(); 852 853 AssertLocked(); 854 oldCache->AssertLocked(); 855 856 // remove from old cache 857 oldCache->pages.Remove(page); 858 oldCache->page_count--; 859 T2(RemovePage(oldCache, page)); 860 861 // change the offset 862 page->cache_offset = offset >> PAGE_SHIFT; 863 864 // insert here 865 pages.Insert(page); 866 page_count++; 867 page->SetCacheRef(fCacheRef); 868 869 if (page->WiredCount() > 0) { 870 IncrementWiredPagesCount(); 871 oldCache->DecrementWiredPagesCount(); 872 } 873 874 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 875 } 876 877 /*! Moves the given page from its current cache inserts it into this cache. 878 Both caches must be locked. 879 */ 880 void 881 VMCache::MovePage(vm_page* page) 882 { 883 MovePage(page, page->cache_offset << PAGE_SHIFT); 884 } 885 886 887 /*! Moves all pages from the given cache to this one. 888 Both caches must be locked. This cache must be empty. 889 */ 890 void 891 VMCache::MoveAllPages(VMCache* fromCache) 892 { 893 AssertLocked(); 894 fromCache->AssertLocked(); 895 ASSERT(page_count == 0); 896 897 std::swap(fromCache->pages, pages); 898 page_count = fromCache->page_count; 899 fromCache->page_count = 0; 900 fWiredPagesCount = fromCache->fWiredPagesCount; 901 fromCache->fWiredPagesCount = 0; 902 903 // swap the VMCacheRefs 904 rw_lock_write_lock(&sCacheListLock); 905 std::swap(fCacheRef, fromCache->fCacheRef); 906 fCacheRef->cache = this; 907 fromCache->fCacheRef->cache = fromCache; 908 rw_lock_write_unlock(&sCacheListLock); 909 910 #if VM_CACHE_TRACING >= 2 911 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 912 vm_page* page = it.Next();) { 913 T2(RemovePage(fromCache, page)); 914 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 915 } 916 #endif 917 } 918 919 920 /*! Waits until one or more events happened for a given page which belongs to 921 this cache. 922 The cache must be locked. It will be unlocked by the method. \a relock 923 specifies whether the method shall re-lock the cache before returning. 924 \param page The page for which to wait. 925 \param events The mask of events the caller is interested in. 926 \param relock If \c true, the cache will be locked when returning, 927 otherwise it won't be locked. 928 */ 929 void 930 VMCache::WaitForPageEvents(vm_page* page, uint32 events, bool relock) 931 { 932 PageEventWaiter waiter; 933 waiter.thread = thread_get_current_thread(); 934 waiter.next = fPageEventWaiters; 935 waiter.page = page; 936 waiter.events = events; 937 938 fPageEventWaiters = &waiter; 939 940 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_OTHER_OBJECT, page); 941 942 Unlock(); 943 thread_block(); 944 945 if (relock) 946 Lock(); 947 } 948 949 950 /*! Makes this case the source of the \a consumer cache, 951 and adds the \a consumer to its list. 952 This also grabs a reference to the source cache. 953 Assumes you have the cache and the consumer's lock held. 954 */ 955 void 956 VMCache::AddConsumer(VMCache* consumer) 957 { 958 TRACE(("add consumer vm cache %p to cache %p\n", consumer, this)); 959 AssertLocked(); 960 consumer->AssertLocked(); 961 962 T(AddConsumer(this, consumer)); 963 964 consumer->source = this; 965 consumers.Add(consumer); 966 967 AcquireRefLocked(); 968 AcquireStoreRef(); 969 } 970 971 972 /*! Adds the \a area to this cache. 973 Assumes you have the locked the cache. 974 */ 975 status_t 976 VMCache::InsertAreaLocked(VMArea* area) 977 { 978 TRACE(("VMCache::InsertAreaLocked(cache %p, area %p)\n", this, area)); 979 AssertLocked(); 980 981 T(InsertArea(this, area)); 982 983 area->cache_next = areas; 984 if (area->cache_next) 985 area->cache_next->cache_prev = area; 986 area->cache_prev = NULL; 987 areas = area; 988 989 AcquireStoreRef(); 990 991 return B_OK; 992 } 993 994 995 status_t 996 VMCache::RemoveArea(VMArea* area) 997 { 998 TRACE(("VMCache::RemoveArea(cache %p, area %p)\n", this, area)); 999 1000 T(RemoveArea(this, area)); 1001 1002 // We release the store reference first, since otherwise we would reverse 1003 // the locking order or even deadlock ourselves (... -> free_vnode() -> ... 1004 // -> bfs_remove_vnode() -> ... -> file_cache_set_size() -> mutex_lock()). 1005 // Also cf. _RemoveConsumer(). 1006 ReleaseStoreRef(); 1007 1008 AutoLocker<VMCache> locker(this); 1009 1010 if (area->cache_prev) 1011 area->cache_prev->cache_next = area->cache_next; 1012 if (area->cache_next) 1013 area->cache_next->cache_prev = area->cache_prev; 1014 if (areas == area) 1015 areas = area->cache_next; 1016 1017 return B_OK; 1018 } 1019 1020 1021 /*! Transfers the areas from \a fromCache to this cache. This cache must not 1022 have areas yet. Both caches must be locked. 1023 */ 1024 void 1025 VMCache::TransferAreas(VMCache* fromCache) 1026 { 1027 AssertLocked(); 1028 fromCache->AssertLocked(); 1029 ASSERT(areas == NULL); 1030 1031 areas = fromCache->areas; 1032 fromCache->areas = NULL; 1033 1034 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1035 area->cache = this; 1036 AcquireRefLocked(); 1037 fromCache->ReleaseRefLocked(); 1038 1039 T(RemoveArea(fromCache, area)); 1040 T(InsertArea(this, area)); 1041 } 1042 } 1043 1044 1045 uint32 1046 VMCache::CountWritableAreas(VMArea* ignoreArea) const 1047 { 1048 uint32 count = 0; 1049 1050 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1051 if (area != ignoreArea 1052 && (area->protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { 1053 count++; 1054 } 1055 } 1056 1057 return count; 1058 } 1059 1060 1061 status_t 1062 VMCache::WriteModified() 1063 { 1064 TRACE(("VMCache::WriteModified(cache = %p)\n", this)); 1065 1066 if (temporary) 1067 return B_OK; 1068 1069 Lock(); 1070 status_t status = vm_page_write_modified_pages(this); 1071 Unlock(); 1072 1073 return status; 1074 } 1075 1076 1077 /*! Commits the memory to the store if the \a commitment is larger than 1078 what's committed already. 1079 Assumes you have the cache's lock held. 1080 */ 1081 status_t 1082 VMCache::SetMinimalCommitment(off_t commitment, int priority) 1083 { 1084 TRACE(("VMCache::SetMinimalCommitment(cache %p, commitment %" B_PRIdOFF 1085 ")\n", this, commitment)); 1086 T(SetMinimalCommitment(this, commitment)); 1087 1088 status_t status = B_OK; 1089 1090 // If we don't have enough committed space to cover through to the new end 1091 // of the area... 1092 if (committed_size < commitment) { 1093 #if KDEBUG 1094 const off_t size = ROUNDUP(virtual_end - virtual_base, B_PAGE_SIZE); 1095 ASSERT_PRINT(commitment <= size, "cache %p, commitment %" B_PRIdOFF ", size %" B_PRIdOFF, 1096 this, commitment, size); 1097 #endif 1098 1099 // try to commit more memory 1100 status = Commit(commitment, priority); 1101 } 1102 1103 return status; 1104 } 1105 1106 1107 bool 1108 VMCache::_FreePageRange(VMCachePagesTree::Iterator it, 1109 page_num_t* toPage = NULL) 1110 { 1111 for (vm_page* page = it.Next(); 1112 page != NULL && (toPage == NULL || page->cache_offset < *toPage); 1113 page = it.Next()) { 1114 1115 if (page->busy) { 1116 if (page->busy_writing) { 1117 // We cannot wait for the page to become available 1118 // as we might cause a deadlock this way 1119 page->busy_writing = false; 1120 // this will notify the writer to free the page 1121 continue; 1122 } 1123 1124 // wait for page to become unbusy 1125 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1126 return true; 1127 } 1128 1129 // remove the page and put it into the free queue 1130 DEBUG_PAGE_ACCESS_START(page); 1131 vm_remove_all_page_mappings(page); 1132 ASSERT(page->WiredCount() == 0); 1133 // TODO: Find a real solution! If the page is wired 1134 // temporarily (e.g. by lock_memory()), we actually must not 1135 // unmap it! 1136 RemovePage(page); 1137 // Note: When iterating through a IteratableSplayTree 1138 // removing the current node is safe. 1139 1140 vm_page_free(this, page); 1141 } 1142 1143 return false; 1144 } 1145 1146 1147 /*! This function updates the size field of the cache. 1148 If needed, it will free up all pages that don't belong to the cache anymore. 1149 The cache lock must be held when you call it. 1150 Since removed pages don't belong to the cache any longer, they are not 1151 written back before they will be removed. 1152 1153 Note, this function may temporarily release the cache lock in case it 1154 has to wait for busy pages. 1155 */ 1156 status_t 1157 VMCache::Resize(off_t newSize, int priority) 1158 { 1159 TRACE(("VMCache::Resize(cache %p, newSize %" B_PRIdOFF ") old size %" 1160 B_PRIdOFF "\n", this, newSize, this->virtual_end)); 1161 T(Resize(this, newSize)); 1162 1163 AssertLocked(); 1164 1165 page_num_t oldPageCount = (page_num_t)((virtual_end + B_PAGE_SIZE - 1) 1166 >> PAGE_SHIFT); 1167 page_num_t newPageCount = (page_num_t)((newSize + B_PAGE_SIZE - 1) 1168 >> PAGE_SHIFT); 1169 1170 if (newPageCount < oldPageCount) { 1171 // we need to remove all pages in the cache outside of the new virtual 1172 // size 1173 while (_FreePageRange(pages.GetIterator(newPageCount, true, true))) 1174 ; 1175 } 1176 1177 status_t status = Commit(newSize - virtual_base, priority); 1178 if (status != B_OK) 1179 return status; 1180 1181 virtual_end = newSize; 1182 return B_OK; 1183 } 1184 1185 /*! This function updates the virtual_base field of the cache. 1186 If needed, it will free up all pages that don't belong to the cache anymore. 1187 The cache lock must be held when you call it. 1188 Since removed pages don't belong to the cache any longer, they are not 1189 written back before they will be removed. 1190 1191 Note, this function may temporarily release the cache lock in case it 1192 has to wait for busy pages. 1193 */ 1194 status_t 1195 VMCache::Rebase(off_t newBase, int priority) 1196 { 1197 TRACE(("VMCache::Rebase(cache %p, newBase %lld) old base %lld\n", 1198 this, newBase, this->virtual_base)); 1199 this->AssertLocked(); 1200 1201 T(Rebase(this, newBase)); 1202 1203 status_t status = Commit(virtual_end - newBase, priority); 1204 if (status != B_OK) 1205 return status; 1206 1207 page_num_t basePage = (page_num_t)(newBase >> PAGE_SHIFT); 1208 1209 if (newBase > virtual_base) { 1210 // we need to remove all pages in the cache outside of the new virtual 1211 // base 1212 while (_FreePageRange(pages.GetIterator(), &basePage)) 1213 ; 1214 } 1215 1216 virtual_base = newBase; 1217 return B_OK; 1218 } 1219 1220 1221 /*! Moves pages in the given range from the source cache into this cache. Both 1222 caches must be locked. 1223 */ 1224 status_t 1225 VMCache::Adopt(VMCache* source, off_t offset, off_t size, off_t newOffset) 1226 { 1227 page_num_t startPage = offset >> PAGE_SHIFT; 1228 page_num_t endPage = (offset + size + B_PAGE_SIZE - 1) >> PAGE_SHIFT; 1229 off_t offsetChange = newOffset - offset; 1230 1231 VMCachePagesTree::Iterator it = source->pages.GetIterator(startPage, true, 1232 true); 1233 for (vm_page* page = it.Next(); 1234 page != NULL && page->cache_offset < endPage; 1235 page = it.Next()) { 1236 MovePage(page, (page->cache_offset << PAGE_SHIFT) + offsetChange); 1237 } 1238 1239 return B_OK; 1240 } 1241 1242 1243 /*! Discards pages in the given range. */ 1244 status_t 1245 VMCache::Discard(off_t offset, off_t size) 1246 { 1247 page_num_t startPage = offset >> PAGE_SHIFT; 1248 page_num_t endPage = (offset + size + B_PAGE_SIZE - 1) >> PAGE_SHIFT; 1249 while (_FreePageRange(pages.GetIterator(startPage, true, true), &endPage)) 1250 ; 1251 1252 return B_OK; 1253 } 1254 1255 1256 /*! You have to call this function with the VMCache lock held. */ 1257 status_t 1258 VMCache::FlushAndRemoveAllPages() 1259 { 1260 ASSERT_LOCKED_MUTEX(&fLock); 1261 1262 while (page_count > 0) { 1263 // write back modified pages 1264 status_t status = vm_page_write_modified_pages(this); 1265 if (status != B_OK) 1266 return status; 1267 1268 // remove pages 1269 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 1270 vm_page* page = it.Next();) { 1271 if (page->busy) { 1272 // wait for page to become unbusy 1273 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1274 1275 // restart from the start of the list 1276 it = pages.GetIterator(); 1277 continue; 1278 } 1279 1280 // skip modified pages -- they will be written back in the next 1281 // iteration 1282 if (page->State() == PAGE_STATE_MODIFIED) 1283 continue; 1284 1285 // We can't remove mapped pages. 1286 if (page->IsMapped()) 1287 return B_BUSY; 1288 1289 DEBUG_PAGE_ACCESS_START(page); 1290 RemovePage(page); 1291 vm_page_free(this, page); 1292 // Note: When iterating through a IteratableSplayTree 1293 // removing the current node is safe. 1294 } 1295 } 1296 1297 return B_OK; 1298 } 1299 1300 1301 status_t 1302 VMCache::Commit(off_t size, int priority) 1303 { 1304 ASSERT_UNREACHABLE(); 1305 return B_NOT_SUPPORTED; 1306 } 1307 1308 1309 /*! Returns whether the cache's underlying backing store could deliver the 1310 page at the given offset. 1311 1312 Basically it returns whether a Read() at \a offset would at least read a 1313 partial page (assuming that no unexpected errors occur or the situation 1314 changes in the meantime). 1315 */ 1316 bool 1317 VMCache::HasPage(off_t offset) 1318 { 1319 // In accordance with Fault() the default implementation doesn't have a 1320 // backing store and doesn't allow faults. 1321 return false; 1322 } 1323 1324 1325 status_t 1326 VMCache::Read(off_t offset, const generic_io_vec *vecs, size_t count, 1327 uint32 flags, generic_size_t *_numBytes) 1328 { 1329 return B_ERROR; 1330 } 1331 1332 1333 status_t 1334 VMCache::Write(off_t offset, const generic_io_vec *vecs, size_t count, 1335 uint32 flags, generic_size_t *_numBytes) 1336 { 1337 return B_ERROR; 1338 } 1339 1340 1341 status_t 1342 VMCache::WriteAsync(off_t offset, const generic_io_vec* vecs, size_t count, 1343 generic_size_t numBytes, uint32 flags, AsyncIOCallback* callback) 1344 { 1345 // Not supported, fall back to the synchronous hook. 1346 generic_size_t transferred = numBytes; 1347 status_t error = Write(offset, vecs, count, flags, &transferred); 1348 1349 if (callback != NULL) 1350 callback->IOFinished(error, transferred != numBytes, transferred); 1351 1352 return error; 1353 } 1354 1355 1356 /*! \brief Returns whether the cache can write the page at the given offset. 1357 1358 The cache must be locked when this function is invoked. 1359 1360 @param offset The page offset. 1361 @return \c true, if the page can be written, \c false otherwise. 1362 */ 1363 bool 1364 VMCache::CanWritePage(off_t offset) 1365 { 1366 return false; 1367 } 1368 1369 1370 status_t 1371 VMCache::Fault(struct VMAddressSpace *aspace, off_t offset) 1372 { 1373 return B_BAD_ADDRESS; 1374 } 1375 1376 1377 void 1378 VMCache::Merge(VMCache* source) 1379 { 1380 for (VMCachePagesTree::Iterator it = source->pages.GetIterator(); 1381 vm_page* page = it.Next();) { 1382 // Note: Removing the current node while iterating through a 1383 // IteratableSplayTree is safe. 1384 vm_page* consumerPage = LookupPage( 1385 (off_t)page->cache_offset << PAGE_SHIFT); 1386 if (consumerPage == NULL) { 1387 // the page is not yet in the consumer cache - move it upwards 1388 MovePage(page); 1389 } 1390 } 1391 } 1392 1393 1394 status_t 1395 VMCache::AcquireUnreferencedStoreRef() 1396 { 1397 return B_OK; 1398 } 1399 1400 1401 void 1402 VMCache::AcquireStoreRef() 1403 { 1404 } 1405 1406 1407 void 1408 VMCache::ReleaseStoreRef() 1409 { 1410 } 1411 1412 1413 /*! Kernel debugger version of HasPage(). 1414 Does not do any locking. 1415 */ 1416 bool 1417 VMCache::DebugHasPage(off_t offset) 1418 { 1419 // default that works for all subclasses that don't lock anyway 1420 return HasPage(offset); 1421 } 1422 1423 1424 /*! Kernel debugger version of LookupPage(). 1425 Does not do any locking. 1426 */ 1427 vm_page* 1428 VMCache::DebugLookupPage(off_t offset) 1429 { 1430 return pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 1431 } 1432 1433 1434 void 1435 VMCache::Dump(bool showPages) const 1436 { 1437 kprintf("CACHE %p:\n", this); 1438 kprintf(" ref_count: %" B_PRId32 "\n", RefCount()); 1439 kprintf(" source: %p\n", source); 1440 kprintf(" type: %s\n", vm_cache_type_to_string(type)); 1441 kprintf(" virtual_base: 0x%" B_PRIx64 "\n", virtual_base); 1442 kprintf(" virtual_end: 0x%" B_PRIx64 "\n", virtual_end); 1443 kprintf(" temporary: %" B_PRIu32 "\n", uint32(temporary)); 1444 kprintf(" lock: %p\n", &fLock); 1445 #if KDEBUG 1446 kprintf(" lock.holder: %" B_PRId32 "\n", fLock.holder); 1447 #endif 1448 kprintf(" areas:\n"); 1449 1450 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1451 kprintf(" area 0x%" B_PRIx32 ", %s\n", area->id, area->name); 1452 kprintf("\tbase_addr: 0x%lx, size: 0x%lx\n", area->Base(), 1453 area->Size()); 1454 kprintf("\tprotection: 0x%" B_PRIx32 "\n", area->protection); 1455 kprintf("\towner: 0x%" B_PRIx32 "\n", area->address_space->ID()); 1456 } 1457 1458 kprintf(" consumers:\n"); 1459 for (ConsumerList::ConstIterator it = consumers.GetIterator(); 1460 VMCache* consumer = it.Next();) { 1461 kprintf("\t%p\n", consumer); 1462 } 1463 1464 kprintf(" pages:\n"); 1465 if (showPages) { 1466 for (VMCachePagesTree::ConstIterator it = pages.GetIterator(); 1467 vm_page* page = it.Next();) { 1468 if (!vm_page_is_dummy(page)) { 1469 kprintf("\t%p ppn %#" B_PRIxPHYSADDR " offset %#" B_PRIxPHYSADDR 1470 " state %u (%s) wired_count %u\n", page, 1471 page->physical_page_number, page->cache_offset, 1472 page->State(), page_state_to_string(page->State()), 1473 page->WiredCount()); 1474 } else { 1475 kprintf("\t%p DUMMY PAGE state %u (%s)\n", 1476 page, page->State(), page_state_to_string(page->State())); 1477 } 1478 } 1479 } else 1480 kprintf("\t%" B_PRIu32 " in cache\n", page_count); 1481 } 1482 1483 1484 /*! Wakes up threads waiting for page events. 1485 \param page The page for which events occurred. 1486 \param events The mask of events that occurred. 1487 */ 1488 void 1489 VMCache::_NotifyPageEvents(vm_page* page, uint32 events) 1490 { 1491 PageEventWaiter** it = &fPageEventWaiters; 1492 while (PageEventWaiter* waiter = *it) { 1493 if (waiter->page == page && (waiter->events & events) != 0) { 1494 // remove from list and unblock 1495 *it = waiter->next; 1496 thread_unblock(waiter->thread, B_OK); 1497 } else 1498 it = &waiter->next; 1499 } 1500 } 1501 1502 1503 /*! Merges the given cache with its only consumer. 1504 The caller must hold both the cache's and the consumer's lock. The method 1505 does release neither lock. 1506 */ 1507 void 1508 VMCache::_MergeWithOnlyConsumer() 1509 { 1510 VMCache* consumer = consumers.RemoveHead(); 1511 1512 TRACE(("merge vm cache %p (ref == %" B_PRId32 ") with vm cache %p\n", 1513 this, this->fRefCount, consumer)); 1514 1515 T(Merge(this, consumer)); 1516 1517 // merge the cache 1518 consumer->Merge(this); 1519 1520 // The remaining consumer has got a new source. 1521 if (source != NULL) { 1522 VMCache* newSource = source; 1523 1524 newSource->Lock(); 1525 1526 newSource->consumers.Remove(this); 1527 newSource->consumers.Add(consumer); 1528 consumer->source = newSource; 1529 source = NULL; 1530 1531 newSource->Unlock(); 1532 } else 1533 consumer->source = NULL; 1534 1535 // Release the reference the cache's consumer owned. The consumer takes 1536 // over the cache's ref to its source (if any) instead. 1537 ReleaseRefLocked(); 1538 } 1539 1540 1541 /*! Removes the \a consumer from this cache. 1542 It will also release the reference to the cache owned by the consumer. 1543 Assumes you have the consumer's cache lock held. This cache must not be 1544 locked. 1545 */ 1546 void 1547 VMCache::_RemoveConsumer(VMCache* consumer) 1548 { 1549 TRACE(("remove consumer vm cache %p from cache %p\n", consumer, this)); 1550 consumer->AssertLocked(); 1551 1552 T(RemoveConsumer(this, consumer)); 1553 1554 // Remove the store ref before locking the cache. Otherwise we'd call into 1555 // the VFS while holding the cache lock, which would reverse the usual 1556 // locking order. 1557 ReleaseStoreRef(); 1558 1559 // remove the consumer from the cache, but keep its reference until later 1560 Lock(); 1561 consumers.Remove(consumer); 1562 consumer->source = NULL; 1563 1564 ReleaseRefAndUnlock(); 1565 } 1566 1567 1568 // #pragma mark - VMCacheFactory 1569 // TODO: Move to own source file! 1570 1571 1572 /*static*/ status_t 1573 VMCacheFactory::CreateAnonymousCache(VMCache*& _cache, bool canOvercommit, 1574 int32 numPrecommittedPages, int32 numGuardPages, bool swappable, 1575 int priority) 1576 { 1577 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1578 | HEAP_DONT_LOCK_KERNEL_SPACE; 1579 if (priority >= VM_PRIORITY_VIP) 1580 allocationFlags |= HEAP_PRIORITY_VIP; 1581 1582 #if ENABLE_SWAP_SUPPORT 1583 if (swappable) { 1584 VMAnonymousCache* cache 1585 = new(gAnonymousCacheObjectCache, allocationFlags) VMAnonymousCache; 1586 if (cache == NULL) 1587 return B_NO_MEMORY; 1588 1589 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1590 numGuardPages, allocationFlags); 1591 if (error != B_OK) { 1592 cache->Delete(); 1593 return error; 1594 } 1595 1596 T(Create(cache)); 1597 1598 _cache = cache; 1599 return B_OK; 1600 } 1601 #endif 1602 1603 VMAnonymousNoSwapCache* cache 1604 = new(gAnonymousNoSwapCacheObjectCache, allocationFlags) 1605 VMAnonymousNoSwapCache; 1606 if (cache == NULL) 1607 return B_NO_MEMORY; 1608 1609 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1610 numGuardPages, allocationFlags); 1611 if (error != B_OK) { 1612 cache->Delete(); 1613 return error; 1614 } 1615 1616 T(Create(cache)); 1617 1618 _cache = cache; 1619 return B_OK; 1620 } 1621 1622 1623 /*static*/ status_t 1624 VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode) 1625 { 1626 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1627 | HEAP_DONT_LOCK_KERNEL_SPACE; 1628 // Note: Vnode cache creation is never VIP. 1629 1630 VMVnodeCache* cache 1631 = new(gVnodeCacheObjectCache, allocationFlags) VMVnodeCache; 1632 if (cache == NULL) 1633 return B_NO_MEMORY; 1634 1635 status_t error = cache->Init(vnode, allocationFlags); 1636 if (error != B_OK) { 1637 cache->Delete(); 1638 return error; 1639 } 1640 1641 T(Create(cache)); 1642 1643 _cache = cache; 1644 return B_OK; 1645 } 1646 1647 1648 /*static*/ status_t 1649 VMCacheFactory::CreateDeviceCache(VMCache*& _cache, addr_t baseAddress) 1650 { 1651 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1652 | HEAP_DONT_LOCK_KERNEL_SPACE; 1653 // Note: Device cache creation is never VIP. 1654 1655 VMDeviceCache* cache 1656 = new(gDeviceCacheObjectCache, allocationFlags) VMDeviceCache; 1657 if (cache == NULL) 1658 return B_NO_MEMORY; 1659 1660 status_t error = cache->Init(baseAddress, allocationFlags); 1661 if (error != B_OK) { 1662 cache->Delete(); 1663 return error; 1664 } 1665 1666 T(Create(cache)); 1667 1668 _cache = cache; 1669 return B_OK; 1670 } 1671 1672 1673 /*static*/ status_t 1674 VMCacheFactory::CreateNullCache(int priority, VMCache*& _cache) 1675 { 1676 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1677 | HEAP_DONT_LOCK_KERNEL_SPACE; 1678 if (priority >= VM_PRIORITY_VIP) 1679 allocationFlags |= HEAP_PRIORITY_VIP; 1680 1681 VMNullCache* cache 1682 = new(gNullCacheObjectCache, allocationFlags) VMNullCache; 1683 if (cache == NULL) 1684 return B_NO_MEMORY; 1685 1686 status_t error = cache->Init(allocationFlags); 1687 if (error != B_OK) { 1688 cache->Delete(); 1689 return error; 1690 } 1691 1692 T(Create(cache)); 1693 1694 _cache = cache; 1695 return B_OK; 1696 } 1697