2 * Copyright 2008, Zhao Shuai, upczhsh@163.com.
3 * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@gmx.de.
4 * Copyright 2002-2009, Axel Dörfler, axeld@pinc-software.de.
5 * Distributed under the terms of the MIT License.
7 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
8 * Distributed under the terms of the NewOS License.
10 * Copyright 2011-2012 Haiku, Inc. All rights reserved.
11 * Distributed under the terms of the MIT License.
14 * Hamish Morrison, hamish@lavabit.com
15 * Alexander von Gluck IV, kallisti5@unixzen.com
19 #include "VMAnonymousCache.h"
27 #include <FindDirectory.h>
28 #include <KernelExport.h>
29 #include <NodeMonitor.h>
31 #include <arch_config.h>
32 #include <boot_device.h>
33 #include <disk_device_manager/KDiskDevice.h>
34 #include <disk_device_manager/KDiskDeviceManager.h>
35 #include <disk_device_manager/KDiskSystem.h>
36 #include <disk_device_manager/KPartitionVisitor.h>
37 #include <driver_settings.h>
41 #include <fs_interface.h>
43 #include <kernel_daemon.h>
44 #include <slab/Slab.h>
46 #include <system_info.h>
48 #include <util/AutoLock.h>
49 #include <util/DoublyLinkedList.h>
50 #include <util/OpenHashTable.h>
51 #include <util/RadixBitmap.h>
54 #include <vm/vm_page.h>
55 #include <vm/vm_priv.h>
56 #include <vm/VMAddressSpace.h>
58 #include "IORequest.h"
61 #if ENABLE_SWAP_SUPPORT
63 //#define TRACE_VM_ANONYMOUS_CACHE
64 #ifdef TRACE_VM_ANONYMOUS_CACHE
65 # define TRACE(x...) dprintf(x)
67 # define TRACE(x...) do { } while (false)
71 // number of free swap blocks the object cache shall minimally have
72 #define MIN_SWAP_BLOCK_RESERVE 4096
74 // interval the has resizer is triggered (in 0.1s)
75 #define SWAP_HASH_RESIZE_INTERVAL 5
77 #define INITIAL_SWAP_HASH_SIZE 1024
79 #define SWAP_SLOT_NONE RADIX_SLOT_NONE
81 #define SWAP_BLOCK_PAGES 32
82 #define SWAP_BLOCK_SHIFT 5 /* 1 << SWAP_BLOCK_SHIFT == SWAP_BLOCK_PAGES */
83 #define SWAP_BLOCK_MASK (SWAP_BLOCK_PAGES - 1)
86 static const char* const kDefaultSwapPath
= "/var/swap";
88 struct swap_file
: DoublyLinkedListLinkImpl
<swap_file
> {
92 swap_addr_t first_slot
;
93 swap_addr_t last_slot
;
97 struct swap_hash_key
{
98 VMAnonymousCache
*cache
;
99 off_t page_index
; // page index in the cache
102 // Each swap block contains swap address information for
103 // SWAP_BLOCK_PAGES continuous pages from the same cache
105 swap_block
* hash_link
;
108 swap_addr_t swap_slots
[SWAP_BLOCK_PAGES
];
111 struct SwapHashTableDefinition
{
112 typedef swap_hash_key KeyType
;
113 typedef swap_block ValueType
;
115 SwapHashTableDefinition() {}
117 size_t HashKey(const swap_hash_key
& key
) const
119 off_t blockIndex
= key
.page_index
>> SWAP_BLOCK_SHIFT
;
120 VMAnonymousCache
* cache
= key
.cache
;
121 return blockIndex
^ (size_t)(int*)cache
;
124 size_t Hash(const swap_block
* value
) const
126 return HashKey(value
->key
);
129 bool Compare(const swap_hash_key
& key
, const swap_block
* value
) const
131 return (key
.page_index
& ~(off_t
)SWAP_BLOCK_MASK
)
132 == (value
->key
.page_index
& ~(off_t
)SWAP_BLOCK_MASK
)
133 && key
.cache
== value
->key
.cache
;
136 swap_block
*& GetLink(swap_block
* value
) const
138 return value
->hash_link
;
142 typedef BOpenHashTable
<SwapHashTableDefinition
> SwapHashTable
;
143 typedef DoublyLinkedList
<swap_file
> SwapFileList
;
145 static SwapHashTable sSwapHashTable
;
146 static rw_lock sSwapHashLock
;
148 static SwapFileList sSwapFileList
;
149 static mutex sSwapFileListLock
;
150 static swap_file
* sSwapFileAlloc
= NULL
; // allocate from here
151 static uint32 sSwapFileCount
= 0;
153 static off_t sAvailSwapSpace
= 0;
154 static mutex sAvailSwapSpaceLock
;
156 static object_cache
* sSwapBlockCache
;
160 namespace SwapTracing
{
162 class SwapTraceEntry
: public AbstractTraceEntry
{
164 SwapTraceEntry(VMAnonymousCache
* cache
)
171 VMAnonymousCache
* fCache
;
175 class ReadPage
: public SwapTraceEntry
{
177 ReadPage(VMAnonymousCache
* cache
, page_num_t pageIndex
,
178 swap_addr_t swapSlotIndex
)
180 SwapTraceEntry(cache
),
181 fPageIndex(pageIndex
),
182 fSwapSlotIndex(swapSlotIndex
)
187 virtual void AddDump(TraceOutput
& out
)
189 out
.Print("swap read: cache %p, page index: %lu <- swap slot: %lu",
190 fCache
, fPageIndex
, fSwapSlotIndex
);
194 page_num_t fPageIndex
;
195 swap_addr_t fSwapSlotIndex
;
199 class WritePage
: public SwapTraceEntry
{
201 WritePage(VMAnonymousCache
* cache
, page_num_t pageIndex
,
202 swap_addr_t swapSlotIndex
)
204 SwapTraceEntry(cache
),
205 fPageIndex(pageIndex
),
206 fSwapSlotIndex(swapSlotIndex
)
211 virtual void AddDump(TraceOutput
& out
)
213 out
.Print("swap write: cache %p, page index: %lu -> swap slot: %lu",
214 fCache
, fPageIndex
, fSwapSlotIndex
);
218 page_num_t fPageIndex
;
219 swap_addr_t fSwapSlotIndex
;
222 } // namespace SwapTracing
224 # define T(x) new(std::nothrow) SwapTracing::x;
231 dump_swap_info(int argc
, char** argv
)
233 swap_addr_t totalSwapPages
= 0;
234 swap_addr_t freeSwapPages
= 0;
236 kprintf("swap files:\n");
238 for (SwapFileList::Iterator it
= sSwapFileList
.GetIterator();
239 swap_file
* file
= it
.Next();) {
240 swap_addr_t total
= file
->last_slot
- file
->first_slot
;
241 kprintf(" vnode: %p, pages: total: %" B_PRIu32
", free: %" B_PRIu32
242 "\n", file
->vnode
, total
, file
->bmp
->free_slots
);
244 totalSwapPages
+= total
;
245 freeSwapPages
+= file
->bmp
->free_slots
;
249 kprintf("swap space in pages:\n");
250 kprintf("total: %9" B_PRIu32
"\n", totalSwapPages
);
251 kprintf("available: %9" B_PRIdOFF
"\n", sAvailSwapSpace
/ B_PAGE_SIZE
);
252 kprintf("reserved: %9" B_PRIdOFF
"\n",
253 totalSwapPages
- sAvailSwapSpace
/ B_PAGE_SIZE
);
254 kprintf("used: %9" B_PRIu32
"\n", totalSwapPages
- freeSwapPages
);
255 kprintf("free: %9" B_PRIu32
"\n", freeSwapPages
);
262 swap_slot_alloc(uint32 count
)
264 mutex_lock(&sSwapFileListLock
);
266 if (sSwapFileList
.IsEmpty()) {
267 mutex_unlock(&sSwapFileListLock
);
268 panic("swap_slot_alloc(): no swap file in the system\n");
269 return SWAP_SLOT_NONE
;
272 // since radix bitmap could not handle more than 32 pages, we return
273 // SWAP_SLOT_NONE, this forces Write() adjust allocation amount
274 if (count
> BITMAP_RADIX
) {
275 mutex_unlock(&sSwapFileListLock
);
276 return SWAP_SLOT_NONE
;
279 swap_addr_t j
, addr
= SWAP_SLOT_NONE
;
280 for (j
= 0; j
< sSwapFileCount
; j
++) {
281 if (sSwapFileAlloc
== NULL
)
282 sSwapFileAlloc
= sSwapFileList
.First();
284 addr
= radix_bitmap_alloc(sSwapFileAlloc
->bmp
, count
);
285 if (addr
!= SWAP_SLOT_NONE
) {
286 addr
+= sSwapFileAlloc
->first_slot
;
290 // this swap_file is full, find another
291 sSwapFileAlloc
= sSwapFileList
.GetNext(sSwapFileAlloc
);
294 if (j
== sSwapFileCount
) {
295 mutex_unlock(&sSwapFileListLock
);
296 panic("swap_slot_alloc: swap space exhausted!\n");
297 return SWAP_SLOT_NONE
;
300 // if this swap file has used more than 90% percent of its space
302 if (sSwapFileAlloc
->bmp
->free_slots
303 < (sSwapFileAlloc
->last_slot
- sSwapFileAlloc
->first_slot
) / 10) {
304 sSwapFileAlloc
= sSwapFileList
.GetNext(sSwapFileAlloc
);
307 mutex_unlock(&sSwapFileListLock
);
314 find_swap_file(swap_addr_t slotIndex
)
316 for (SwapFileList::Iterator it
= sSwapFileList
.GetIterator();
317 swap_file
* swapFile
= it
.Next();) {
318 if (slotIndex
>= swapFile
->first_slot
319 && slotIndex
< swapFile
->last_slot
) {
324 panic("find_swap_file(): can't find swap file for slot %" B_PRIu32
"\n",
331 swap_slot_dealloc(swap_addr_t slotIndex
, uint32 count
)
333 if (slotIndex
== SWAP_SLOT_NONE
)
336 mutex_lock(&sSwapFileListLock
);
337 swap_file
* swapFile
= find_swap_file(slotIndex
);
338 slotIndex
-= swapFile
->first_slot
;
339 radix_bitmap_dealloc(swapFile
->bmp
, slotIndex
, count
);
340 mutex_unlock(&sSwapFileListLock
);
345 swap_space_reserve(off_t amount
)
347 mutex_lock(&sAvailSwapSpaceLock
);
348 if (sAvailSwapSpace
>= amount
)
349 sAvailSwapSpace
-= amount
;
351 amount
= sAvailSwapSpace
;
354 mutex_unlock(&sAvailSwapSpaceLock
);
361 swap_space_unreserve(off_t amount
)
363 mutex_lock(&sAvailSwapSpaceLock
);
364 sAvailSwapSpace
+= amount
;
365 mutex_unlock(&sAvailSwapSpaceLock
);
370 swap_hash_resizer(void*, int)
372 WriteLocker
locker(sSwapHashLock
);
378 size
= sSwapHashTable
.ResizeNeeded();
384 allocation
= malloc(size
);
385 if (allocation
== NULL
)
390 } while (!sSwapHashTable
.Resize(allocation
, size
));
397 class VMAnonymousCache::WriteCallback
: public StackableAsyncIOCallback
{
399 WriteCallback(VMAnonymousCache
* cache
, AsyncIOCallback
* callback
)
401 StackableAsyncIOCallback(callback
),
406 void SetTo(page_num_t pageIndex
, swap_addr_t slotIndex
, bool newSlot
)
408 fPageIndex
= pageIndex
;
409 fSlotIndex
= slotIndex
;
413 virtual void IOFinished(status_t status
, bool partialTransfer
,
414 generic_size_t bytesTransferred
)
417 if (status
== B_OK
) {
418 fCache
->_SwapBlockBuild(fPageIndex
, fSlotIndex
, 1);
420 AutoLocker
<VMCache
> locker(fCache
);
421 fCache
->fAllocatedSwapSize
-= B_PAGE_SIZE
;
424 swap_slot_dealloc(fSlotIndex
, 1);
428 fNextCallback
->IOFinished(status
, partialTransfer
, bytesTransferred
);
434 VMAnonymousCache
* fCache
;
435 page_num_t fPageIndex
;
436 swap_addr_t fSlotIndex
;
444 VMAnonymousCache::~VMAnonymousCache()
446 // free allocated swap space and swap block
447 for (off_t offset
= virtual_base
, toFree
= fAllocatedSwapSize
;
448 offset
< virtual_end
&& toFree
> 0; offset
+= B_PAGE_SIZE
) {
449 swap_addr_t slotIndex
= _SwapBlockGetAddress(offset
>> PAGE_SHIFT
);
450 if (slotIndex
== SWAP_SLOT_NONE
)
453 swap_slot_dealloc(slotIndex
, 1);
454 _SwapBlockFree(offset
>> PAGE_SHIFT
, 1);
455 toFree
-= B_PAGE_SIZE
;
458 swap_space_unreserve(fCommittedSwapSize
);
459 if (committed_size
> fCommittedSwapSize
)
460 vm_unreserve_memory(committed_size
- fCommittedSwapSize
);
465 VMAnonymousCache::Init(bool canOvercommit
, int32 numPrecommittedPages
,
466 int32 numGuardPages
, uint32 allocationFlags
)
468 TRACE("%p->VMAnonymousCache::Init(canOvercommit = %s, "
469 "numPrecommittedPages = %" B_PRId32
", numGuardPages = %" B_PRId32
470 ")\n", this, canOvercommit
? "yes" : "no", numPrecommittedPages
,
473 status_t error
= VMCache::Init(CACHE_TYPE_RAM
, allocationFlags
);
477 fCanOvercommit
= canOvercommit
;
478 fHasPrecommitted
= false;
479 fPrecommittedPages
= min_c(numPrecommittedPages
, 255);
480 fGuardedSize
= numGuardPages
* B_PAGE_SIZE
;
481 fCommittedSwapSize
= 0;
482 fAllocatedSwapSize
= 0;
489 VMAnonymousCache::Resize(off_t newSize
, int priority
)
491 // If the cache size shrinks, drop all swap pages beyond the new size.
492 if (fAllocatedSwapSize
> 0) {
493 off_t oldPageCount
= (virtual_end
+ B_PAGE_SIZE
- 1) >> PAGE_SHIFT
;
494 swap_block
* swapBlock
= NULL
;
496 for (off_t pageIndex
= (newSize
+ B_PAGE_SIZE
- 1) >> PAGE_SHIFT
;
497 pageIndex
< oldPageCount
&& fAllocatedSwapSize
> 0; pageIndex
++) {
499 WriteLocker
locker(sSwapHashLock
);
501 // Get the swap slot index for the page.
502 swap_addr_t blockIndex
= pageIndex
& SWAP_BLOCK_MASK
;
503 if (swapBlock
== NULL
|| blockIndex
== 0) {
504 swap_hash_key key
= { this, pageIndex
};
505 swapBlock
= sSwapHashTable
.Lookup(key
);
507 if (swapBlock
== NULL
) {
508 pageIndex
= ROUNDUP(pageIndex
+ 1, SWAP_BLOCK_PAGES
);
513 swap_addr_t slotIndex
= swapBlock
->swap_slots
[blockIndex
];
515 if (slotIndex
!= SWAP_SLOT_NONE
516 && ((page
= LookupPage((off_t
)pageIndex
* B_PAGE_SIZE
)) == NULL
518 // TODO: We skip (i.e. leak) swap space of busy pages, since
519 // there could be I/O going on (paging in/out). Waiting is
520 // not an option as 1. unlocking the cache means that new
521 // swap pages could be added in a range we've already
522 // cleared (since the cache still has the old size) and 2.
523 // we'd risk a deadlock in case we come from the file cache
524 // and the FS holds the node's write-lock. We should mark
525 // the page invalid and let the one responsible clean up.
526 // There's just no such mechanism yet.
527 swap_slot_dealloc(slotIndex
, 1);
528 fAllocatedSwapSize
-= B_PAGE_SIZE
;
530 swapBlock
->swap_slots
[blockIndex
] = SWAP_SLOT_NONE
;
531 if (--swapBlock
->used
== 0) {
532 // All swap pages have been freed -- we can discard the swap
534 sSwapHashTable
.RemoveUnchecked(swapBlock
);
535 object_cache_free(sSwapBlockCache
, swapBlock
,
536 CACHE_DONT_WAIT_FOR_MEMORY
537 | CACHE_DONT_LOCK_KERNEL_SPACE
);
543 return VMCache::Resize(newSize
, priority
);
548 VMAnonymousCache::Commit(off_t size
, int priority
)
550 TRACE("%p->VMAnonymousCache::Commit(%" B_PRIdOFF
")\n", this, size
);
552 // If we can overcommit, we don't commit here, but in Fault(). We always
553 // unreserve memory, if we're asked to shrink our commitment, though.
554 if (fCanOvercommit
&& size
> committed_size
) {
555 if (fHasPrecommitted
)
558 // pre-commit some pages to make a later failure less probable
559 fHasPrecommitted
= true;
560 uint32 precommitted
= fPrecommittedPages
* B_PAGE_SIZE
;
561 if (size
> precommitted
)
565 return _Commit(size
, priority
);
570 VMAnonymousCache::HasPage(off_t offset
)
572 if (_SwapBlockGetAddress(offset
>> PAGE_SHIFT
) != SWAP_SLOT_NONE
)
580 VMAnonymousCache::DebugHasPage(off_t offset
)
582 off_t pageIndex
= offset
>> PAGE_SHIFT
;
583 swap_hash_key key
= { this, pageIndex
};
584 swap_block
* swap
= sSwapHashTable
.Lookup(key
);
588 return swap
->swap_slots
[pageIndex
& SWAP_BLOCK_MASK
] != SWAP_SLOT_NONE
;
593 VMAnonymousCache::Read(off_t offset
, const generic_io_vec
* vecs
, size_t count
,
594 uint32 flags
, generic_size_t
* _numBytes
)
596 off_t pageIndex
= offset
>> PAGE_SHIFT
;
598 for (uint32 i
= 0, j
= 0; i
< count
; i
= j
) {
599 swap_addr_t startSlotIndex
= _SwapBlockGetAddress(pageIndex
+ i
);
600 for (j
= i
+ 1; j
< count
; j
++) {
601 swap_addr_t slotIndex
= _SwapBlockGetAddress(pageIndex
+ j
);
602 if (slotIndex
!= startSlotIndex
+ j
- i
)
606 T(ReadPage(this, pageIndex
, startSlotIndex
));
607 // TODO: Assumes that only one page is read.
609 swap_file
* swapFile
= find_swap_file(startSlotIndex
);
611 off_t pos
= (off_t
)(startSlotIndex
- swapFile
->first_slot
)
614 status_t status
= vfs_read_pages(swapFile
->vnode
, swapFile
->cookie
, pos
,
615 vecs
+ i
, j
- i
, flags
, _numBytes
);
625 VMAnonymousCache::Write(off_t offset
, const generic_io_vec
* vecs
, size_t count
,
626 uint32 flags
, generic_size_t
* _numBytes
)
628 off_t pageIndex
= offset
>> PAGE_SHIFT
;
630 AutoLocker
<VMCache
> locker(this);
632 page_num_t totalPages
= 0;
633 for (uint32 i
= 0; i
< count
; i
++) {
634 page_num_t pageCount
= (vecs
[i
].length
+ B_PAGE_SIZE
- 1) >> PAGE_SHIFT
;
635 swap_addr_t slotIndex
= _SwapBlockGetAddress(pageIndex
+ totalPages
);
636 if (slotIndex
!= SWAP_SLOT_NONE
) {
637 swap_slot_dealloc(slotIndex
, pageCount
);
638 _SwapBlockFree(pageIndex
+ totalPages
, pageCount
);
639 fAllocatedSwapSize
-= pageCount
* B_PAGE_SIZE
;
642 totalPages
+= pageCount
;
645 off_t totalSize
= totalPages
* B_PAGE_SIZE
;
646 if (fAllocatedSwapSize
+ totalSize
> fCommittedSwapSize
)
649 fAllocatedSwapSize
+= totalSize
;
652 page_num_t pagesLeft
= totalPages
;
655 for (uint32 i
= 0; i
< count
; i
++) {
656 page_num_t pageCount
= (vecs
[i
].length
+ B_PAGE_SIZE
- 1) >> PAGE_SHIFT
;
658 generic_addr_t vectorBase
= vecs
[i
].base
;
659 generic_size_t vectorLength
= vecs
[i
].length
;
660 page_num_t n
= pageCount
;
662 for (page_num_t j
= 0; j
< pageCount
; j
+= n
) {
663 swap_addr_t slotIndex
;
664 // try to allocate n slots, if fail, try to allocate n/2
665 while ((slotIndex
= swap_slot_alloc(n
)) == SWAP_SLOT_NONE
&& n
>= 2)
668 if (slotIndex
== SWAP_SLOT_NONE
)
669 panic("VMAnonymousCache::Write(): can't allocate swap space\n");
671 T(WritePage(this, pageIndex
, slotIndex
));
672 // TODO: Assumes that only one page is written.
674 swap_file
* swapFile
= find_swap_file(slotIndex
);
676 off_t pos
= (off_t
)(slotIndex
- swapFile
->first_slot
) * B_PAGE_SIZE
;
678 generic_size_t length
= (phys_addr_t
)n
* B_PAGE_SIZE
;
679 generic_io_vec vector
[1];
680 vector
->base
= vectorBase
;
681 vector
->length
= length
;
683 status_t status
= vfs_write_pages(swapFile
->vnode
, swapFile
->cookie
,
684 pos
, vector
, 1, flags
, &length
);
685 if (status
!= B_OK
) {
687 fAllocatedSwapSize
-= (off_t
)pagesLeft
* B_PAGE_SIZE
;
690 swap_slot_dealloc(slotIndex
, n
);
694 _SwapBlockBuild(pageIndex
+ totalPages
, slotIndex
, n
);
697 if (n
!= pageCount
) {
698 vectorBase
= vectorBase
+ n
* B_PAGE_SIZE
;
699 vectorLength
-= n
* B_PAGE_SIZE
;
703 totalPages
+= pageCount
;
706 ASSERT(pagesLeft
== 0);
712 VMAnonymousCache::WriteAsync(off_t offset
, const generic_io_vec
* vecs
,
713 size_t count
, generic_size_t numBytes
, uint32 flags
,
714 AsyncIOCallback
* _callback
)
716 // TODO: Currently this method is only used for single pages. Either make
717 // more flexible use of it or change the interface!
718 // This implementation relies on the current usage!
720 ASSERT(numBytes
<= B_PAGE_SIZE
);
722 page_num_t pageIndex
= offset
>> PAGE_SHIFT
;
723 swap_addr_t slotIndex
= _SwapBlockGetAddress(pageIndex
);
724 bool newSlot
= slotIndex
== SWAP_SLOT_NONE
;
726 // If the page doesn't have any swap space yet, allocate it.
728 AutoLocker
<VMCache
> locker(this);
729 if (fAllocatedSwapSize
+ B_PAGE_SIZE
> fCommittedSwapSize
) {
730 _callback
->IOFinished(B_ERROR
, true, 0);
734 fAllocatedSwapSize
+= B_PAGE_SIZE
;
736 slotIndex
= swap_slot_alloc(1);
739 // create our callback
740 WriteCallback
* callback
= (flags
& B_VIP_IO_REQUEST
) != 0
741 ? new(malloc_flags(HEAP_PRIORITY_VIP
)) WriteCallback(this, _callback
)
742 : new(std::nothrow
) WriteCallback(this, _callback
);
743 if (callback
== NULL
) {
745 AutoLocker
<VMCache
> locker(this);
746 fAllocatedSwapSize
-= B_PAGE_SIZE
;
749 swap_slot_dealloc(slotIndex
, 1);
751 _callback
->IOFinished(B_NO_MEMORY
, true, 0);
754 // TODO: If the page already had swap space assigned, we don't need an own
757 callback
->SetTo(pageIndex
, slotIndex
, newSlot
);
759 T(WritePage(this, pageIndex
, slotIndex
));
761 // write the page asynchrounously
762 swap_file
* swapFile
= find_swap_file(slotIndex
);
763 off_t pos
= (off_t
)(slotIndex
- swapFile
->first_slot
) * B_PAGE_SIZE
;
765 return vfs_asynchronous_write_pages(swapFile
->vnode
, swapFile
->cookie
, pos
,
766 vecs
, 1, numBytes
, flags
, callback
);
771 VMAnonymousCache::CanWritePage(off_t offset
)
773 // We can write the page, if we have not used all of our committed swap
774 // space or the page already has a swap slot assigned.
775 return fAllocatedSwapSize
< fCommittedSwapSize
776 || _SwapBlockGetAddress(offset
>> PAGE_SHIFT
) != SWAP_SLOT_NONE
;
781 VMAnonymousCache::MaxPagesPerAsyncWrite() const
788 VMAnonymousCache::Fault(struct VMAddressSpace
* aspace
, off_t offset
)
790 if (fGuardedSize
> 0) {
793 #ifdef STACK_GROWS_DOWNWARDS
795 #elif defined(STACK_GROWS_UPWARDS)
796 guardOffset
= virtual_size
- fGuardedSize
;
798 # error Stack direction has not been defined in arch_config.h
800 // report stack fault, guard page hit!
801 if (offset
>= guardOffset
&& offset
< guardOffset
+ fGuardedSize
) {
802 TRACE(("stack overflow!\n"));
803 return B_BAD_ADDRESS
;
807 if (fCanOvercommit
&& LookupPage(offset
) == NULL
&& !HasPage(offset
)) {
808 if (fPrecommittedPages
== 0) {
809 // never commit more than needed
810 if (committed_size
/ B_PAGE_SIZE
> page_count
)
811 return B_BAD_HANDLER
;
813 // try to commit additional swap space/memory
814 if (swap_space_reserve(B_PAGE_SIZE
) == B_PAGE_SIZE
) {
815 fCommittedSwapSize
+= B_PAGE_SIZE
;
817 int priority
= aspace
== VMAddressSpace::Kernel()
818 ? VM_PRIORITY_SYSTEM
: VM_PRIORITY_USER
;
819 if (vm_try_reserve_memory(B_PAGE_SIZE
, priority
, 0) != B_OK
) {
820 dprintf("%p->VMAnonymousCache::Fault(): Failed to reserve "
821 "%d bytes of RAM.\n", this, (int)B_PAGE_SIZE
);
826 committed_size
+= B_PAGE_SIZE
;
828 fPrecommittedPages
--;
831 // This will cause vm_soft_fault() to handle the fault
832 return B_BAD_HANDLER
;
837 VMAnonymousCache::Merge(VMCache
* _source
)
839 VMAnonymousCache
* source
= dynamic_cast<VMAnonymousCache
*>(_source
);
840 if (source
== NULL
) {
841 panic("VMAnonymousCache::MergeStore(): merge with incompatible cache "
842 "%p requested", _source
);
846 // take over the source' committed size
847 fCommittedSwapSize
+= source
->fCommittedSwapSize
;
848 source
->fCommittedSwapSize
= 0;
849 committed_size
+= source
->committed_size
;
850 source
->committed_size
= 0;
852 off_t actualSize
= virtual_end
- virtual_base
;
853 if (committed_size
> actualSize
)
854 _Commit(actualSize
, VM_PRIORITY_USER
);
856 // Move all not shadowed swap pages from the source to the consumer cache.
857 // Also remove all source pages that are shadowed by consumer swap pages.
858 _MergeSwapPages(source
);
860 // Move all not shadowed pages from the source to the consumer cache.
861 if (source
->page_count
< page_count
)
862 _MergePagesSmallerSource(source
);
864 _MergePagesSmallerConsumer(source
);
869 VMAnonymousCache::DeleteObject()
871 object_cache_delete(gAnonymousCacheObjectCache
, this);
876 VMAnonymousCache::_SwapBlockBuild(off_t startPageIndex
,
877 swap_addr_t startSlotIndex
, uint32 count
)
879 WriteLocker
locker(sSwapHashLock
);
882 for (uint32 i
= 0, j
= 0; i
< count
; i
+= j
) {
883 off_t pageIndex
= startPageIndex
+ i
;
884 swap_addr_t slotIndex
= startSlotIndex
+ i
;
886 swap_hash_key key
= { this, pageIndex
};
888 swap_block
* swap
= sSwapHashTable
.Lookup(key
);
889 while (swap
== NULL
) {
890 swap
= (swap_block
*)object_cache_alloc(sSwapBlockCache
,
891 CACHE_DONT_WAIT_FOR_MEMORY
| CACHE_DONT_LOCK_KERNEL_SPACE
);
893 // Wait a short time until memory is available again.
897 swap
= sSwapHashTable
.Lookup(key
);
901 swap
->key
.cache
= this;
902 swap
->key
.page_index
= pageIndex
& ~(off_t
)SWAP_BLOCK_MASK
;
904 for (uint32 i
= 0; i
< SWAP_BLOCK_PAGES
; i
++)
905 swap
->swap_slots
[i
] = SWAP_SLOT_NONE
;
907 sSwapHashTable
.InsertUnchecked(swap
);
910 swap_addr_t blockIndex
= pageIndex
& SWAP_BLOCK_MASK
;
911 for (j
= 0; blockIndex
< SWAP_BLOCK_PAGES
&& left
> 0; j
++) {
912 swap
->swap_slots
[blockIndex
++] = slotIndex
+ j
;
922 VMAnonymousCache::_SwapBlockFree(off_t startPageIndex
, uint32 count
)
924 WriteLocker
locker(sSwapHashLock
);
927 for (uint32 i
= 0, j
= 0; i
< count
; i
+= j
) {
928 off_t pageIndex
= startPageIndex
+ i
;
929 swap_hash_key key
= { this, pageIndex
};
930 swap_block
* swap
= sSwapHashTable
.Lookup(key
);
932 ASSERT(swap
!= NULL
);
934 swap_addr_t blockIndex
= pageIndex
& SWAP_BLOCK_MASK
;
935 for (j
= 0; blockIndex
< SWAP_BLOCK_PAGES
&& left
> 0; j
++) {
936 swap
->swap_slots
[blockIndex
++] = SWAP_SLOT_NONE
;
941 if (swap
->used
== 0) {
942 sSwapHashTable
.RemoveUnchecked(swap
);
943 object_cache_free(sSwapBlockCache
, swap
,
944 CACHE_DONT_WAIT_FOR_MEMORY
| CACHE_DONT_LOCK_KERNEL_SPACE
);
951 VMAnonymousCache::_SwapBlockGetAddress(off_t pageIndex
)
953 ReadLocker
locker(sSwapHashLock
);
955 swap_hash_key key
= { this, pageIndex
};
956 swap_block
* swap
= sSwapHashTable
.Lookup(key
);
957 swap_addr_t slotIndex
= SWAP_SLOT_NONE
;
960 swap_addr_t blockIndex
= pageIndex
& SWAP_BLOCK_MASK
;
961 slotIndex
= swap
->swap_slots
[blockIndex
];
969 VMAnonymousCache::_Commit(off_t size
, int priority
)
971 TRACE("%p->VMAnonymousCache::_Commit(%" B_PRIdOFF
"), already committed: "
972 "%" B_PRIdOFF
" (%" B_PRIdOFF
" swap)\n", this, size
, committed_size
,
975 // Basic strategy: reserve swap space first, only when running out of swap
976 // space, reserve real memory.
978 off_t committedMemory
= committed_size
- fCommittedSwapSize
;
980 // Regardless of whether we're asked to grow or shrink the commitment,
981 // we always try to reserve as much as possible of the final commitment
982 // in the swap space.
983 if (size
> fCommittedSwapSize
) {
984 fCommittedSwapSize
+= swap_space_reserve(size
- fCommittedSwapSize
);
985 committed_size
= fCommittedSwapSize
+ committedMemory
;
986 if (size
> fCommittedSwapSize
) {
987 TRACE("%p->VMAnonymousCache::_Commit(%" B_PRIdOFF
"), reserved "
988 "only %" B_PRIdOFF
" swap\n", this, size
, fCommittedSwapSize
);
992 if (committed_size
== size
)
995 if (committed_size
> size
) {
996 // The commitment shrinks -- unreserve real memory first.
997 off_t toUnreserve
= committed_size
- size
;
998 if (committedMemory
> 0) {
999 off_t unreserved
= min_c(toUnreserve
, committedMemory
);
1000 vm_unreserve_memory(unreserved
);
1001 committedMemory
-= unreserved
;
1002 committed_size
-= unreserved
;
1003 toUnreserve
-= unreserved
;
1006 // Unreserve swap space.
1007 if (toUnreserve
> 0) {
1008 swap_space_unreserve(toUnreserve
);
1009 fCommittedSwapSize
-= toUnreserve
;
1010 committed_size
-= toUnreserve
;
1016 // The commitment grows -- we have already tried to reserve swap space at
1017 // the start of the method, so we try to reserve real memory, now.
1019 off_t toReserve
= size
- committed_size
;
1020 if (vm_try_reserve_memory(toReserve
, priority
, 1000000) != B_OK
) {
1021 dprintf("%p->VMAnonymousCache::_Commit(%" B_PRIdOFF
"): Failed to "
1022 "reserve %" B_PRIdOFF
" bytes of RAM\n", this, size
, toReserve
);
1026 committed_size
= size
;
1032 VMAnonymousCache::_MergePagesSmallerSource(VMAnonymousCache
* source
)
1034 // The source cache has less pages than the consumer (this cache), so we
1035 // iterate through the source's pages and move the ones that are not
1036 // shadowed up to the consumer.
1038 for (VMCachePagesTree::Iterator it
= source
->pages
.GetIterator();
1039 vm_page
* page
= it
.Next();) {
1040 // Note: Removing the current node while iterating through a
1041 // IteratableSplayTree is safe.
1042 vm_page
* consumerPage
= LookupPage(
1043 (off_t
)page
->cache_offset
<< PAGE_SHIFT
);
1044 if (consumerPage
== NULL
) {
1045 // the page is not yet in the consumer cache - move it upwards
1046 ASSERT_PRINT(!page
->busy
, "page: %p", page
);
1054 VMAnonymousCache::_MergePagesSmallerConsumer(VMAnonymousCache
* source
)
1056 // The consumer (this cache) has less pages than the source, so we move the
1057 // consumer's pages to the source (freeing shadowed ones) and finally just
1058 // all pages of the source back to the consumer.
1060 for (VMCachePagesTree::Iterator it
= pages
.GetIterator();
1061 vm_page
* page
= it
.Next();) {
1062 // If a source page is in the way, remove and free it.
1063 vm_page
* sourcePage
= source
->LookupPage(
1064 (off_t
)page
->cache_offset
<< PAGE_SHIFT
);
1065 if (sourcePage
!= NULL
) {
1066 DEBUG_PAGE_ACCESS_START(sourcePage
);
1067 ASSERT_PRINT(!sourcePage
->busy
, "page: %p", sourcePage
);
1068 ASSERT_PRINT(sourcePage
->WiredCount() == 0
1069 && sourcePage
->mappings
.IsEmpty(),
1070 "sourcePage: %p, page: %p", sourcePage
, page
);
1071 source
->RemovePage(sourcePage
);
1072 vm_page_free(source
, sourcePage
);
1075 // Note: Removing the current node while iterating through a
1076 // IteratableSplayTree is safe.
1077 source
->MovePage(page
);
1080 MoveAllPages(source
);
1085 VMAnonymousCache::_MergeSwapPages(VMAnonymousCache
* source
)
1087 // If neither source nor consumer have swap pages, we don't have to do
1089 if (source
->fAllocatedSwapSize
== 0 && fAllocatedSwapSize
== 0)
1092 for (off_t offset
= source
->virtual_base
1093 & ~(off_t
)(B_PAGE_SIZE
* SWAP_BLOCK_PAGES
- 1);
1094 offset
< source
->virtual_end
;
1095 offset
+= B_PAGE_SIZE
* SWAP_BLOCK_PAGES
) {
1097 WriteLocker
locker(sSwapHashLock
);
1099 off_t swapBlockPageIndex
= offset
>> PAGE_SHIFT
;
1100 swap_hash_key key
= { source
, swapBlockPageIndex
};
1101 swap_block
* sourceSwapBlock
= sSwapHashTable
.Lookup(key
);
1103 // remove the source swap block -- we will either take over the swap
1104 // space (and the block) or free it
1105 if (sourceSwapBlock
!= NULL
)
1106 sSwapHashTable
.RemoveUnchecked(sourceSwapBlock
);
1109 swap_block
* swapBlock
= sSwapHashTable
.Lookup(key
);
1113 // remove all source pages that are shadowed by consumer swap pages
1114 if (swapBlock
!= NULL
) {
1115 for (uint32 i
= 0; i
< SWAP_BLOCK_PAGES
; i
++) {
1116 if (swapBlock
->swap_slots
[i
] != SWAP_SLOT_NONE
) {
1117 vm_page
* page
= source
->LookupPage(
1118 (off_t
)(swapBlockPageIndex
+ i
) << PAGE_SHIFT
);
1120 DEBUG_PAGE_ACCESS_START(page
);
1121 ASSERT_PRINT(!page
->busy
, "page: %p", page
);
1122 source
->RemovePage(page
);
1123 vm_page_free(source
, page
);
1129 if (sourceSwapBlock
== NULL
)
1132 for (uint32 i
= 0; i
< SWAP_BLOCK_PAGES
; i
++) {
1133 off_t pageIndex
= swapBlockPageIndex
+ i
;
1134 swap_addr_t sourceSlotIndex
= sourceSwapBlock
->swap_slots
[i
];
1136 if (sourceSlotIndex
== SWAP_SLOT_NONE
)
1139 if ((swapBlock
!= NULL
1140 && swapBlock
->swap_slots
[i
] != SWAP_SLOT_NONE
)
1141 || LookupPage((off_t
)pageIndex
<< PAGE_SHIFT
) != NULL
) {
1142 // The consumer already has a page or a swapped out page
1143 // at this index. So we can free the source swap space.
1144 swap_slot_dealloc(sourceSlotIndex
, 1);
1145 sourceSwapBlock
->swap_slots
[i
] = SWAP_SLOT_NONE
;
1146 sourceSwapBlock
->used
--;
1149 // We've either freed the source swap page or are going to move it
1150 // to the consumer. At any rate, the source cache doesn't own it
1152 source
->fAllocatedSwapSize
-= B_PAGE_SIZE
;
1155 // All source swap pages that have not been freed yet are taken over by
1157 fAllocatedSwapSize
+= B_PAGE_SIZE
* (off_t
)sourceSwapBlock
->used
;
1159 if (sourceSwapBlock
->used
== 0) {
1160 // All swap pages have been freed -- we can discard the source swap
1162 object_cache_free(sSwapBlockCache
, sourceSwapBlock
,
1163 CACHE_DONT_WAIT_FOR_MEMORY
| CACHE_DONT_LOCK_KERNEL_SPACE
);
1164 } else if (swapBlock
== NULL
) {
1165 // We need to take over some of the source's swap pages and there's
1166 // no swap block in the consumer cache. Just take over the source
1168 sourceSwapBlock
->key
.cache
= this;
1170 sSwapHashTable
.InsertUnchecked(sourceSwapBlock
);
1173 // We need to take over some of the source's swap pages and there's
1174 // already a swap block in the consumer cache. Copy the respective
1175 // swap addresses and discard the source swap block.
1176 for (uint32 i
= 0; i
< SWAP_BLOCK_PAGES
; i
++) {
1177 if (sourceSwapBlock
->swap_slots
[i
] != SWAP_SLOT_NONE
)
1178 swapBlock
->swap_slots
[i
] = sourceSwapBlock
->swap_slots
[i
];
1181 object_cache_free(sSwapBlockCache
, sourceSwapBlock
,
1182 CACHE_DONT_WAIT_FOR_MEMORY
| CACHE_DONT_LOCK_KERNEL_SPACE
);
1191 // TODO: This can be removed if we get BFS uuid's
1193 char name
[B_FILE_NAME_LENGTH
];
1194 char device
[B_FILE_NAME_LENGTH
];
1195 char filesystem
[B_OS_NAME_LENGTH
];
1200 class PartitionScorer
: public KPartitionVisitor
{
1202 PartitionScorer(VolumeInfo
& volumeInfo
)
1204 fBestPartition(NULL
),
1206 fVolumeInfo(volumeInfo
)
1210 virtual bool VisitPre(KPartition
* partition
)
1212 if (!partition
->ContainsFileSystem())
1216 partition
->GetPath(&path
);
1219 if (strcmp(fVolumeInfo
.name
, partition
->ContentName()) == 0)
1221 if (strcmp(fVolumeInfo
.device
, path
.Path()) == 0)
1223 if (fVolumeInfo
.capacity
== partition
->Size())
1225 if (strcmp(fVolumeInfo
.filesystem
,
1226 partition
->DiskSystem()->ShortName()) == 0) {
1229 if (score
>= 4 && score
> fBestScore
) {
1230 fBestPartition
= partition
;
1237 KPartition
* fBestPartition
;
1241 VolumeInfo fVolumeInfo
;
1246 get_mount_point(KPartition
* partition
, KPath
* mountPoint
)
1248 if (!mountPoint
|| !partition
->ContainsFileSystem())
1251 const char* volumeName
= partition
->ContentName();
1252 if (!volumeName
|| strlen(volumeName
) == 0)
1253 volumeName
= partition
->Name();
1254 if (!volumeName
|| strlen(volumeName
) == 0)
1255 volumeName
= "unnamed volume";
1257 char basePath
[B_PATH_NAME_LENGTH
];
1258 int32 len
= snprintf(basePath
, sizeof(basePath
), "/%s", volumeName
);
1259 for (int32 i
= 1; i
< len
; i
++)
1260 if (basePath
[i
] == '/')
1262 char* path
= mountPoint
->LockBuffer();
1263 int32 pathLen
= mountPoint
->BufferSize();
1264 strncpy(path
, basePath
, pathLen
);
1267 for (int i
= 1; ; i
++) {
1268 if (stat(path
, &dummy
) != 0)
1270 snprintf(path
, pathLen
, "%s%d", basePath
, i
);
1273 mountPoint
->UnlockBuffer();
1279 swap_file_add(const char* path
)
1282 int fd
= open(path
, O_RDWR
| O_NOCACHE
, S_IRUSR
| S_IWUSR
);
1286 // fstat() it and check whether we can use it
1288 if (fstat(fd
, &st
) < 0) {
1293 if (!(S_ISREG(st
.st_mode
) || S_ISCHR(st
.st_mode
) || S_ISBLK(st
.st_mode
))) {
1298 if (st
.st_size
< B_PAGE_SIZE
) {
1303 // get file descriptor, vnode, and cookie
1304 file_descriptor
* descriptor
= get_fd(get_current_io_context(true), fd
);
1307 vnode
* node
= fd_vnode(descriptor
);
1313 // do the allocations and prepare the swap_file structure
1314 swap_file
* swap
= (swap_file
*)malloc(sizeof(swap_file
));
1322 swap
->cookie
= descriptor
->cookie
;
1324 uint32 pageCount
= st
.st_size
>> PAGE_SHIFT
;
1325 swap
->bmp
= radix_bitmap_create(pageCount
);
1326 if (swap
->bmp
== NULL
) {
1332 // set slot index and add this file to swap file list
1333 mutex_lock(&sSwapFileListLock
);
1334 // TODO: Also check whether the swap file is already registered!
1335 if (sSwapFileList
.IsEmpty()) {
1336 swap
->first_slot
= 0;
1337 swap
->last_slot
= pageCount
;
1339 // leave one page gap between two swap files
1340 swap
->first_slot
= sSwapFileList
.Last()->last_slot
+ 1;
1341 swap
->last_slot
= swap
->first_slot
+ pageCount
;
1343 sSwapFileList
.Add(swap
);
1345 mutex_unlock(&sSwapFileListLock
);
1347 mutex_lock(&sAvailSwapSpaceLock
);
1348 sAvailSwapSpace
+= (off_t
)pageCount
* B_PAGE_SIZE
;
1349 mutex_unlock(&sAvailSwapSpaceLock
);
1356 swap_file_delete(const char* path
)
1359 status_t status
= vfs_get_vnode_from_path(path
, true, &node
);
1363 MutexLocker
locker(sSwapFileListLock
);
1365 swap_file
* swapFile
= NULL
;
1366 for (SwapFileList::Iterator it
= sSwapFileList
.GetIterator();
1367 (swapFile
= it
.Next()) != NULL
;) {
1368 if (swapFile
->vnode
== node
)
1372 vfs_put_vnode(node
);
1374 if (swapFile
== NULL
)
1377 // if this file is currently used, we can't delete
1378 // TODO: mark this swap file deleting, and remove it after releasing
1379 // all the swap space
1380 if (swapFile
->bmp
->free_slots
< swapFile
->last_slot
- swapFile
->first_slot
)
1383 sSwapFileList
.Remove(swapFile
);
1387 mutex_lock(&sAvailSwapSpaceLock
);
1388 sAvailSwapSpace
-= (off_t
)(swapFile
->last_slot
- swapFile
->first_slot
)
1390 mutex_unlock(&sAvailSwapSpaceLock
);
1392 close(swapFile
->fd
);
1393 radix_bitmap_destroy(swapFile
->bmp
);
1403 // create swap block cache
1404 sSwapBlockCache
= create_object_cache("swapblock", sizeof(swap_block
),
1405 sizeof(void*), NULL
, NULL
, NULL
);
1406 if (sSwapBlockCache
== NULL
)
1407 panic("swap_init(): can't create object cache for swap blocks\n");
1409 status_t error
= object_cache_set_minimum_reserve(sSwapBlockCache
,
1410 MIN_SWAP_BLOCK_RESERVE
);
1411 if (error
!= B_OK
) {
1412 panic("swap_init(): object_cache_set_minimum_reserve() failed: %s",
1416 // init swap hash table
1417 sSwapHashTable
.Init(INITIAL_SWAP_HASH_SIZE
);
1418 rw_lock_init(&sSwapHashLock
, "swaphash");
1420 error
= register_resource_resizer(swap_hash_resizer
, NULL
,
1421 SWAP_HASH_RESIZE_INTERVAL
);
1422 if (error
!= B_OK
) {
1423 panic("swap_init(): Failed to register swap hash resizer: %s",
1427 // init swap file list
1428 mutex_init(&sSwapFileListLock
, "swaplist");
1429 sSwapFileAlloc
= NULL
;
1432 // init available swap space
1433 mutex_init(&sAvailSwapSpaceLock
, "avail swap space");
1434 sAvailSwapSpace
= 0;
1436 add_debugger_command_etc("swap", &dump_swap_info
,
1437 "Print infos about the swap usage",
1439 "Print infos about the swap usage.\n", 0);
1444 swap_init_post_modules()
1446 // Never try to create a swap file on a read-only device - when booting
1447 // from CD, the write overlay is used.
1448 if (gReadOnlyBootDevice
)
1451 bool swapEnabled
= true;
1452 bool swapAutomatic
= true;
1455 dev_t swapDeviceID
= -1;
1456 VolumeInfo selectedVolume
= {};
1458 void* settings
= load_driver_settings("virtual_memory");
1460 if (settings
!= NULL
) {
1461 // We pass a lot of information on the swap device, this is mostly to
1462 // ensure that we are dealing with the same device that was configured.
1464 // TODO: Some kind of BFS uuid would be great here :)
1465 const char* enabled
= get_driver_parameter(settings
, "vm", NULL
, NULL
);
1467 if (enabled
!= NULL
) {
1468 swapEnabled
= get_driver_boolean_parameter(settings
, "vm",
1470 swapAutomatic
= get_driver_boolean_parameter(settings
, "swap_auto",
1473 if (swapEnabled
&& !swapAutomatic
) {
1474 const char* size
= get_driver_parameter(settings
, "swap_size",
1476 const char* volume
= get_driver_parameter(settings
,
1477 "swap_volume_name", NULL
, NULL
);
1478 const char* device
= get_driver_parameter(settings
,
1479 "swap_volume_device", NULL
, NULL
);
1480 const char* filesystem
= get_driver_parameter(settings
,
1481 "swap_volume_filesystem", NULL
, NULL
);
1482 const char* capacity
= get_driver_parameter(settings
,
1483 "swap_volume_capacity", NULL
, NULL
);
1485 if (size
!= NULL
&& device
!= NULL
&& volume
!= NULL
1486 && filesystem
!= NULL
&& capacity
!= NULL
) {
1487 // User specified a size / volume that seems valid
1488 swapAutomatic
= false;
1489 swapSize
= atoll(size
);
1490 strlcpy(selectedVolume
.name
, volume
,
1491 sizeof(selectedVolume
.name
));
1492 strlcpy(selectedVolume
.device
, device
,
1493 sizeof(selectedVolume
.device
));
1494 strlcpy(selectedVolume
.filesystem
, filesystem
,
1495 sizeof(selectedVolume
.filesystem
));
1496 selectedVolume
.capacity
= atoll(capacity
);
1498 // Something isn't right with swap config, go auto
1499 swapAutomatic
= true;
1500 dprintf("%s: virtual_memory configuration is invalid, "
1501 "using automatic swap\n", __func__
);
1505 unload_driver_settings(settings
);
1508 if (swapAutomatic
) {
1509 swapSize
= (off_t
)vm_page_num_pages() * B_PAGE_SIZE
;
1510 if (swapSize
<= (1024 * 1024 * 1024)) {
1511 // Memory under 1GB? double the swap
1514 // Automatic swap defaults to the boot device
1515 swapDeviceID
= gBootDevice
;
1518 if (!swapEnabled
|| swapSize
< B_PAGE_SIZE
) {
1519 dprintf("%s: virtual_memory is disabled\n", __func__
);
1523 if (!swapAutomatic
&& swapDeviceID
< 0) {
1524 // If user-specified swap, and no swap device has been chosen yet...
1525 KDiskDeviceManager::CreateDefault();
1526 KDiskDeviceManager
* manager
= KDiskDeviceManager::Default();
1527 PartitionScorer
visitor(selectedVolume
);
1529 KDiskDevice
* device
;
1531 while ((device
= manager
->NextDevice(&cookie
)) != NULL
) {
1532 if (device
->IsReadOnlyMedia() || device
->IsWriteOnce()
1533 || device
->IsRemovable()) {
1536 device
->VisitEachDescendant(&visitor
);
1539 if (!visitor
.fBestPartition
) {
1540 dprintf("%s: Can't find configured swap partition '%s'\n",
1541 __func__
, selectedVolume
.name
);
1543 if (visitor
.fBestPartition
->IsMounted())
1544 swapDeviceID
= visitor
.fBestPartition
->VolumeID();
1546 KPath devPath
, mountPoint
;
1547 visitor
.fBestPartition
->GetPath(&devPath
);
1548 get_mount_point(visitor
.fBestPartition
, &mountPoint
);
1549 const char* mountPath
= mountPoint
.Path();
1550 mkdir(mountPath
, S_IRWXU
| S_IRWXG
| S_IRWXO
);
1551 swapDeviceID
= _kern_mount(mountPath
, devPath
.Path(),
1553 if (swapDeviceID
< 0) {
1554 dprintf("%s: Can't mount configured swap partition '%s'\n",
1555 __func__
, selectedVolume
.name
);
1561 if (swapDeviceID
< 0)
1562 swapDeviceID
= gBootDevice
;
1564 // We now have a swapDeviceID which is used for the swap file
1567 struct fs_info info
;
1568 _kern_read_fs_info(swapDeviceID
, &info
);
1569 if (swapDeviceID
== gBootDevice
)
1570 path
= kDefaultSwapPath
;
1572 vfs_entry_ref_to_path(info
.dev
, info
.root
, ".", true, path
.LockBuffer(),
1574 path
.UnlockBuffer();
1575 path
.Append("swap");
1578 const char* swapPath
= path
.Path();
1580 // Swap size limits prevent oversized swap files
1581 if (swapAutomatic
) {
1582 off_t existingSwapSize
= 0;
1583 struct stat existingSwapStat
;
1584 if (stat(swapPath
, &existingSwapStat
) == 0)
1585 existingSwapSize
= existingSwapStat
.st_size
;
1587 off_t freeSpace
= info
.free_blocks
* info
.block_size
+ existingSwapSize
;
1589 // Adjust automatic swap to a maximum of 25% of the free space
1590 if (swapSize
> (freeSpace
/ 4))
1591 swapSize
= (freeSpace
/ 4);
1595 int fd
= open(swapPath
, O_RDWR
| O_CREAT
| O_NOCACHE
, S_IRUSR
| S_IWUSR
);
1597 dprintf("%s: Can't open/create %s: %s\n", __func__
,
1598 swapPath
, strerror(errno
));
1603 stat
.st_size
= swapSize
;
1604 status_t error
= _kern_write_stat(fd
, NULL
, false, &stat
,
1605 sizeof(struct stat
), B_STAT_SIZE
| B_STAT_SIZE_INSECURE
);
1606 if (error
!= B_OK
) {
1607 dprintf("%s: Failed to resize %s to %" B_PRIdOFF
" bytes: %s\n",
1608 __func__
, swapPath
, swapSize
, strerror(error
));
1613 error
= swap_file_add(swapPath
);
1614 if (error
!= B_OK
) {
1615 dprintf("%s: Failed to add swap file %s: %s\n", __func__
, swapPath
,
1621 //! Used by page daemon to free swap space.
1623 swap_free_page_swap_space(vm_page
* page
)
1625 VMAnonymousCache
* cache
= dynamic_cast<VMAnonymousCache
*>(page
->Cache());
1629 swap_addr_t slotIndex
= cache
->_SwapBlockGetAddress(page
->cache_offset
);
1630 if (slotIndex
== SWAP_SLOT_NONE
)
1633 swap_slot_dealloc(slotIndex
, 1);
1634 cache
->fAllocatedSwapSize
-= B_PAGE_SIZE
;
1635 cache
->_SwapBlockFree(page
->cache_offset
, 1);
1642 swap_available_pages()
1644 mutex_lock(&sAvailSwapSpaceLock
);
1645 uint32 avail
= sAvailSwapSpace
>> PAGE_SHIFT
;
1646 mutex_unlock(&sAvailSwapSpaceLock
);
1653 swap_total_swap_pages()
1655 mutex_lock(&sSwapFileListLock
);
1657 uint32 totalSwapSlots
= 0;
1658 for (SwapFileList::Iterator it
= sSwapFileList
.GetIterator();
1659 swap_file
* swapFile
= it
.Next();) {
1660 totalSwapSlots
+= swapFile
->last_slot
- swapFile
->first_slot
;
1663 mutex_unlock(&sSwapFileListLock
);
1665 return totalSwapSlots
;
1669 #endif // ENABLE_SWAP_SUPPORT
1673 swap_get_info(system_info
* info
)
1675 #if ENABLE_SWAP_SUPPORT
1676 info
->max_swap_pages
= swap_total_swap_pages();
1677 info
->free_swap_pages
= swap_available_pages();
1679 info
->max_swap_space
= 0;
1680 info
->free_swap_space
= 0;