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