From 428bc69ab87e0c292882660da33acd68cff729dc Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Sat, 23 May 2020 00:00:38 +0200 Subject: [PATCH] VMCache: Factor out a _FreePageRange method. The code in the Resize and Rebase methods was identical except for the iterator. Change-Id: I9f6b3c2c09af0c26778215bd627fed030c4d46f1 Reviewed-on: https://review.haiku-os.org/c/haiku/+/2835 Reviewed-by: waddlesplash --- headers/private/kernel/vm/VMCache.h | 3 + src/system/kernel/vm/VMCache.cpp | 116 ++++++++++++---------------- 2 files changed, 52 insertions(+), 67 deletions(-) diff --git a/headers/private/kernel/vm/VMCache.h b/headers/private/kernel/vm/VMCache.h index d5185b93b7..0aee4f9210 100644 --- a/headers/private/kernel/vm/VMCache.h +++ b/headers/private/kernel/vm/VMCache.h @@ -215,6 +215,9 @@ private: void _MergeWithOnlyConsumer(); void _RemoveConsumer(VMCache* consumer); + bool _FreePageRange(VMCachePagesTree::Iterator it, + page_num_t* toPage); + private: int32 fRefCount; mutex fLock; diff --git a/src/system/kernel/vm/VMCache.cpp b/src/system/kernel/vm/VMCache.cpp index f0f626c50d..d820333513 100644 --- a/src/system/kernel/vm/VMCache.cpp +++ b/src/system/kernel/vm/VMCache.cpp @@ -1111,6 +1111,46 @@ VMCache::SetMinimalCommitment(off_t commitment, int priority) } +bool +VMCache::_FreePageRange(VMCachePagesTree::Iterator it, + page_num_t* toPage = NULL) +{ + for (vm_page* page = it.Next(); + page != NULL && (toPage == NULL || page->cache_offset < *toPage); + page = it.Next()) { + + if (page->busy) { + if (page->busy_writing) { + // We cannot wait for the page to become available + // as we might cause a deadlock this way + page->busy_writing = false; + // this will notify the writer to free the page + continue; + } + + // wait for page to become unbusy + WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); + return true; + } + + // remove the page and put it into the free queue + DEBUG_PAGE_ACCESS_START(page); + vm_remove_all_page_mappings(page); + ASSERT(page->WiredCount() == 0); + // TODO: Find a real solution! If the page is wired + // temporarily (e.g. by lock_memory()), we actually must not + // unmap it! + RemovePage(page); + // Note: When iterating through a IteratableSplayTree + // removing the current node is safe. + + vm_page_free(this, page); + } + + return false; +} + + /*! This function updates the size field of the cache. If needed, it will free up all pages that don't belong to the cache anymore. The cache lock must be held when you call it. @@ -1133,44 +1173,16 @@ VMCache::Resize(off_t newSize, int priority) if (status != B_OK) return status; - uint32 oldPageCount = (uint32)((virtual_end + B_PAGE_SIZE - 1) + page_num_t oldPageCount = (page_num_t)((virtual_end + B_PAGE_SIZE - 1) + >> PAGE_SHIFT); + page_num_t newPageCount = (page_num_t)((newSize + B_PAGE_SIZE - 1) >> PAGE_SHIFT); - uint32 newPageCount = (uint32)((newSize + B_PAGE_SIZE - 1) >> PAGE_SHIFT); if (newPageCount < oldPageCount) { // we need to remove all pages in the cache outside of the new virtual // size - for (VMCachePagesTree::Iterator it - = pages.GetIterator(newPageCount, true, true); - vm_page* page = it.Next();) { - if (page->busy) { - if (page->busy_writing) { - // We cannot wait for the page to become available - // as we might cause a deadlock this way - page->busy_writing = false; - // this will notify the writer to free the page - } else { - // wait for page to become unbusy - WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); - - // restart from the start of the list - it = pages.GetIterator(newPageCount, true, true); - } - continue; - } - - // remove the page and put it into the free queue - DEBUG_PAGE_ACCESS_START(page); - vm_remove_all_page_mappings(page); - ASSERT(page->WiredCount() == 0); - // TODO: Find a real solution! If the page is wired - // temporarily (e.g. by lock_memory()), we actually must not - // unmap it! - RemovePage(page); - vm_page_free(this, page); - // Note: When iterating through a IteratableSplayTree - // removing the current node is safe. - } + while (_FreePageRange(pages.GetIterator(newPageCount, true, true))) + ; } virtual_end = newSize; @@ -1199,43 +1211,13 @@ VMCache::Rebase(off_t newBase, int priority) if (status != B_OK) return status; - uint32 basePage = (uint32)(newBase >> PAGE_SHIFT); + page_num_t basePage = (page_num_t)(newBase >> PAGE_SHIFT); if (newBase > virtual_base) { // we need to remove all pages in the cache outside of the new virtual - // size - VMCachePagesTree::Iterator it = pages.GetIterator(); - for (vm_page* page = it.Next(); - page != NULL && page->cache_offset < basePage; - page = it.Next()) { - if (page->busy) { - if (page->busy_writing) { - // We cannot wait for the page to become available - // as we might cause a deadlock this way - page->busy_writing = false; - // this will notify the writer to free the page - } else { - // wait for page to become unbusy - WaitForPageEvents(page, PAGE_EVENT_NOT_BUSY, true); - - // restart from the start of the list - it = pages.GetIterator(); - } - continue; - } - - // remove the page and put it into the free queue - DEBUG_PAGE_ACCESS_START(page); - vm_remove_all_page_mappings(page); - ASSERT(page->WiredCount() == 0); - // TODO: Find a real solution! If the page is wired - // temporarily (e.g. by lock_memory()), we actually must not - // unmap it! - RemovePage(page); - vm_page_free(this, page); - // Note: When iterating through a IteratableSplayTree - // removing the current node is safe. - } + // base + while (_FreePageRange(pages.GetIterator(), &basePage)) + ; } virtual_base = newBase;