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