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