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