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