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