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