kernel/vm: Add discard_address_range that discards pages.
Pages in the given range are unmapped and freed without getting written back anywhere. It can be used whenever a caller does not care about the data in the given range anymore and wants to reduce page pressure. Change-Id: I8bcce68fab278efef710d3714677e1d463504a56 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2843 Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
4bfc0072e3
commit
8e74e30784
@@ -554,6 +554,14 @@ VMAnonymousCache::Rebase(off_t newBase, int priority)
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
VMAnonymousCache::Discard(off_t offset, off_t size)
|
||||
{
|
||||
_FreeSwapPageRange(offset, offset + size);
|
||||
return VMCache::Discard(offset, size);
|
||||
}
|
||||
|
||||
|
||||
/*! Moves the swap pages for the given range from the source cache into this
|
||||
cache. Both caches must be locked.
|
||||
*/
|
||||
|
||||
@@ -44,6 +44,8 @@ public:
|
||||
virtual status_t Adopt(VMCache* source, off_t offset,
|
||||
off_t size, off_t newOffset);
|
||||
|
||||
virtual status_t Discard(off_t offset, off_t size);
|
||||
|
||||
virtual status_t Commit(off_t size, int priority);
|
||||
virtual bool HasPage(off_t offset);
|
||||
virtual bool DebugHasPage(off_t offset);
|
||||
|
||||
@@ -1254,6 +1254,19 @@ VMCache::Adopt(VMCache* source, off_t offset, off_t size, off_t newOffset)
|
||||
}
|
||||
|
||||
|
||||
/*! Discards pages in the given range. */
|
||||
status_t
|
||||
VMCache::Discard(off_t offset, off_t size)
|
||||
{
|
||||
page_num_t startPage = offset >> PAGE_SHIFT;
|
||||
page_num_t endPage = (offset + size + B_PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
while (_FreePageRange(pages.GetIterator(startPage, true, true), &endPage))
|
||||
;
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! You have to call this function with the VMCache lock held. */
|
||||
status_t
|
||||
VMCache::FlushAndRemoveAllPages()
|
||||
|
||||
@@ -865,6 +865,52 @@ unmap_address_range(VMAddressSpace* addressSpace, addr_t address, addr_t size,
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
discard_area_range(VMArea* area, addr_t address, addr_t size)
|
||||
{
|
||||
addr_t offset;
|
||||
if (!intersect_area(area, address, size, offset))
|
||||
return B_OK;
|
||||
|
||||
// If someone else uses the area's cache or it's not an anonymous cache, we
|
||||
// can't discard.
|
||||
VMCache* cache = vm_area_get_locked_cache(area);
|
||||
if (cache->areas != area || area->cache_next != NULL
|
||||
|| !cache->consumers.IsEmpty() || cache->type != CACHE_TYPE_RAM) {
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
VMCacheChainLocker cacheChainLocker(cache);
|
||||
cacheChainLocker.LockAllSourceCaches();
|
||||
|
||||
unmap_pages(area, address, size);
|
||||
|
||||
// Since VMCache::Discard() can temporarily drop the lock, we must
|
||||
// unlock all lower caches to prevent locking order inversion.
|
||||
cacheChainLocker.Unlock(cache);
|
||||
cache->Discard(cache->virtual_base + offset, size);
|
||||
cache->ReleaseRefAndUnlock();
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
discard_address_range(VMAddressSpace* addressSpace, addr_t address, addr_t size,
|
||||
bool kernel)
|
||||
{
|
||||
for (VMAddressSpace::AreaRangeIterator it
|
||||
= addressSpace->GetAreaRangeIterator(address, size);
|
||||
VMArea* area = it.Next();) {
|
||||
status_t error = discard_area_range(area, address, size);
|
||||
if (error != B_OK)
|
||||
return error;
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
/*! You need to hold the lock of the cache and the write lock of the address
|
||||
space when calling this function.
|
||||
Note, that in case of error your cache will be temporarily unlocked.
|
||||
|
||||
Reference in New Issue
Block a user