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 mutex sCacheListLock = MUTEX_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 mutex_lock(&sCacheListLock); 562 563 while (dontWait) { 564 VMCacheRef* cacheRef = page->CacheRef(); 565 if (cacheRef == NULL) { 566 mutex_unlock(&sCacheListLock); 567 return NULL; 568 } 569 570 VMCache* cache = cacheRef->cache; 571 if (!cache->TryLock()) { 572 mutex_unlock(&sCacheListLock); 573 return NULL; 574 } 575 576 if (cacheRef == page->CacheRef()) { 577 mutex_unlock(&sCacheListLock); 578 cache->AcquireRefLocked(); 579 return cache; 580 } 581 582 // the cache changed in the meantime 583 cache->Unlock(); 584 } 585 586 while (true) { 587 VMCacheRef* cacheRef = page->CacheRef(); 588 if (cacheRef == NULL) { 589 mutex_unlock(&sCacheListLock); 590 return NULL; 591 } 592 593 VMCache* cache = cacheRef->cache; 594 if (!cache->SwitchLock(&sCacheListLock)) { 595 // cache has been deleted 596 mutex_lock(&sCacheListLock); 597 continue; 598 } 599 600 mutex_lock(&sCacheListLock); 601 if (cache == page->Cache()) { 602 mutex_unlock(&sCacheListLock); 603 cache->AcquireRefLocked(); 604 return cache; 605 } 606 607 // the cache changed in the meantime 608 cache->Unlock(); 609 } 610 } 611 612 613 // #pragma mark - VMCache 614 615 616 VMCacheRef::VMCacheRef(VMCache* cache) 617 : 618 cache(cache), 619 ref_count(1) 620 { 621 } 622 623 624 // #pragma mark - VMCache 625 626 627 bool 628 VMCache::_IsMergeable() const 629 { 630 return areas == NULL && temporary && !consumers.IsEmpty() 631 && consumers.Head() == consumers.Tail(); 632 } 633 634 635 VMCache::VMCache() 636 : 637 fCacheRef(NULL) 638 { 639 } 640 641 642 VMCache::~VMCache() 643 { 644 object_cache_delete(gCacheRefObjectCache, fCacheRef); 645 } 646 647 648 status_t 649 VMCache::Init(uint32 cacheType, uint32 allocationFlags) 650 { 651 mutex_init(&fLock, "VMCache"); 652 653 areas = NULL; 654 fRefCount = 1; 655 source = NULL; 656 virtual_base = 0; 657 virtual_end = 0; 658 committed_size = 0; 659 temporary = 0; 660 page_count = 0; 661 fWiredPagesCount = 0; 662 type = cacheType; 663 fPageEventWaiters = NULL; 664 665 #if DEBUG_CACHE_LIST 666 debug_previous = NULL; 667 debug_next = NULL; 668 // initialize in case the following fails 669 #endif 670 671 fCacheRef = new(gCacheRefObjectCache, allocationFlags) VMCacheRef(this); 672 if (fCacheRef == NULL) 673 return B_NO_MEMORY; 674 675 #if DEBUG_CACHE_LIST 676 mutex_lock(&sCacheListLock); 677 678 if (gDebugCacheList != NULL) 679 gDebugCacheList->debug_previous = this; 680 debug_next = gDebugCacheList; 681 gDebugCacheList = this; 682 683 mutex_unlock(&sCacheListLock); 684 #endif 685 686 return B_OK; 687 } 688 689 690 void 691 VMCache::Delete() 692 { 693 if (areas != NULL) 694 panic("cache %p to be deleted still has areas", this); 695 if (!consumers.IsEmpty()) 696 panic("cache %p to be deleted still has consumers", this); 697 698 T(Delete(this)); 699 700 // free all of the pages in the cache 701 while (vm_page* page = pages.Root()) { 702 if (!page->mappings.IsEmpty() || page->WiredCount() != 0) { 703 panic("remove page %p from cache %p: page still has mappings!\n" 704 "@!page %p; cache %p", page, this, page, this); 705 } 706 707 // remove it 708 pages.Remove(page); 709 page->SetCacheRef(NULL); 710 711 TRACE(("vm_cache_release_ref: freeing page 0x%lx\n", 712 page->physical_page_number)); 713 DEBUG_PAGE_ACCESS_START(page); 714 vm_page_free(this, page); 715 } 716 717 // remove the ref to the source 718 if (source) 719 source->_RemoveConsumer(this); 720 721 // We lock and unlock the sCacheListLock, even if the DEBUG_CACHE_LIST is 722 // not enabled. This synchronization point is needed for 723 // vm_cache_acquire_locked_page_cache(). 724 mutex_lock(&sCacheListLock); 725 726 #if DEBUG_CACHE_LIST 727 if (debug_previous) 728 debug_previous->debug_next = debug_next; 729 if (debug_next) 730 debug_next->debug_previous = debug_previous; 731 if (this == gDebugCacheList) 732 gDebugCacheList = debug_next; 733 #endif 734 735 mutex_destroy(&fLock); 736 737 mutex_unlock(&sCacheListLock); 738 739 DeleteObject(); 740 } 741 742 743 void 744 VMCache::Unlock(bool consumerLocked) 745 { 746 while (fRefCount == 1 && _IsMergeable()) { 747 VMCache* consumer = consumers.Head(); 748 if (consumerLocked) { 749 _MergeWithOnlyConsumer(); 750 } else if (consumer->TryLock()) { 751 _MergeWithOnlyConsumer(); 752 consumer->Unlock(); 753 } else { 754 // Someone else has locked the consumer ATM. Unlock this cache and 755 // wait for the consumer lock. Increment the cache's ref count 756 // temporarily, so that no one else will try what we are doing or 757 // delete the cache. 758 fRefCount++; 759 bool consumerLockedTemp = consumer->SwitchLock(&fLock); 760 Lock(); 761 fRefCount--; 762 763 if (consumerLockedTemp) { 764 if (fRefCount == 1 && _IsMergeable() 765 && consumer == consumers.Head()) { 766 // nothing has changed in the meantime -- merge 767 _MergeWithOnlyConsumer(); 768 } 769 770 consumer->Unlock(); 771 } 772 } 773 } 774 775 if (fRefCount == 0) { 776 // delete this cache 777 Delete(); 778 } else 779 mutex_unlock(&fLock); 780 } 781 782 783 vm_page* 784 VMCache::LookupPage(off_t offset) 785 { 786 AssertLocked(); 787 788 vm_page* page = pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 789 790 #if KDEBUG 791 if (page != NULL && page->Cache() != this) 792 panic("page %p not in cache %p\n", page, this); 793 #endif 794 795 return page; 796 } 797 798 799 void 800 VMCache::InsertPage(vm_page* page, off_t offset) 801 { 802 TRACE(("VMCache::InsertPage(): cache %p, page %p, offset %" B_PRIdOFF "\n", 803 this, page, offset)); 804 AssertLocked(); 805 806 if (page->CacheRef() != NULL) { 807 panic("insert page %p into cache %p: page cache is set to %p\n", 808 page, this, page->Cache()); 809 } 810 811 T2(InsertPage(this, page, offset)); 812 813 page->cache_offset = (page_num_t)(offset >> PAGE_SHIFT); 814 page_count++; 815 page->SetCacheRef(fCacheRef); 816 817 #if KDEBUG 818 vm_page* otherPage = pages.Lookup(page->cache_offset); 819 if (otherPage != NULL) { 820 panic("VMCache::InsertPage(): there's already page %p with cache " 821 "offset %" B_PRIuPHYSADDR " in cache %p; inserting page %p", 822 otherPage, page->cache_offset, this, page); 823 } 824 #endif // KDEBUG 825 826 pages.Insert(page); 827 828 if (page->WiredCount() > 0) 829 IncrementWiredPagesCount(); 830 } 831 832 833 /*! Removes the vm_page from this cache. Of course, the page must 834 really be in this cache or evil things will happen. 835 The cache lock must be held. 836 */ 837 void 838 VMCache::RemovePage(vm_page* page) 839 { 840 TRACE(("VMCache::RemovePage(): cache %p, page %p\n", this, page)); 841 AssertLocked(); 842 843 if (page->Cache() != this) { 844 panic("remove page %p from cache %p: page cache is set to %p\n", page, 845 this, page->Cache()); 846 } 847 848 T2(RemovePage(this, page)); 849 850 pages.Remove(page); 851 page_count--; 852 page->SetCacheRef(NULL); 853 854 if (page->WiredCount() > 0) 855 DecrementWiredPagesCount(); 856 } 857 858 859 /*! Moves the given page from its current cache inserts it into this cache 860 at the given offset. 861 Both caches must be locked. 862 */ 863 void 864 VMCache::MovePage(vm_page* page, off_t offset) 865 { 866 VMCache* oldCache = page->Cache(); 867 868 AssertLocked(); 869 oldCache->AssertLocked(); 870 871 // remove from old cache 872 oldCache->pages.Remove(page); 873 oldCache->page_count--; 874 T2(RemovePage(oldCache, page)); 875 876 // change the offset 877 page->cache_offset = offset >> PAGE_SHIFT; 878 879 // insert here 880 pages.Insert(page); 881 page_count++; 882 page->SetCacheRef(fCacheRef); 883 884 if (page->WiredCount() > 0) { 885 IncrementWiredPagesCount(); 886 oldCache->DecrementWiredPagesCount(); 887 } 888 889 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 890 } 891 892 /*! Moves the given page from its current cache inserts it into this cache. 893 Both caches must be locked. 894 */ 895 void 896 VMCache::MovePage(vm_page* page) 897 { 898 MovePage(page, page->cache_offset << PAGE_SHIFT); 899 } 900 901 902 /*! Moves all pages from the given cache to this one. 903 Both caches must be locked. This cache must be empty. 904 */ 905 void 906 VMCache::MoveAllPages(VMCache* fromCache) 907 { 908 AssertLocked(); 909 fromCache->AssertLocked(); 910 ASSERT(page_count == 0); 911 912 std::swap(fromCache->pages, pages); 913 page_count = fromCache->page_count; 914 fromCache->page_count = 0; 915 fWiredPagesCount = fromCache->fWiredPagesCount; 916 fromCache->fWiredPagesCount = 0; 917 918 // swap the VMCacheRefs 919 mutex_lock(&sCacheListLock); 920 std::swap(fCacheRef, fromCache->fCacheRef); 921 fCacheRef->cache = this; 922 fromCache->fCacheRef->cache = fromCache; 923 mutex_unlock(&sCacheListLock); 924 925 #if VM_CACHE_TRACING >= 2 926 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 927 vm_page* page = it.Next();) { 928 T2(RemovePage(fromCache, page)); 929 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 930 } 931 #endif 932 } 933 934 935 /*! Waits until one or more events happened for a given page which belongs to 936 this cache. 937 The cache must be locked. It will be unlocked by the method. \a relock 938 specifies whether the method shall re-lock the cache before returning. 939 \param page The page for which to wait. 940 \param events The mask of events the caller is interested in. 941 \param relock If \c true, the cache will be locked when returning, 942 otherwise it won't be locked. 943 */ 944 void 945 VMCache::WaitForPageEvents(vm_page* page, uint32 events, bool relock) 946 { 947 PageEventWaiter waiter; 948 waiter.thread = thread_get_current_thread(); 949 waiter.next = fPageEventWaiters; 950 waiter.page = page; 951 waiter.events = events; 952 953 fPageEventWaiters = &waiter; 954 955 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_OTHER, page); 956 957 Unlock(); 958 thread_block(); 959 960 if (relock) 961 Lock(); 962 } 963 964 965 /*! Makes this case the source of the \a consumer cache, 966 and adds the \a consumer to its list. 967 This also grabs a reference to the source cache. 968 Assumes you have the cache and the consumer's lock held. 969 */ 970 void 971 VMCache::AddConsumer(VMCache* consumer) 972 { 973 TRACE(("add consumer vm cache %p to cache %p\n", consumer, this)); 974 AssertLocked(); 975 consumer->AssertLocked(); 976 977 T(AddConsumer(this, consumer)); 978 979 consumer->source = this; 980 consumers.Add(consumer); 981 982 AcquireRefLocked(); 983 AcquireStoreRef(); 984 } 985 986 987 /*! Adds the \a area to this cache. 988 Assumes you have the locked the cache. 989 */ 990 status_t 991 VMCache::InsertAreaLocked(VMArea* area) 992 { 993 TRACE(("VMCache::InsertAreaLocked(cache %p, area %p)\n", this, area)); 994 AssertLocked(); 995 996 T(InsertArea(this, area)); 997 998 area->cache_next = areas; 999 if (area->cache_next) 1000 area->cache_next->cache_prev = area; 1001 area->cache_prev = NULL; 1002 areas = area; 1003 1004 AcquireStoreRef(); 1005 1006 return B_OK; 1007 } 1008 1009 1010 status_t 1011 VMCache::RemoveArea(VMArea* area) 1012 { 1013 TRACE(("VMCache::RemoveArea(cache %p, area %p)\n", this, area)); 1014 1015 T(RemoveArea(this, area)); 1016 1017 // We release the store reference first, since otherwise we would reverse 1018 // the locking order or even deadlock ourselves (... -> free_vnode() -> ... 1019 // -> bfs_remove_vnode() -> ... -> file_cache_set_size() -> mutex_lock()). 1020 // Also cf. _RemoveConsumer(). 1021 ReleaseStoreRef(); 1022 1023 AutoLocker<VMCache> locker(this); 1024 1025 if (area->cache_prev) 1026 area->cache_prev->cache_next = area->cache_next; 1027 if (area->cache_next) 1028 area->cache_next->cache_prev = area->cache_prev; 1029 if (areas == area) 1030 areas = area->cache_next; 1031 1032 return B_OK; 1033 } 1034 1035 1036 /*! Transfers the areas from \a fromCache to this cache. This cache must not 1037 have areas yet. Both caches must be locked. 1038 */ 1039 void 1040 VMCache::TransferAreas(VMCache* fromCache) 1041 { 1042 AssertLocked(); 1043 fromCache->AssertLocked(); 1044 ASSERT(areas == NULL); 1045 1046 areas = fromCache->areas; 1047 fromCache->areas = NULL; 1048 1049 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1050 area->cache = this; 1051 AcquireRefLocked(); 1052 fromCache->ReleaseRefLocked(); 1053 1054 T(RemoveArea(fromCache, area)); 1055 T(InsertArea(this, area)); 1056 } 1057 } 1058 1059 1060 uint32 1061 VMCache::CountWritableAreas(VMArea* ignoreArea) const 1062 { 1063 uint32 count = 0; 1064 1065 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1066 if (area != ignoreArea 1067 && (area->protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { 1068 count++; 1069 } 1070 } 1071 1072 return count; 1073 } 1074 1075 1076 status_t 1077 VMCache::WriteModified() 1078 { 1079 TRACE(("VMCache::WriteModified(cache = %p)\n", this)); 1080 1081 if (temporary) 1082 return B_OK; 1083 1084 Lock(); 1085 status_t status = vm_page_write_modified_pages(this); 1086 Unlock(); 1087 1088 return status; 1089 } 1090 1091 1092 /*! Commits the memory to the store if the \a commitment is larger than 1093 what's committed already. 1094 Assumes you have the cache's lock held. 1095 */ 1096 status_t 1097 VMCache::SetMinimalCommitment(off_t commitment, int priority) 1098 { 1099 TRACE(("VMCache::SetMinimalCommitment(cache %p, commitment %" B_PRIdOFF 1100 ")\n", this, commitment)); 1101 AssertLocked(); 1102 1103 T(SetMinimalCommitment(this, commitment)); 1104 1105 status_t status = B_OK; 1106 1107 // If we don't have enough committed space to cover through to the new end 1108 // of the area... 1109 if (committed_size < commitment) { 1110 // ToDo: should we check if the cache's virtual size is large 1111 // enough for a commitment of that size? 1112 1113 // try to commit more memory 1114 status = Commit(commitment, priority); 1115 } 1116 1117 return status; 1118 } 1119 1120 1121 bool 1122 VMCache::_FreePageRange(VMCachePagesTree::Iterator it, 1123 page_num_t* toPage = NULL) 1124 { 1125 for (vm_page* page = it.Next(); 1126 page != NULL && (toPage == NULL || page->cache_offset < *toPage); 1127 page = it.Next()) { 1128 1129 if (page->busy) { 1130 if (page->busy_writing) { 1131 // We cannot wait for the page to become available 1132 // as we might cause a deadlock this way 1133 page->busy_writing = false; 1134 // this will notify the writer to free the page 1135 continue; 1136 } 1137 1138 // wait for page to become unbusy 1139 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1140 return true; 1141 } 1142 1143 // remove the page and put it into the free queue 1144 DEBUG_PAGE_ACCESS_START(page); 1145 vm_remove_all_page_mappings(page); 1146 ASSERT(page->WiredCount() == 0); 1147 // TODO: Find a real solution! If the page is wired 1148 // temporarily (e.g. by lock_memory()), we actually must not 1149 // unmap it! 1150 RemovePage(page); 1151 // Note: When iterating through a IteratableSplayTree 1152 // removing the current node is safe. 1153 1154 vm_page_free(this, page); 1155 } 1156 1157 return false; 1158 } 1159 1160 1161 /*! This function updates the size field of the cache. 1162 If needed, it will free up all pages that don't belong to the cache anymore. 1163 The cache lock must be held when you call it. 1164 Since removed pages don't belong to the cache any longer, they are not 1165 written back before they will be removed. 1166 1167 Note, this function may temporarily release the cache lock in case it 1168 has to wait for busy pages. 1169 */ 1170 status_t 1171 VMCache::Resize(off_t newSize, int priority) 1172 { 1173 TRACE(("VMCache::Resize(cache %p, newSize %" B_PRIdOFF ") old size %" 1174 B_PRIdOFF "\n", this, newSize, this->virtual_end)); 1175 this->AssertLocked(); 1176 1177 T(Resize(this, newSize)); 1178 1179 status_t status = Commit(newSize - virtual_base, priority); 1180 if (status != B_OK) 1181 return status; 1182 1183 page_num_t oldPageCount = (page_num_t)((virtual_end + B_PAGE_SIZE - 1) 1184 >> PAGE_SHIFT); 1185 page_num_t newPageCount = (page_num_t)((newSize + B_PAGE_SIZE - 1) 1186 >> PAGE_SHIFT); 1187 1188 if (newPageCount < oldPageCount) { 1189 // we need to remove all pages in the cache outside of the new virtual 1190 // size 1191 while (_FreePageRange(pages.GetIterator(newPageCount, true, true))) 1192 ; 1193 } 1194 1195 virtual_end = newSize; 1196 return B_OK; 1197 } 1198 1199 /*! This function updates the virtual_base field of the cache. 1200 If needed, it will free up all pages that don't belong to the cache anymore. 1201 The cache lock must be held when you call it. 1202 Since removed pages don't belong to the cache any longer, they are not 1203 written back before they will be removed. 1204 1205 Note, this function may temporarily release the cache lock in case it 1206 has to wait for busy pages. 1207 */ 1208 status_t 1209 VMCache::Rebase(off_t newBase, int priority) 1210 { 1211 TRACE(("VMCache::Rebase(cache %p, newBase %Ld) old base %Ld\n", 1212 this, newBase, this->virtual_base)); 1213 this->AssertLocked(); 1214 1215 T(Rebase(this, newBase)); 1216 1217 status_t status = Commit(virtual_end - newBase, priority); 1218 if (status != B_OK) 1219 return status; 1220 1221 page_num_t basePage = (page_num_t)(newBase >> PAGE_SHIFT); 1222 1223 if (newBase > virtual_base) { 1224 // we need to remove all pages in the cache outside of the new virtual 1225 // base 1226 while (_FreePageRange(pages.GetIterator(), &basePage)) 1227 ; 1228 } 1229 1230 virtual_base = newBase; 1231 return B_OK; 1232 } 1233 1234 1235 /*! Moves pages in the given range from the source cache into this cache. Both 1236 caches must be locked. 1237 */ 1238 status_t 1239 VMCache::Adopt(VMCache* source, off_t offset, off_t size, off_t newOffset) 1240 { 1241 page_num_t startPage = offset >> PAGE_SHIFT; 1242 page_num_t endPage = (offset + size + B_PAGE_SIZE - 1) >> PAGE_SHIFT; 1243 off_t offsetChange = newOffset - offset; 1244 1245 VMCachePagesTree::Iterator it = source->pages.GetIterator(startPage, true, 1246 true); 1247 for (vm_page* page = it.Next(); 1248 page != NULL && page->cache_offset < endPage; 1249 page = it.Next()) { 1250 MovePage(page, (page->cache_offset << PAGE_SHIFT) + offsetChange); 1251 } 1252 1253 return B_OK; 1254 } 1255 1256 1257 /*! Discards pages in the given range. */ 1258 status_t 1259 VMCache::Discard(off_t offset, off_t size) 1260 { 1261 page_num_t startPage = offset >> PAGE_SHIFT; 1262 page_num_t endPage = (offset + size + B_PAGE_SIZE - 1) >> PAGE_SHIFT; 1263 while (_FreePageRange(pages.GetIterator(startPage, true, true), &endPage)) 1264 ; 1265 1266 return B_OK; 1267 } 1268 1269 1270 /*! You have to call this function with the VMCache lock held. */ 1271 status_t 1272 VMCache::FlushAndRemoveAllPages() 1273 { 1274 ASSERT_LOCKED_MUTEX(&fLock); 1275 1276 while (page_count > 0) { 1277 // write back modified pages 1278 status_t status = vm_page_write_modified_pages(this); 1279 if (status != B_OK) 1280 return status; 1281 1282 // remove pages 1283 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 1284 vm_page* page = it.Next();) { 1285 if (page->busy) { 1286 // wait for page to become unbusy 1287 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1288 1289 // restart from the start of the list 1290 it = pages.GetIterator(); 1291 continue; 1292 } 1293 1294 // skip modified pages -- they will be written back in the next 1295 // iteration 1296 if (page->State() == PAGE_STATE_MODIFIED) 1297 continue; 1298 1299 // We can't remove mapped pages. 1300 if (page->IsMapped()) 1301 return B_BUSY; 1302 1303 DEBUG_PAGE_ACCESS_START(page); 1304 RemovePage(page); 1305 vm_page_free(this, page); 1306 // Note: When iterating through a IteratableSplayTree 1307 // removing the current node is safe. 1308 } 1309 } 1310 1311 return B_OK; 1312 } 1313 1314 1315 status_t 1316 VMCache::Commit(off_t size, int priority) 1317 { 1318 committed_size = size; 1319 return B_OK; 1320 } 1321 1322 1323 /*! Returns whether the cache's underlying backing store could deliver the 1324 page at the given offset. 1325 1326 Basically it returns whether a Read() at \a offset would at least read a 1327 partial page (assuming that no unexpected errors occur or the situation 1328 changes in the meantime). 1329 */ 1330 bool 1331 VMCache::HasPage(off_t offset) 1332 { 1333 // In accordance with Fault() the default implementation doesn't have a 1334 // backing store and doesn't allow faults. 1335 return false; 1336 } 1337 1338 1339 status_t 1340 VMCache::Read(off_t offset, const generic_io_vec *vecs, size_t count, 1341 uint32 flags, generic_size_t *_numBytes) 1342 { 1343 return B_ERROR; 1344 } 1345 1346 1347 status_t 1348 VMCache::Write(off_t offset, const generic_io_vec *vecs, size_t count, 1349 uint32 flags, generic_size_t *_numBytes) 1350 { 1351 return B_ERROR; 1352 } 1353 1354 1355 status_t 1356 VMCache::WriteAsync(off_t offset, const generic_io_vec* vecs, size_t count, 1357 generic_size_t numBytes, uint32 flags, AsyncIOCallback* callback) 1358 { 1359 // Not supported, fall back to the synchronous hook. 1360 generic_size_t transferred = numBytes; 1361 status_t error = Write(offset, vecs, count, flags, &transferred); 1362 1363 if (callback != NULL) 1364 callback->IOFinished(error, transferred != numBytes, transferred); 1365 1366 return error; 1367 } 1368 1369 1370 /*! \brief Returns whether the cache can write the page at the given offset. 1371 1372 The cache must be locked when this function is invoked. 1373 1374 @param offset The page offset. 1375 @return \c true, if the page can be written, \c false otherwise. 1376 */ 1377 bool 1378 VMCache::CanWritePage(off_t offset) 1379 { 1380 return false; 1381 } 1382 1383 1384 status_t 1385 VMCache::Fault(struct VMAddressSpace *aspace, off_t offset) 1386 { 1387 return B_BAD_ADDRESS; 1388 } 1389 1390 1391 void 1392 VMCache::Merge(VMCache* source) 1393 { 1394 for (VMCachePagesTree::Iterator it = source->pages.GetIterator(); 1395 vm_page* page = it.Next();) { 1396 // Note: Removing the current node while iterating through a 1397 // IteratableSplayTree is safe. 1398 vm_page* consumerPage = LookupPage( 1399 (off_t)page->cache_offset << PAGE_SHIFT); 1400 if (consumerPage == NULL) { 1401 // the page is not yet in the consumer cache - move it upwards 1402 MovePage(page); 1403 } 1404 } 1405 } 1406 1407 1408 status_t 1409 VMCache::AcquireUnreferencedStoreRef() 1410 { 1411 return B_OK; 1412 } 1413 1414 1415 void 1416 VMCache::AcquireStoreRef() 1417 { 1418 } 1419 1420 1421 void 1422 VMCache::ReleaseStoreRef() 1423 { 1424 } 1425 1426 1427 /*! Kernel debugger version of HasPage(). 1428 Does not do any locking. 1429 */ 1430 bool 1431 VMCache::DebugHasPage(off_t offset) 1432 { 1433 // default that works for all subclasses that don't lock anyway 1434 return HasPage(offset); 1435 } 1436 1437 1438 /*! Kernel debugger version of LookupPage(). 1439 Does not do any locking. 1440 */ 1441 vm_page* 1442 VMCache::DebugLookupPage(off_t offset) 1443 { 1444 return pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 1445 } 1446 1447 1448 void 1449 VMCache::Dump(bool showPages) const 1450 { 1451 kprintf("CACHE %p:\n", this); 1452 kprintf(" ref_count: %" B_PRId32 "\n", RefCount()); 1453 kprintf(" source: %p\n", source); 1454 kprintf(" type: %s\n", vm_cache_type_to_string(type)); 1455 kprintf(" virtual_base: 0x%" B_PRIx64 "\n", virtual_base); 1456 kprintf(" virtual_end: 0x%" B_PRIx64 "\n", virtual_end); 1457 kprintf(" temporary: %" B_PRIu32 "\n", uint32(temporary)); 1458 kprintf(" lock: %p\n", &fLock); 1459 #if KDEBUG 1460 kprintf(" lock.holder: %" B_PRId32 "\n", fLock.holder); 1461 #endif 1462 kprintf(" areas:\n"); 1463 1464 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1465 kprintf(" area 0x%" B_PRIx32 ", %s\n", area->id, area->name); 1466 kprintf("\tbase_addr: 0x%lx, size: 0x%lx\n", area->Base(), 1467 area->Size()); 1468 kprintf("\tprotection: 0x%" B_PRIx32 "\n", area->protection); 1469 kprintf("\towner: 0x%" B_PRIx32 "\n", area->address_space->ID()); 1470 } 1471 1472 kprintf(" consumers:\n"); 1473 for (ConsumerList::ConstIterator it = consumers.GetIterator(); 1474 VMCache* consumer = it.Next();) { 1475 kprintf("\t%p\n", consumer); 1476 } 1477 1478 kprintf(" pages:\n"); 1479 if (showPages) { 1480 for (VMCachePagesTree::ConstIterator it = pages.GetIterator(); 1481 vm_page* page = it.Next();) { 1482 if (!vm_page_is_dummy(page)) { 1483 kprintf("\t%p ppn %#" B_PRIxPHYSADDR " offset %#" B_PRIxPHYSADDR 1484 " state %u (%s) wired_count %u\n", page, 1485 page->physical_page_number, page->cache_offset, 1486 page->State(), page_state_to_string(page->State()), 1487 page->WiredCount()); 1488 } else { 1489 kprintf("\t%p DUMMY PAGE state %u (%s)\n", 1490 page, page->State(), page_state_to_string(page->State())); 1491 } 1492 } 1493 } else 1494 kprintf("\t%" B_PRIu32 " in cache\n", page_count); 1495 } 1496 1497 1498 /*! Wakes up threads waiting for page events. 1499 \param page The page for which events occurred. 1500 \param events The mask of events that occurred. 1501 */ 1502 void 1503 VMCache::_NotifyPageEvents(vm_page* page, uint32 events) 1504 { 1505 PageEventWaiter** it = &fPageEventWaiters; 1506 while (PageEventWaiter* waiter = *it) { 1507 if (waiter->page == page && (waiter->events & events) != 0) { 1508 // remove from list and unblock 1509 *it = waiter->next; 1510 thread_unblock(waiter->thread, B_OK); 1511 } else 1512 it = &waiter->next; 1513 } 1514 } 1515 1516 1517 /*! Merges the given cache with its only consumer. 1518 The caller must hold both the cache's and the consumer's lock. The method 1519 does release neither lock. 1520 */ 1521 void 1522 VMCache::_MergeWithOnlyConsumer() 1523 { 1524 VMCache* consumer = consumers.RemoveHead(); 1525 1526 TRACE(("merge vm cache %p (ref == %" B_PRId32 ") with vm cache %p\n", 1527 this, this->fRefCount, consumer)); 1528 1529 T(Merge(this, consumer)); 1530 1531 // merge the cache 1532 consumer->Merge(this); 1533 1534 // The remaining consumer has got a new source. 1535 if (source != NULL) { 1536 VMCache* newSource = source; 1537 1538 newSource->Lock(); 1539 1540 newSource->consumers.Remove(this); 1541 newSource->consumers.Add(consumer); 1542 consumer->source = newSource; 1543 source = NULL; 1544 1545 newSource->Unlock(); 1546 } else 1547 consumer->source = NULL; 1548 1549 // Release the reference the cache's consumer owned. The consumer takes 1550 // over the cache's ref to its source (if any) instead. 1551 ReleaseRefLocked(); 1552 } 1553 1554 1555 /*! Removes the \a consumer from this cache. 1556 It will also release the reference to the cache owned by the consumer. 1557 Assumes you have the consumer's cache lock held. This cache must not be 1558 locked. 1559 */ 1560 void 1561 VMCache::_RemoveConsumer(VMCache* consumer) 1562 { 1563 TRACE(("remove consumer vm cache %p from cache %p\n", consumer, this)); 1564 consumer->AssertLocked(); 1565 1566 T(RemoveConsumer(this, consumer)); 1567 1568 // Remove the store ref before locking the cache. Otherwise we'd call into 1569 // the VFS while holding the cache lock, which would reverse the usual 1570 // locking order. 1571 ReleaseStoreRef(); 1572 1573 // remove the consumer from the cache, but keep its reference until later 1574 Lock(); 1575 consumers.Remove(consumer); 1576 consumer->source = NULL; 1577 1578 ReleaseRefAndUnlock(); 1579 } 1580 1581 1582 // #pragma mark - VMCacheFactory 1583 // TODO: Move to own source file! 1584 1585 1586 /*static*/ status_t 1587 VMCacheFactory::CreateAnonymousCache(VMCache*& _cache, bool canOvercommit, 1588 int32 numPrecommittedPages, int32 numGuardPages, bool swappable, 1589 int priority) 1590 { 1591 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1592 | HEAP_DONT_LOCK_KERNEL_SPACE; 1593 if (priority >= VM_PRIORITY_VIP) 1594 allocationFlags |= HEAP_PRIORITY_VIP; 1595 1596 #if ENABLE_SWAP_SUPPORT 1597 if (swappable) { 1598 VMAnonymousCache* cache 1599 = new(gAnonymousCacheObjectCache, allocationFlags) VMAnonymousCache; 1600 if (cache == NULL) 1601 return B_NO_MEMORY; 1602 1603 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1604 numGuardPages, allocationFlags); 1605 if (error != B_OK) { 1606 cache->Delete(); 1607 return error; 1608 } 1609 1610 T(Create(cache)); 1611 1612 _cache = cache; 1613 return B_OK; 1614 } 1615 #endif 1616 1617 VMAnonymousNoSwapCache* cache 1618 = new(gAnonymousNoSwapCacheObjectCache, allocationFlags) 1619 VMAnonymousNoSwapCache; 1620 if (cache == NULL) 1621 return B_NO_MEMORY; 1622 1623 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1624 numGuardPages, allocationFlags); 1625 if (error != B_OK) { 1626 cache->Delete(); 1627 return error; 1628 } 1629 1630 T(Create(cache)); 1631 1632 _cache = cache; 1633 return B_OK; 1634 } 1635 1636 1637 /*static*/ status_t 1638 VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode) 1639 { 1640 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1641 | HEAP_DONT_LOCK_KERNEL_SPACE; 1642 // Note: Vnode cache creation is never VIP. 1643 1644 VMVnodeCache* cache 1645 = new(gVnodeCacheObjectCache, allocationFlags) VMVnodeCache; 1646 if (cache == NULL) 1647 return B_NO_MEMORY; 1648 1649 status_t error = cache->Init(vnode, allocationFlags); 1650 if (error != B_OK) { 1651 cache->Delete(); 1652 return error; 1653 } 1654 1655 T(Create(cache)); 1656 1657 _cache = cache; 1658 return B_OK; 1659 } 1660 1661 1662 /*static*/ status_t 1663 VMCacheFactory::CreateDeviceCache(VMCache*& _cache, addr_t baseAddress) 1664 { 1665 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1666 | HEAP_DONT_LOCK_KERNEL_SPACE; 1667 // Note: Device cache creation is never VIP. 1668 1669 VMDeviceCache* cache 1670 = new(gDeviceCacheObjectCache, allocationFlags) VMDeviceCache; 1671 if (cache == NULL) 1672 return B_NO_MEMORY; 1673 1674 status_t error = cache->Init(baseAddress, allocationFlags); 1675 if (error != B_OK) { 1676 cache->Delete(); 1677 return error; 1678 } 1679 1680 T(Create(cache)); 1681 1682 _cache = cache; 1683 return B_OK; 1684 } 1685 1686 1687 /*static*/ status_t 1688 VMCacheFactory::CreateNullCache(int priority, VMCache*& _cache) 1689 { 1690 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1691 | HEAP_DONT_LOCK_KERNEL_SPACE; 1692 if (priority >= VM_PRIORITY_VIP) 1693 allocationFlags |= HEAP_PRIORITY_VIP; 1694 1695 VMNullCache* cache 1696 = new(gNullCacheObjectCache, allocationFlags) VMNullCache; 1697 if (cache == NULL) 1698 return B_NO_MEMORY; 1699 1700 status_t error = cache->Init(allocationFlags); 1701 if (error != B_OK) { 1702 cache->Delete(); 1703 return error; 1704 } 1705 1706 T(Create(cache)); 1707 1708 _cache = cache; 1709 return B_OK; 1710 } 1711