kernel/vm: Move committed_size to the cache implementations.

This fixes a long-standing TODO, and a major inconsistency
that for different caches, "commitment" reserves from different
pools, so any two aren't necessarily convertible.

The new system introduces a virtual method "TakeCommitmentFrom",
which allows the VM's cut_area routine to behave as it did
previously.
This commit is contained in:
Augustin Cavalier
2026-03-26 17:36:27 -04:00
parent 0a5cd96093
commit 33dc47cb2c
7 changed files with 107 additions and 45 deletions
+2 -3
View File
@@ -147,9 +147,10 @@ public:
status_t SetMinimalCommitment(off_t commitment, status_t SetMinimalCommitment(off_t commitment,
int priority); int priority);
off_t Commitment() const; virtual off_t Commitment() const;
virtual bool CanOvercommit(); virtual bool CanOvercommit();
virtual status_t Commit(off_t size, int priority); virtual status_t Commit(off_t size, int priority);
virtual void TakeCommitmentFrom(VMCache* from, off_t commitment);
// backing store operations // backing store operations
virtual bool StoreHasPage(off_t offset); virtual bool StoreHasPage(off_t offset);
@@ -206,8 +207,6 @@ public:
VMCache* source; VMCache* source;
off_t virtual_base; off_t virtual_base;
off_t virtual_end; off_t virtual_end;
off_t committed_size;
// TODO: Remove!
uint32 page_count; uint32 page_count;
uint32 temporary : 1; uint32 temporary : 1;
uint32 type : 6; uint32 type : 6;
+41 -20
View File
@@ -452,7 +452,7 @@ VMAnonymousCache::~VMAnonymousCache()
_FreeSwapPageRange(virtual_base, virtual_end, false); _FreeSwapPageRange(virtual_base, virtual_end, false);
swap_space_unreserve(fReservedSwapSize); swap_space_unreserve(fReservedSwapSize);
vm_unreserve_memory_or_swap(committed_size); vm_unreserve_memory_or_swap(fCommittedSize);
} }
@@ -469,6 +469,7 @@ VMAnonymousCache::Init(bool canOvercommit, int32 numPrecommittedPages,
if (error != B_OK) if (error != B_OK)
return error; return error;
fCommittedSize = 0;
fCanOvercommit = canOvercommit; fCanOvercommit = canOvercommit;
fHasPrecommitted = false; fHasPrecommitted = false;
fPrecommittedPages = min_c(numPrecommittedPages, 255); fPrecommittedPages = min_c(numPrecommittedPages, 255);
@@ -612,7 +613,7 @@ VMAnonymousCache::Discard(off_t offset, off_t size)
_FreeSwapPageRange(offset, offset + size); _FreeSwapPageRange(offset, offset + size);
const ssize_t discarded = VMCache::Discard(offset, size); const ssize_t discarded = VMCache::Discard(offset, size);
if (discarded > 0 && fCanOvercommit) if (discarded > 0 && fCanOvercommit)
Commit(committed_size - discarded, VM_PRIORITY_USER); Commit(fCommittedSize - discarded, VM_PRIORITY_USER);
return discarded; return discarded;
} }
@@ -724,14 +725,28 @@ VMAnonymousCache::Adopt(VMCache* _source, off_t offset, off_t size,
// We need to adopt the commitment for these pages. // We need to adopt the commitment for these pages.
uint32 newPages = page_count - initialPageCount; uint32 newPages = page_count - initialPageCount;
off_t pagesCommitment = newPages * B_PAGE_SIZE; off_t pagesCommitment = newPages * B_PAGE_SIZE;
source->committed_size -= pagesCommitment; source->fCommittedSize -= pagesCommitment;
committed_size += pagesCommitment; fCommittedSize += pagesCommitment;
} }
return status; return status;
} }
off_t
VMAnonymousCache::Commitment() const
{
return fCommittedSize;
}
bool
VMAnonymousCache::CanOvercommit()
{
return fCanOvercommit;
}
status_t status_t
VMAnonymousCache::Commit(off_t size, int priority) VMAnonymousCache::Commit(off_t size, int priority)
{ {
@@ -743,7 +758,7 @@ VMAnonymousCache::Commit(off_t size, int priority)
// If we can overcommit, we don't commit here, but in Fault(). We always // If we can overcommit, we don't commit here, but in Fault(). We always
// unreserve memory, if we're asked to shrink our commitment, though. // unreserve memory, if we're asked to shrink our commitment, though.
if (fCanOvercommit && size > committed_size) { if (fCanOvercommit && size > fCommittedSize) {
if (fHasPrecommitted) if (fHasPrecommitted)
return B_OK; return B_OK;
@@ -754,31 +769,37 @@ VMAnonymousCache::Commit(off_t size, int priority)
size = precommitted; size = precommitted;
// pre-commit should not shrink existing commitment // pre-commit should not shrink existing commitment
size += committed_size; size += fCommittedSize;
} }
// Check to see how much we could commit - we need real memory // Check to see how much we could commit - we need real memory
if (size > committed_size) { if (size > fCommittedSize) {
// try to commit // try to commit
if (vm_try_reserve_memory_or_swap(size - committed_size, priority, 1000000) if (vm_try_reserve_memory_or_swap(size - fCommittedSize, priority, 1000000)
!= B_OK) { != B_OK) {
return B_NO_MEMORY; return B_NO_MEMORY;
} }
} else { } else {
// we can release some // we can release some
vm_unreserve_memory_or_swap(committed_size - size); vm_unreserve_memory_or_swap(fCommittedSize - size);
} }
committed_size = size; fCommittedSize = size;
return B_OK; return B_OK;
} }
bool void
VMAnonymousCache::CanOvercommit() VMAnonymousCache::TakeCommitmentFrom(VMCache* _from, off_t commitment)
{ {
return fCanOvercommit; VMAnonymousCache* from = dynamic_cast<VMAnonymousCache*>(_from);
ASSERT(from != NULL && from->fCommittedSize >= commitment);
AssertLocked();
from->AssertLocked();
from->fCommittedSize -= commitment;
fCommittedSize += commitment;
} }
@@ -1046,7 +1067,7 @@ VMAnonymousCache::Fault(struct VMAddressSpace* aspace, off_t offset)
if (fCanOvercommit && LookupPage(offset) == NULL && !StoreHasPage(offset)) { if (fCanOvercommit && LookupPage(offset) == NULL && !StoreHasPage(offset)) {
if (fPrecommittedPages == 0) { if (fPrecommittedPages == 0) {
// never commit more than needed // never commit more than needed
if (committed_size / B_PAGE_SIZE > page_count) if (fCommittedSize / B_PAGE_SIZE > page_count)
return B_BAD_HANDLER; return B_BAD_HANDLER;
// try to commit additional memory // try to commit additional memory
@@ -1058,7 +1079,7 @@ VMAnonymousCache::Fault(struct VMAddressSpace* aspace, off_t offset)
return B_NO_MEMORY; return B_NO_MEMORY;
} }
committed_size += B_PAGE_SIZE; fCommittedSize += B_PAGE_SIZE;
} else } else
fPrecommittedPages--; fPrecommittedPages--;
} }
@@ -1079,13 +1100,13 @@ VMAnonymousCache::Merge(VMCache* _source)
} }
// take over the source's committed size // take over the source's committed size
committed_size += source->committed_size; fCommittedSize += source->fCommittedSize;
source->committed_size = 0; source->fCommittedSize = 0;
off_t actualSize = PAGE_ALIGN(virtual_end - virtual_base); off_t actualSize = PAGE_ALIGN(virtual_end - virtual_base);
if (committed_size > actualSize) { if (fCommittedSize > actualSize) {
vm_unreserve_memory_or_swap(committed_size - actualSize); vm_unreserve_memory_or_swap(fCommittedSize - actualSize);
committed_size = actualSize; fCommittedSize = actualSize;
} }
// Move all not shadowed swap pages from the source to the consumer cache. // Move all not shadowed swap pages from the source to the consumer cache.
+4
View File
@@ -48,8 +48,11 @@ public:
virtual ssize_t Discard(off_t offset, off_t size); virtual ssize_t Discard(off_t offset, off_t size);
virtual off_t Commitment() const;
virtual bool CanOvercommit(); virtual bool CanOvercommit();
virtual status_t Commit(off_t size, int priority); virtual status_t Commit(off_t size, int priority);
virtual void TakeCommitmentFrom(VMCache* from, off_t commitment);
virtual bool StoreHasPage(off_t offset); virtual bool StoreHasPage(off_t offset);
virtual bool DebugStoreHasPage(off_t offset); virtual bool DebugStoreHasPage(off_t offset);
@@ -100,6 +103,7 @@ private:
private: private:
friend bool swap_free_page_swap_space(vm_page* page); friend bool swap_free_page_swap_space(vm_page* page);
off_t fCommittedSize;
bool fCanOvercommit; bool fCanOvercommit;
bool fHasPrecommitted; bool fHasPrecommitted;
uint8 fPrecommittedPages; uint8 fPrecommittedPages;
@@ -49,6 +49,7 @@ VMAnonymousNoSwapCache::Init(bool canOvercommit, int32 numPrecommittedPages,
if (error != B_OK) if (error != B_OK)
return error; return error;
committed_size = 0;
fCanOvercommit = canOvercommit; fCanOvercommit = canOvercommit;
fHasPrecommitted = false; fHasPrecommitted = false;
fPrecommittedPages = min_c(numPrecommittedPages, 255); fPrecommittedPages = min_c(numPrecommittedPages, 255);
@@ -59,14 +60,19 @@ VMAnonymousNoSwapCache::Init(bool canOvercommit, int32 numPrecommittedPages,
status_t status_t
VMAnonymousNoSwapCache::Adopt(VMCache* from, off_t offset, off_t size, VMAnonymousNoSwapCache::Adopt(VMCache* _from, off_t offset, off_t size,
off_t newOffset) off_t newOffset)
{ {
VMAnonymousNoSwapCache* from = dynamic_cast<VMAnonymousNoSwapCache*>(_from);
ASSERT(from != NULL);
uint32 initialPageCount = page_count; uint32 initialPageCount = page_count;
status_t status = VMCache::Adopt(from, offset, size, newOffset); status_t status = VMCache::Adopt(from, offset, size, newOffset);
if (fCanOvercommit) { if (fCanOvercommit) {
// We need to adopt the commitment for these pages. // We need to adopt the commitment for these pages.
ASSERT(from->fCanOvercommit);
uint32 newPages = page_count - initialPageCount; uint32 newPages = page_count - initialPageCount;
off_t pagesCommitment = newPages * B_PAGE_SIZE; off_t pagesCommitment = newPages * B_PAGE_SIZE;
from->committed_size -= pagesCommitment; from->committed_size -= pagesCommitment;
@@ -87,6 +93,20 @@ VMAnonymousNoSwapCache::Discard(off_t offset, off_t size)
} }
off_t
VMAnonymousNoSwapCache::Commitment() const
{
return committed_size;
}
bool
VMAnonymousNoSwapCache::CanOvercommit()
{
return fCanOvercommit;
}
status_t status_t
VMAnonymousNoSwapCache::Commit(off_t size, int priority) VMAnonymousNoSwapCache::Commit(off_t size, int priority)
{ {
@@ -127,10 +147,16 @@ VMAnonymousNoSwapCache::Commit(off_t size, int priority)
} }
bool void
VMAnonymousNoSwapCache::CanOvercommit() VMAnonymousNoSwapCache::TakeCommitmentFrom(VMCache* _from, off_t commitment)
{ {
return fCanOvercommit; VMAnonymousNoSwapCache* from = dynamic_cast<VMAnonymousNoSwapCache*>(_from);
ASSERT(from != NULL && from->committed_size >= commitment);
AssertLocked();
from->AssertLocked();
from->committed_size -= commitment;
committed_size += commitment;
} }
@@ -26,8 +26,11 @@ public:
off_t newOffset); off_t newOffset);
virtual ssize_t Discard(off_t offset, off_t size); virtual ssize_t Discard(off_t offset, off_t size);
virtual off_t Commitment() const;
virtual bool CanOvercommit(); virtual bool CanOvercommit();
virtual status_t Commit(off_t size, int priority); virtual status_t Commit(off_t size, int priority);
virtual void TakeCommitmentFrom(VMCache* from, off_t commitment);
virtual bool StoreHasPage(off_t offset); virtual bool StoreHasPage(off_t offset);
virtual int32 GuardSize() { return fGuardedSize; } virtual int32 GuardSize() { return fGuardedSize; }
@@ -49,6 +52,9 @@ public:
protected: protected:
virtual void DeleteObject(); virtual void DeleteObject();
public:
off_t committed_size;
private: private:
bool fCanOvercommit; bool fCanOvercommit;
bool fHasPrecommitted; bool fHasPrecommitted;
+8 -2
View File
@@ -638,7 +638,6 @@ VMCache::Init(const char* name, uint32 cacheType, uint32 allocationFlags)
source = NULL; source = NULL;
virtual_base = 0; virtual_base = 0;
virtual_end = 0; virtual_end = 0;
committed_size = 0;
temporary = 0; temporary = 0;
page_count = 0; page_count = 0;
fWiredPagesCount = 0; fWiredPagesCount = 0;
@@ -1310,7 +1309,7 @@ VMCache::FlushAndRemoveAllPages()
off_t off_t
VMCache::Commitment() const VMCache::Commitment() const
{ {
return committed_size; return 0;
} }
@@ -1329,6 +1328,13 @@ VMCache::Commit(off_t size, int priority)
} }
void
VMCache::TakeCommitmentFrom(VMCache* from, off_t commitment)
{
ASSERT_UNREACHABLE();
}
/*! Returns whether the cache's underlying backing store could deliver the /*! Returns whether the cache's underlying backing store could deliver the
page at the given offset. page at the given offset.
+16 -16
View File
@@ -969,9 +969,8 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
if (!overcommitting && resizePriority != -1) { if (!overcommitting && resizePriority != -1) {
// Steal some of the original cache's commitment. // Steal some of the original cache's commitment.
const size_t steal = PAGE_ALIGN(secondSize); const size_t steal = PAGE_ALIGN(secondSize);
if (cache->committed_size > (off_t)steal) { if (cache->Commitment() > (off_t)steal) {
cache->committed_size -= steal; secondCache->TakeCommitmentFrom(cache, steal);
secondCache->committed_size += steal;
commitmentStolen = steal; commitmentStolen = steal;
} }
} }
@@ -1001,8 +1000,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
} }
if (error != B_OK) { if (error != B_OK) {
secondCache->committed_size -= commitmentStolen; cache->TakeCommitmentFrom(secondCache, commitmentStolen);
cache->committed_size += commitmentStolen;
// Move the pages back. // Move the pages back.
status_t readoptStatus = cache->Adopt(secondCache, status_t readoptStatus = cache->Adopt(secondCache,
@@ -1036,9 +1034,9 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
if (resizePriority == -1) { if (resizePriority == -1) {
// Adjust commitments. // Adjust commitments.
const off_t areaCommit = compute_area_page_commitment(area) * B_PAGE_SIZE; const off_t areaCommit = compute_area_page_commitment(area) * B_PAGE_SIZE;
if (areaCommit < area->cache->committed_size) { if (areaCommit < area->cache->Commitment()) {
secondArea->cache->committed_size += area->cache->committed_size - areaCommit; secondArea->cache->TakeCommitmentFrom(area->cache,
area->cache->committed_size = areaCommit; area->cache->Commitment() - areaCommit);
} }
area->cache->Commit(areaCommit, priority); area->cache->Commit(areaCommit, priority);
@@ -1768,9 +1766,12 @@ vm_create_anonymous_area(team_id team, const char *name, addr_t size,
cache->temporary = 1; cache->temporary = 1;
cache->virtual_end = size; cache->virtual_end = size;
cache->committed_size = reservedMemory;
// TODO: This should be done via a method. if (reservedMemory != 0) {
reservedMemory = 0; VMAnonymousNoSwapCache* noSwapCache = dynamic_cast<VMAnonymousNoSwapCache*>(cache);
noSwapCache->committed_size = reservedMemory;
reservedMemory = 0;
}
cache->Lock(); cache->Lock();
@@ -2864,16 +2865,15 @@ vm_copy_on_write_area(VMCache* lowerCache,
// Shrink the lower cache's commitment (if possible) and steal the remainder; // Shrink the lower cache's commitment (if possible) and steal the remainder;
// and increase the upper cache's commitment to the lower cache's old commitment. // and increase the upper cache's commitment to the lower cache's old commitment.
const off_t lowerOldCommitment = lowerCache->committed_size, const off_t lowerOldCommitment = lowerCache->Commitment(),
lowerNewCommitment = (lowerCache->page_count * B_PAGE_SIZE); lowerNewCommitment = (lowerCache->page_count * B_PAGE_SIZE);
if (lowerNewCommitment < lowerOldCommitment) { if (lowerNewCommitment < lowerOldCommitment) {
lowerCache->committed_size = lowerNewCommitment; upperCache->TakeCommitmentFrom(lowerCache,
upperCache->committed_size = lowerOldCommitment - lowerNewCommitment; lowerOldCommitment - lowerNewCommitment);
} }
status = upperCache->Commit(lowerOldCommitment, VM_PRIORITY_USER); status = upperCache->Commit(lowerOldCommitment, VM_PRIORITY_USER);
if (status != B_OK) { if (status != B_OK) {
lowerCache->committed_size += upperCache->committed_size; lowerCache->TakeCommitmentFrom(upperCache, upperCache->Commitment());
upperCache->committed_size = 0;
upperCache->ReleaseRefAndUnlock(); upperCache->ReleaseRefAndUnlock();
return status; return status;
} }