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