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