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,
int priority);
off_t Commitment() const;
virtual off_t Commitment() const;
virtual bool CanOvercommit();
virtual status_t Commit(off_t size, int priority);
virtual void TakeCommitmentFrom(VMCache* from, off_t commitment);
// backing store operations
virtual bool StoreHasPage(off_t offset);
@@ -206,8 +207,6 @@ public:
VMCache* source;
off_t virtual_base;
off_t virtual_end;
off_t committed_size;
// TODO: Remove!
uint32 page_count;
uint32 temporary : 1;
uint32 type : 6;
+41 -20
View File
@@ -452,7 +452,7 @@ VMAnonymousCache::~VMAnonymousCache()
_FreeSwapPageRange(virtual_base, virtual_end, false);
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)
return error;
fCommittedSize = 0;
fCanOvercommit = canOvercommit;
fHasPrecommitted = false;
fPrecommittedPages = min_c(numPrecommittedPages, 255);
@@ -612,7 +613,7 @@ VMAnonymousCache::Discard(off_t offset, off_t size)
_FreeSwapPageRange(offset, offset + size);
const ssize_t discarded = VMCache::Discard(offset, size);
if (discarded > 0 && fCanOvercommit)
Commit(committed_size - discarded, VM_PRIORITY_USER);
Commit(fCommittedSize - discarded, VM_PRIORITY_USER);
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.
uint32 newPages = page_count - initialPageCount;
off_t pagesCommitment = newPages * B_PAGE_SIZE;
source->committed_size -= pagesCommitment;
committed_size += pagesCommitment;
source->fCommittedSize -= pagesCommitment;
fCommittedSize += pagesCommitment;
}
return status;
}
off_t
VMAnonymousCache::Commitment() const
{
return fCommittedSize;
}
bool
VMAnonymousCache::CanOvercommit()
{
return fCanOvercommit;
}
status_t
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
// unreserve memory, if we're asked to shrink our commitment, though.
if (fCanOvercommit && size > committed_size) {
if (fCanOvercommit && size > fCommittedSize) {
if (fHasPrecommitted)
return B_OK;
@@ -754,31 +769,37 @@ VMAnonymousCache::Commit(off_t size, int priority)
size = precommitted;
// pre-commit should not shrink existing commitment
size += committed_size;
size += fCommittedSize;
}
// Check to see how much we could commit - we need real memory
if (size > committed_size) {
if (size > fCommittedSize) {
// 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) {
return B_NO_MEMORY;
}
} else {
// 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;
}
bool
VMAnonymousCache::CanOvercommit()
void
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 (fPrecommittedPages == 0) {
// never commit more than needed
if (committed_size / B_PAGE_SIZE > page_count)
if (fCommittedSize / B_PAGE_SIZE > page_count)
return B_BAD_HANDLER;
// try to commit additional memory
@@ -1058,7 +1079,7 @@ VMAnonymousCache::Fault(struct VMAddressSpace* aspace, off_t offset)
return B_NO_MEMORY;
}
committed_size += B_PAGE_SIZE;
fCommittedSize += B_PAGE_SIZE;
} else
fPrecommittedPages--;
}
@@ -1079,13 +1100,13 @@ VMAnonymousCache::Merge(VMCache* _source)
}
// take over the source's committed size
committed_size += source->committed_size;
source->committed_size = 0;
fCommittedSize += source->fCommittedSize;
source->fCommittedSize = 0;
off_t actualSize = PAGE_ALIGN(virtual_end - virtual_base);
if (committed_size > actualSize) {
vm_unreserve_memory_or_swap(committed_size - actualSize);
committed_size = actualSize;
if (fCommittedSize > actualSize) {
vm_unreserve_memory_or_swap(fCommittedSize - actualSize);
fCommittedSize = actualSize;
}
// 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 off_t Commitment() const;
virtual bool CanOvercommit();
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 DebugStoreHasPage(off_t offset);
@@ -100,6 +103,7 @@ private:
private:
friend bool swap_free_page_swap_space(vm_page* page);
off_t fCommittedSize;
bool fCanOvercommit;
bool fHasPrecommitted;
uint8 fPrecommittedPages;
@@ -49,6 +49,7 @@ VMAnonymousNoSwapCache::Init(bool canOvercommit, int32 numPrecommittedPages,
if (error != B_OK)
return error;
committed_size = 0;
fCanOvercommit = canOvercommit;
fHasPrecommitted = false;
fPrecommittedPages = min_c(numPrecommittedPages, 255);
@@ -59,14 +60,19 @@ VMAnonymousNoSwapCache::Init(bool canOvercommit, int32 numPrecommittedPages,
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)
{
VMAnonymousNoSwapCache* from = dynamic_cast<VMAnonymousNoSwapCache*>(_from);
ASSERT(from != NULL);
uint32 initialPageCount = page_count;
status_t status = VMCache::Adopt(from, offset, size, newOffset);
if (fCanOvercommit) {
// We need to adopt the commitment for these pages.
ASSERT(from->fCanOvercommit);
uint32 newPages = page_count - initialPageCount;
off_t pagesCommitment = newPages * B_PAGE_SIZE;
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
VMAnonymousNoSwapCache::Commit(off_t size, int priority)
{
@@ -127,10 +147,16 @@ VMAnonymousNoSwapCache::Commit(off_t size, int priority)
}
bool
VMAnonymousNoSwapCache::CanOvercommit()
void
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);
virtual ssize_t Discard(off_t offset, off_t size);
virtual off_t Commitment() const;
virtual bool CanOvercommit();
virtual status_t Commit(off_t size, int priority);
virtual void TakeCommitmentFrom(VMCache* from, off_t commitment);
virtual bool StoreHasPage(off_t offset);
virtual int32 GuardSize() { return fGuardedSize; }
@@ -49,6 +52,9 @@ public:
protected:
virtual void DeleteObject();
public:
off_t committed_size;
private:
bool fCanOvercommit;
bool fHasPrecommitted;
+8 -2
View File
@@ -638,7 +638,6 @@ VMCache::Init(const char* name, uint32 cacheType, uint32 allocationFlags)
source = NULL;
virtual_base = 0;
virtual_end = 0;
committed_size = 0;
temporary = 0;
page_count = 0;
fWiredPagesCount = 0;
@@ -1310,7 +1309,7 @@ VMCache::FlushAndRemoveAllPages()
off_t
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
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) {
// Steal some of the original cache's commitment.
const size_t steal = PAGE_ALIGN(secondSize);
if (cache->committed_size > (off_t)steal) {
cache->committed_size -= steal;
secondCache->committed_size += steal;
if (cache->Commitment() > (off_t)steal) {
secondCache->TakeCommitmentFrom(cache, steal);
commitmentStolen = steal;
}
}
@@ -1001,8 +1000,7 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
}
if (error != B_OK) {
secondCache->committed_size -= commitmentStolen;
cache->committed_size += commitmentStolen;
cache->TakeCommitmentFrom(secondCache, commitmentStolen);
// Move the pages back.
status_t readoptStatus = cache->Adopt(secondCache,
@@ -1036,9 +1034,9 @@ cut_area(VMAddressSpace* addressSpace, VMArea* area, addr_t address,
if (resizePriority == -1) {
// Adjust commitments.
const off_t areaCommit = compute_area_page_commitment(area) * B_PAGE_SIZE;
if (areaCommit < area->cache->committed_size) {
secondArea->cache->committed_size += area->cache->committed_size - areaCommit;
area->cache->committed_size = areaCommit;
if (areaCommit < area->cache->Commitment()) {
secondArea->cache->TakeCommitmentFrom(area->cache,
area->cache->Commitment() - areaCommit);
}
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->virtual_end = size;
cache->committed_size = reservedMemory;
// TODO: This should be done via a method.
reservedMemory = 0;
if (reservedMemory != 0) {
VMAnonymousNoSwapCache* noSwapCache = dynamic_cast<VMAnonymousNoSwapCache*>(cache);
noSwapCache->committed_size = reservedMemory;
reservedMemory = 0;
}
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;
// 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);
if (lowerNewCommitment < lowerOldCommitment) {
lowerCache->committed_size = lowerNewCommitment;
upperCache->committed_size = lowerOldCommitment - lowerNewCommitment;
upperCache->TakeCommitmentFrom(lowerCache,
lowerOldCommitment - lowerNewCommitment);
}
status = upperCache->Commit(lowerOldCommitment, VM_PRIORITY_USER);
if (status != B_OK) {
lowerCache->committed_size += upperCache->committed_size;
upperCache->committed_size = 0;
lowerCache->TakeCommitmentFrom(upperCache, upperCache->Commitment());
upperCache->ReleaseRefAndUnlock();
return status;
}