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:
Michael Lotz
2020-08-01 19:23:27 +00:00
committed by waddlesplash
parent 4bfc0072e3
commit 8e74e30784
5 changed files with 71 additions and 0 deletions
@@ -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.
*/
+2
View File
@@ -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);
+13
View File
@@ -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()
+46
View File
@@ -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.