1 /* 2 * Copyright 2008-2010, 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 struct 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(true); 686 } else if (consumer->TryLock()) { 687 _MergeWithOnlyConsumer(false); 688 } else { 689 // Someone else has locked the consumer ATM. Unlock this cache and 690 // wait for the consumer lock. Increment the cache's ref count 691 // temporarily, so that no one else will try what we are doing or 692 // delete the cache. 693 fRefCount++; 694 bool consumerLocked = consumer->SwitchLock(&fLock); 695 Lock(); 696 fRefCount--; 697 698 if (consumerLocked) { 699 if (fRefCount == 1 && _IsMergeable() 700 && consumer == list_get_first_item(&consumers)) { 701 _MergeWithOnlyConsumer(false); 702 } else { 703 // something changed, get rid of the consumer lock 704 consumer->Unlock(); 705 } 706 } 707 } 708 } 709 710 if (fRefCount == 0) { 711 // delete this cache 712 Delete(); 713 } else 714 mutex_unlock(&fLock); 715 } 716 717 718 vm_page* 719 VMCache::LookupPage(off_t offset) 720 { 721 AssertLocked(); 722 723 vm_page* page = pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 724 725 #if KDEBUG 726 if (page != NULL && page->Cache() != this) 727 panic("page %p not in cache %p\n", page, this); 728 #endif 729 730 return page; 731 } 732 733 734 void 735 VMCache::InsertPage(vm_page* page, off_t offset) 736 { 737 TRACE(("VMCache::InsertPage(): cache %p, page %p, offset %Ld\n", 738 this, page, offset)); 739 AssertLocked(); 740 741 if (page->CacheRef() != NULL) { 742 panic("insert page %p into cache %p: page cache is set to %p\n", 743 page, this, page->Cache()); 744 } 745 746 T2(InsertPage(this, page, offset)); 747 748 page->cache_offset = (page_num_t)(offset >> PAGE_SHIFT); 749 page_count++; 750 page->SetCacheRef(fCacheRef); 751 752 #if KDEBUG 753 vm_page* otherPage = pages.Lookup(page->cache_offset); 754 if (otherPage != NULL) { 755 panic("VMCache::InsertPage(): there's already page %p with cache " 756 "offset %" B_PRIuPHYSADDR " in cache %p; inserting page %p", 757 otherPage, page->cache_offset, this, page); 758 } 759 #endif // KDEBUG 760 761 pages.Insert(page); 762 763 if (page->WiredCount() > 0) 764 IncrementWiredPagesCount(); 765 } 766 767 768 /*! Removes the vm_page from this cache. Of course, the page must 769 really be in this cache or evil things will happen. 770 The cache lock must be held. 771 */ 772 void 773 VMCache::RemovePage(vm_page* page) 774 { 775 TRACE(("VMCache::RemovePage(): cache %p, page %p\n", this, page)); 776 AssertLocked(); 777 778 if (page->Cache() != this) { 779 panic("remove page %p from cache %p: page cache is set to %p\n", page, 780 this, page->Cache()); 781 } 782 783 T2(RemovePage(this, page)); 784 785 pages.Remove(page); 786 page_count--; 787 page->SetCacheRef(NULL); 788 789 if (page->WiredCount() > 0) 790 DecrementWiredPagesCount(); 791 } 792 793 794 /*! Moves the given page from its current cache inserts it into this cache. 795 Both caches must be locked. 796 */ 797 void 798 VMCache::MovePage(vm_page* page) 799 { 800 VMCache* oldCache = page->Cache(); 801 802 AssertLocked(); 803 oldCache->AssertLocked(); 804 805 // remove from old cache 806 oldCache->pages.Remove(page); 807 oldCache->page_count--; 808 T2(RemovePage(oldCache, page)); 809 810 // insert here 811 pages.Insert(page); 812 page_count++; 813 page->SetCacheRef(fCacheRef); 814 815 if (page->WiredCount() > 0) { 816 IncrementWiredPagesCount(); 817 oldCache->DecrementWiredPagesCount(); 818 } 819 820 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 821 } 822 823 824 /*! Moves all pages from the given cache to this one. 825 Both caches must be locked. This cache must be empty. 826 */ 827 void 828 VMCache::MoveAllPages(VMCache* fromCache) 829 { 830 AssertLocked(); 831 fromCache->AssertLocked(); 832 ASSERT(page_count == 0); 833 834 std::swap(fromCache->pages, pages); 835 page_count = fromCache->page_count; 836 fromCache->page_count = 0; 837 fWiredPagesCount = fromCache->fWiredPagesCount; 838 fromCache->fWiredPagesCount = 0; 839 840 // swap the VMCacheRefs 841 mutex_lock(&sCacheListLock); 842 std::swap(fCacheRef, fromCache->fCacheRef); 843 fCacheRef->cache = this; 844 fromCache->fCacheRef->cache = fromCache; 845 mutex_unlock(&sCacheListLock); 846 847 #if VM_CACHE_TRACING >= 2 848 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 849 vm_page* page = it.Next();) { 850 T2(RemovePage(fromCache, page)); 851 T2(InsertPage(this, page, page->cache_offset << PAGE_SHIFT)); 852 } 853 #endif 854 } 855 856 857 /*! Waits until one or more events happened for a given page which belongs to 858 this cache. 859 The cache must be locked. It will be unlocked by the method. \a relock 860 specifies whether the method shall re-lock the cache before returning. 861 \param page The page for which to wait. 862 \param events The mask of events the caller is interested in. 863 \param relock If \c true, the cache will be locked when returning, 864 otherwise it won't be locked. 865 */ 866 void 867 VMCache::WaitForPageEvents(vm_page* page, uint32 events, bool relock) 868 { 869 PageEventWaiter waiter; 870 waiter.thread = thread_get_current_thread(); 871 waiter.next = fPageEventWaiters; 872 waiter.page = page; 873 waiter.events = events; 874 875 fPageEventWaiters = &waiter; 876 877 thread_prepare_to_block(waiter.thread, 0, THREAD_BLOCK_TYPE_OTHER, 878 "cache page events"); 879 880 Unlock(); 881 thread_block(); 882 883 if (relock) 884 Lock(); 885 } 886 887 888 /*! Makes this case the source of the \a consumer cache, 889 and adds the \a consumer to its list. 890 This also grabs a reference to the source cache. 891 Assumes you have the cache and the consumer's lock held. 892 */ 893 void 894 VMCache::AddConsumer(VMCache* consumer) 895 { 896 TRACE(("add consumer vm cache %p to cache %p\n", consumer, cache)); 897 AssertLocked(); 898 consumer->AssertLocked(); 899 900 T(AddConsumer(this, consumer)); 901 902 consumer->source = this; 903 list_add_item(&consumers, consumer); 904 905 AcquireRefLocked(); 906 AcquireStoreRef(); 907 } 908 909 910 /*! Adds the \a area to this cache. 911 Assumes you have the locked the cache. 912 */ 913 status_t 914 VMCache::InsertAreaLocked(VMArea* area) 915 { 916 TRACE(("VMCache::InsertAreaLocked(cache %p, area %p)\n", this, area)); 917 AssertLocked(); 918 919 T(InsertArea(this, area)); 920 921 area->cache_next = areas; 922 if (area->cache_next) 923 area->cache_next->cache_prev = area; 924 area->cache_prev = NULL; 925 areas = area; 926 927 AcquireStoreRef(); 928 929 return B_OK; 930 } 931 932 933 status_t 934 VMCache::RemoveArea(VMArea* area) 935 { 936 TRACE(("VMCache::RemoveArea(cache %p, area %p)\n", this, area)); 937 938 T(RemoveArea(this, area)); 939 940 // We release the store reference first, since otherwise we would reverse 941 // the locking order or even deadlock ourselves (... -> free_vnode() -> ... 942 // -> bfs_remove_vnode() -> ... -> file_cache_set_size() -> mutex_lock()). 943 // Also cf. _RemoveConsumer(). 944 ReleaseStoreRef(); 945 946 AutoLocker<VMCache> locker(this); 947 948 if (area->cache_prev) 949 area->cache_prev->cache_next = area->cache_next; 950 if (area->cache_next) 951 area->cache_next->cache_prev = area->cache_prev; 952 if (areas == area) 953 areas = area->cache_next; 954 955 return B_OK; 956 } 957 958 959 /*! Transfers the areas from \a fromCache to this cache. This cache must not 960 have areas yet. Both caches must be locked. 961 */ 962 void 963 VMCache::TransferAreas(VMCache* fromCache) 964 { 965 AssertLocked(); 966 fromCache->AssertLocked(); 967 ASSERT(areas == NULL); 968 969 areas = fromCache->areas; 970 fromCache->areas = NULL; 971 972 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 973 area->cache = this; 974 AcquireRefLocked(); 975 fromCache->ReleaseRefLocked(); 976 977 T(RemoveArea(fromCache, area)); 978 T(InsertArea(this, area)); 979 } 980 } 981 982 983 uint32 984 VMCache::CountWritableAreas(VMArea* ignoreArea) const 985 { 986 uint32 count = 0; 987 988 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 989 if (area != ignoreArea 990 && (area->protection & (B_WRITE_AREA | B_KERNEL_WRITE_AREA)) != 0) { 991 count++; 992 } 993 } 994 995 return count; 996 } 997 998 999 status_t 1000 VMCache::WriteModified() 1001 { 1002 TRACE(("VMCache::WriteModified(cache = %p)\n", this)); 1003 1004 if (temporary) 1005 return B_OK; 1006 1007 Lock(); 1008 status_t status = vm_page_write_modified_pages(this); 1009 Unlock(); 1010 1011 return status; 1012 } 1013 1014 1015 /*! Commits the memory to the store if the \a commitment is larger than 1016 what's committed already. 1017 Assumes you have the cache's lock held. 1018 */ 1019 status_t 1020 VMCache::SetMinimalCommitment(off_t commitment, int priority) 1021 { 1022 TRACE(("VMCache::SetMinimalCommitment(cache %p, commitment %Ld)\n", 1023 this, commitment)); 1024 AssertLocked(); 1025 1026 T(SetMinimalCommitment(this, commitment)); 1027 1028 status_t status = B_OK; 1029 1030 // If we don't have enough committed space to cover through to the new end 1031 // of the area... 1032 if (committed_size < commitment) { 1033 // ToDo: should we check if the cache's virtual size is large 1034 // enough for a commitment of that size? 1035 1036 // try to commit more memory 1037 status = Commit(commitment, priority); 1038 } 1039 1040 return status; 1041 } 1042 1043 1044 /*! This function updates the size field of the cache. 1045 If needed, it will free up all pages that don't belong to the cache anymore. 1046 The cache lock must be held when you call it. 1047 Since removed pages don't belong to the cache any longer, they are not 1048 written back before they will be removed. 1049 1050 Note, this function may temporarily release the cache lock in case it 1051 has to wait for busy pages. 1052 */ 1053 status_t 1054 VMCache::Resize(off_t newSize, int priority) 1055 { 1056 TRACE(("VMCache::Resize(cache %p, newSize %Ld) old size %Ld\n", 1057 this, newSize, this->virtual_end)); 1058 this->AssertLocked(); 1059 1060 T(Resize(this, newSize)); 1061 1062 status_t status = Commit(newSize - virtual_base, priority); 1063 if (status != B_OK) 1064 return status; 1065 1066 uint32 oldPageCount = (uint32)((virtual_end + B_PAGE_SIZE - 1) 1067 >> PAGE_SHIFT); 1068 uint32 newPageCount = (uint32)((newSize + B_PAGE_SIZE - 1) >> PAGE_SHIFT); 1069 1070 if (newPageCount < oldPageCount) { 1071 // we need to remove all pages in the cache outside of the new virtual 1072 // size 1073 for (VMCachePagesTree::Iterator it 1074 = pages.GetIterator(newPageCount, true, true); 1075 vm_page* page = it.Next();) { 1076 if (page->busy) { 1077 if (page->busy_writing) { 1078 // We cannot wait for the page to become available 1079 // as we might cause a deadlock this way 1080 page->busy_writing = false; 1081 // this will notify the writer to free the page 1082 } else { 1083 // wait for page to become unbusy 1084 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1085 1086 // restart from the start of the list 1087 it = pages.GetIterator(newPageCount, true, true); 1088 } 1089 continue; 1090 } 1091 1092 // remove the page and put it into the free queue 1093 DEBUG_PAGE_ACCESS_START(page); 1094 vm_remove_all_page_mappings(page); 1095 ASSERT(page->WiredCount() == 0); 1096 // TODO: Find a real solution! If the page is wired 1097 // temporarily (e.g. by lock_memory()), we actually must not 1098 // unmap it! 1099 RemovePage(page); 1100 vm_page_free(this, page); 1101 // Note: When iterating through a IteratableSplayTree 1102 // removing the current node is safe. 1103 } 1104 } 1105 1106 virtual_end = newSize; 1107 return B_OK; 1108 } 1109 1110 1111 /*! You have to call this function with the VMCache lock held. */ 1112 status_t 1113 VMCache::FlushAndRemoveAllPages() 1114 { 1115 ASSERT_LOCKED_MUTEX(&fLock); 1116 1117 while (page_count > 0) { 1118 // write back modified pages 1119 status_t status = vm_page_write_modified_pages(this); 1120 if (status != B_OK) 1121 return status; 1122 1123 // remove pages 1124 for (VMCachePagesTree::Iterator it = pages.GetIterator(); 1125 vm_page* page = it.Next();) { 1126 if (page->busy) { 1127 // wait for page to become unbusy 1128 WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); 1129 1130 // restart from the start of the list 1131 it = pages.GetIterator(); 1132 continue; 1133 } 1134 1135 // skip modified pages -- they will be written back in the next 1136 // iteration 1137 if (page->State() == PAGE_STATE_MODIFIED) 1138 continue; 1139 1140 // We can't remove mapped pages. 1141 if (page->IsMapped()) 1142 return B_BUSY; 1143 1144 DEBUG_PAGE_ACCESS_START(page); 1145 RemovePage(page); 1146 vm_page_free(this, page); 1147 // Note: When iterating through a IteratableSplayTree 1148 // removing the current node is safe. 1149 } 1150 } 1151 1152 return B_OK; 1153 } 1154 1155 1156 status_t 1157 VMCache::Commit(off_t size, int priority) 1158 { 1159 committed_size = size; 1160 return B_OK; 1161 } 1162 1163 1164 /*! Returns whether the cache's underlying backing store could deliver the 1165 page at the given offset. 1166 1167 Basically it returns whether a Read() at \a offset would at least read a 1168 partial page (assuming that no unexpected errors occur or the situation 1169 changes in the meantime). 1170 */ 1171 bool 1172 VMCache::HasPage(off_t offset) 1173 { 1174 // In accordance with Fault() the default implementation doesn't have a 1175 // backing store and doesn't allow faults. 1176 return false; 1177 } 1178 1179 1180 status_t 1181 VMCache::Read(off_t offset, const generic_io_vec *vecs, size_t count, 1182 uint32 flags, generic_size_t *_numBytes) 1183 { 1184 return B_ERROR; 1185 } 1186 1187 1188 status_t 1189 VMCache::Write(off_t offset, const generic_io_vec *vecs, size_t count, 1190 uint32 flags, generic_size_t *_numBytes) 1191 { 1192 return B_ERROR; 1193 } 1194 1195 1196 status_t 1197 VMCache::WriteAsync(off_t offset, const generic_io_vec* vecs, size_t count, 1198 generic_size_t numBytes, uint32 flags, AsyncIOCallback* callback) 1199 { 1200 // Not supported, fall back to the synchronous hook. 1201 generic_size_t transferred = numBytes; 1202 status_t error = Write(offset, vecs, count, flags, &transferred); 1203 1204 if (callback != NULL) 1205 callback->IOFinished(error, transferred != numBytes, transferred); 1206 1207 return error; 1208 } 1209 1210 1211 /*! \brief Returns whether the cache can write the page at the given offset. 1212 1213 The cache must be locked when this function is invoked. 1214 1215 @param offset The page offset. 1216 @return \c true, if the page can be written, \c false otherwise. 1217 */ 1218 bool 1219 VMCache::CanWritePage(off_t offset) 1220 { 1221 return false; 1222 } 1223 1224 1225 status_t 1226 VMCache::Fault(struct VMAddressSpace *aspace, off_t offset) 1227 { 1228 return B_BAD_ADDRESS; 1229 } 1230 1231 1232 void 1233 VMCache::Merge(VMCache* source) 1234 { 1235 for (VMCachePagesTree::Iterator it = source->pages.GetIterator(); 1236 vm_page* page = it.Next();) { 1237 // Note: Removing the current node while iterating through a 1238 // IteratableSplayTree is safe. 1239 vm_page* consumerPage = LookupPage( 1240 (off_t)page->cache_offset << PAGE_SHIFT); 1241 if (consumerPage == NULL) { 1242 // the page is not yet in the consumer cache - move it upwards 1243 MovePage(page); 1244 } 1245 } 1246 } 1247 1248 1249 status_t 1250 VMCache::AcquireUnreferencedStoreRef() 1251 { 1252 return B_OK; 1253 } 1254 1255 1256 void 1257 VMCache::AcquireStoreRef() 1258 { 1259 } 1260 1261 1262 void 1263 VMCache::ReleaseStoreRef() 1264 { 1265 } 1266 1267 1268 /*! Kernel debugger version of HasPage(). 1269 Does not do any locking. 1270 */ 1271 bool 1272 VMCache::DebugHasPage(off_t offset) 1273 { 1274 // default that works for all subclasses that don't lock anyway 1275 return HasPage(offset); 1276 } 1277 1278 1279 /*! Kernel debugger version of LookupPage(). 1280 Does not do any locking. 1281 */ 1282 vm_page* 1283 VMCache::DebugLookupPage(off_t offset) 1284 { 1285 return pages.Lookup((page_num_t)(offset >> PAGE_SHIFT)); 1286 } 1287 1288 1289 void 1290 VMCache::Dump(bool showPages) const 1291 { 1292 kprintf("CACHE %p:\n", this); 1293 kprintf(" ref_count: %ld\n", RefCount()); 1294 kprintf(" source: %p\n", source); 1295 kprintf(" type: %s\n", vm_cache_type_to_string(type)); 1296 kprintf(" virtual_base: 0x%Lx\n", virtual_base); 1297 kprintf(" virtual_end: 0x%Lx\n", virtual_end); 1298 kprintf(" temporary: %ld\n", temporary); 1299 kprintf(" lock: %p\n", &fLock); 1300 #if KDEBUG 1301 kprintf(" lock.holder: %ld\n", fLock.holder); 1302 #endif 1303 kprintf(" areas:\n"); 1304 1305 for (VMArea* area = areas; area != NULL; area = area->cache_next) { 1306 kprintf(" area 0x%lx, %s\n", area->id, area->name); 1307 kprintf("\tbase_addr: 0x%lx, size: 0x%lx\n", area->Base(), 1308 area->Size()); 1309 kprintf("\tprotection: 0x%lx\n", area->protection); 1310 kprintf("\towner: 0x%lx\n", area->address_space->ID()); 1311 } 1312 1313 kprintf(" consumers:\n"); 1314 VMCache* consumer = NULL; 1315 while ((consumer = (VMCache*)list_get_next_item((list*)&consumers, 1316 consumer)) != NULL) { 1317 kprintf("\t%p\n", consumer); 1318 } 1319 1320 kprintf(" pages:\n"); 1321 if (showPages) { 1322 for (VMCachePagesTree::ConstIterator it = pages.GetIterator(); 1323 vm_page* page = it.Next();) { 1324 if (!vm_page_is_dummy(page)) { 1325 kprintf("\t%p ppn %#" B_PRIxPHYSADDR " offset %#" B_PRIxPHYSADDR 1326 " state %u (%s) wired_count %u\n", page, 1327 page->physical_page_number, page->cache_offset, 1328 page->State(), page_state_to_string(page->State()), 1329 page->WiredCount()); 1330 } else { 1331 kprintf("\t%p DUMMY PAGE state %u (%s)\n", 1332 page, page->State(), page_state_to_string(page->State())); 1333 } 1334 } 1335 } else 1336 kprintf("\t%ld in cache\n", page_count); 1337 } 1338 1339 1340 /*! Wakes up threads waiting for page events. 1341 \param page The page for which events occurred. 1342 \param events The mask of events that occurred. 1343 */ 1344 void 1345 VMCache::_NotifyPageEvents(vm_page* page, uint32 events) 1346 { 1347 PageEventWaiter** it = &fPageEventWaiters; 1348 while (PageEventWaiter* waiter = *it) { 1349 if (waiter->page == page && (waiter->events & events) != 0) { 1350 // remove from list and unblock 1351 *it = waiter->next; 1352 InterruptsSpinLocker threadsLocker(gThreadSpinlock); 1353 thread_unblock_locked(waiter->thread, B_OK); 1354 } else 1355 it = &waiter->next; 1356 } 1357 } 1358 1359 1360 /*! Merges the given cache with its only consumer. 1361 The caller must hold both the cache's and the consumer's lock. The method 1362 will unlock the consumer lock. 1363 */ 1364 void 1365 VMCache::_MergeWithOnlyConsumer(bool consumerLocked) 1366 { 1367 VMCache* consumer = (VMCache*)list_remove_head_item(&consumers); 1368 1369 TRACE(("merge vm cache %p (ref == %ld) with vm cache %p\n", 1370 this, this->fRefCount, consumer)); 1371 1372 T(Merge(this, consumer)); 1373 1374 // merge the cache 1375 consumer->Merge(this); 1376 1377 // The remaining consumer has got a new source. 1378 if (source != NULL) { 1379 VMCache* newSource = source; 1380 1381 newSource->Lock(); 1382 1383 list_remove_item(&newSource->consumers, this); 1384 list_add_item(&newSource->consumers, consumer); 1385 consumer->source = newSource; 1386 source = NULL; 1387 1388 newSource->Unlock(); 1389 } else 1390 consumer->source = NULL; 1391 1392 // Release the reference the cache's consumer owned. The consumer takes 1393 // over the cache's ref to its source (if any) instead. 1394 ReleaseRefLocked(); 1395 1396 if (!consumerLocked) 1397 consumer->Unlock(); 1398 } 1399 1400 1401 /*! Removes the \a consumer from this cache. 1402 It will also release the reference to the cache owned by the consumer. 1403 Assumes you have the consumer's cache lock held. This cache must not be 1404 locked. 1405 */ 1406 void 1407 VMCache::_RemoveConsumer(VMCache* consumer) 1408 { 1409 TRACE(("remove consumer vm cache %p from cache %p\n", consumer, this)); 1410 consumer->AssertLocked(); 1411 1412 T(RemoveConsumer(this, consumer)); 1413 1414 // Remove the store ref before locking the cache. Otherwise we'd call into 1415 // the VFS while holding the cache lock, which would reverse the usual 1416 // locking order. 1417 ReleaseStoreRef(); 1418 1419 // remove the consumer from the cache, but keep its reference until later 1420 Lock(); 1421 list_remove_item(&consumers, consumer); 1422 consumer->source = NULL; 1423 1424 ReleaseRefAndUnlock(); 1425 } 1426 1427 1428 // #pragma mark - VMCacheFactory 1429 // TODO: Move to own source file! 1430 1431 1432 #include <heap.h> 1433 1434 #include "VMAnonymousCache.h" 1435 #include "VMAnonymousNoSwapCache.h" 1436 #include "VMDeviceCache.h" 1437 #include "VMNullCache.h" 1438 #include "../cache/vnode_store.h" 1439 1440 1441 /*static*/ status_t 1442 VMCacheFactory::CreateAnonymousCache(VMCache*& _cache, bool canOvercommit, 1443 int32 numPrecommittedPages, int32 numGuardPages, bool swappable, 1444 int priority) 1445 { 1446 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1447 | HEAP_DONT_LOCK_KERNEL_SPACE; 1448 if (priority >= VM_PRIORITY_VIP) 1449 allocationFlags |= HEAP_PRIORITY_VIP; 1450 1451 #if ENABLE_SWAP_SUPPORT 1452 if (swappable) { 1453 VMAnonymousCache* cache 1454 = new(malloc_flags(allocationFlags)) VMAnonymousCache; 1455 if (cache == NULL) 1456 return B_NO_MEMORY; 1457 1458 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1459 numGuardPages, allocationFlags); 1460 if (error != B_OK) { 1461 cache->Delete(); 1462 return error; 1463 } 1464 1465 T(Create(cache)); 1466 1467 _cache = cache; 1468 return B_OK; 1469 } 1470 #endif 1471 1472 VMAnonymousNoSwapCache* cache 1473 = new(malloc_flags(allocationFlags)) VMAnonymousNoSwapCache; 1474 if (cache == NULL) 1475 return B_NO_MEMORY; 1476 1477 status_t error = cache->Init(canOvercommit, numPrecommittedPages, 1478 numGuardPages, allocationFlags); 1479 if (error != B_OK) { 1480 cache->Delete(); 1481 return error; 1482 } 1483 1484 T(Create(cache)); 1485 1486 _cache = cache; 1487 return B_OK; 1488 } 1489 1490 1491 /*static*/ status_t 1492 VMCacheFactory::CreateVnodeCache(VMCache*& _cache, struct vnode* vnode) 1493 { 1494 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1495 | HEAP_DONT_LOCK_KERNEL_SPACE; 1496 // Note: Vnode cache creation is never VIP. 1497 1498 VMVnodeCache* cache = new(malloc_flags(allocationFlags)) VMVnodeCache; 1499 if (cache == NULL) 1500 return B_NO_MEMORY; 1501 1502 status_t error = cache->Init(vnode, allocationFlags); 1503 if (error != B_OK) { 1504 cache->Delete(); 1505 return error; 1506 } 1507 1508 T(Create(cache)); 1509 1510 _cache = cache; 1511 return B_OK; 1512 } 1513 1514 1515 /*static*/ status_t 1516 VMCacheFactory::CreateDeviceCache(VMCache*& _cache, addr_t baseAddress) 1517 { 1518 const uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1519 | HEAP_DONT_LOCK_KERNEL_SPACE; 1520 // Note: Device cache creation is never VIP. 1521 1522 VMDeviceCache* cache = new(malloc_flags(allocationFlags)) VMDeviceCache; 1523 if (cache == NULL) 1524 return B_NO_MEMORY; 1525 1526 status_t error = cache->Init(baseAddress, allocationFlags); 1527 if (error != B_OK) { 1528 cache->Delete(); 1529 return error; 1530 } 1531 1532 T(Create(cache)); 1533 1534 _cache = cache; 1535 return B_OK; 1536 } 1537 1538 1539 /*static*/ status_t 1540 VMCacheFactory::CreateNullCache(int priority, VMCache*& _cache) 1541 { 1542 uint32 allocationFlags = HEAP_DONT_WAIT_FOR_MEMORY 1543 | HEAP_DONT_LOCK_KERNEL_SPACE; 1544 if (priority >= VM_PRIORITY_VIP) 1545 allocationFlags |= HEAP_PRIORITY_VIP; 1546 1547 VMNullCache* cache = new(malloc_flags(allocationFlags)) VMNullCache; 1548 if (cache == NULL) 1549 return B_NO_MEMORY; 1550 1551 status_t error = cache->Init(allocationFlags); 1552 if (error != B_OK) { 1553 cache->Delete(); 1554 return error; 1555 } 1556 1557 T(Create(cache)); 1558 1559 _cache = cache; 1560 return B_OK; 1561 } 1562