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