From 2ab433ba903ec0c8fdf99d7cbf3c2bd4e37848b4 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 22 Jul 2010 11:32:42 +0000 Subject: [PATCH] * PageWriteWrapper::Done(): Returns whether the page was successfully written, now. * PageWriterRun::Go(): Returns the number of pages that could not be written. * page_writer()/next_modified_page(): - Don't use a marker page anymore. A visited page is requeued at the tail. This also makes the functions that schedule pages work a bit better (they queue the pages at the head of the queue). - Have an eye on pages that are busy or failed to write. If we ran through the whole modified queue without writing a single page, sleep for a short time. This mitigates the busyness the page writer falls into when there are enough modified pages to make it run all the time but none that can be written successfully. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@37681 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/system/kernel/vm/vm_page.cpp | 81 ++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 29 deletions(-) diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index 337e1a644e..5d277c3cac 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -1480,28 +1480,22 @@ remove_page_marker(struct vm_page &marker) } -static vm_page * -next_modified_page(struct vm_page &marker) +static vm_page* +next_modified_page(page_num_t& maxPagesToSee) { InterruptsSpinLocker locker(sModifiedPageQueue.GetLock()); - vm_page *page; - DEBUG_PAGE_ACCESS_CHECK(&marker); + while (maxPagesToSee > 0) { + vm_page* page = sModifiedPageQueue.Head(); + if (page == NULL) + return NULL; - if (marker.State() == PAGE_STATE_MODIFIED) { - page = sModifiedPageQueue.Next(&marker); - sModifiedPageQueue.Remove(&marker); - marker.SetState(PAGE_STATE_UNUSED); - } else - page = sModifiedPageQueue.Head(); + sModifiedPageQueue.Requeue(page, true); - for (; page != NULL; page = sModifiedPageQueue.Next(page)) { - if (!page->busy) { - // insert marker - marker.SetState(PAGE_STATE_MODIFIED); - sModifiedPageQueue.InsertAfter(page, &marker); + maxPagesToSee--; + + if (!page->busy) return page; - } } return NULL; @@ -1521,7 +1515,7 @@ public: void PrepareNextRun(); void AddPage(vm_page* page); - void Go(); + uint32 Go(); void PageWritten(PageWriteTransfer* transfer, status_t status, bool partialTransfer, size_t bytesTransferred); @@ -1569,7 +1563,7 @@ public: PageWriteWrapper(); ~PageWriteWrapper(); void SetTo(vm_page* page); - void Done(status_t result); + bool Done(status_t result); private: vm_page* fPage; @@ -1628,8 +1622,10 @@ PageWriteWrapper::SetTo(vm_page* page) /*! The page's cache must be locked. The page queues must not be locked. + \return \c true if the page was written successfully respectively could be + handled somehow, \c false otherwise. */ -void +bool PageWriteWrapper::Done(status_t result) { if (!fIsActive) @@ -1640,6 +1636,8 @@ PageWriteWrapper::Done(status_t result) fPage->busy = false; // Set unbusy and notify later by hand, since we might free the page. + bool success = true; + if (result == B_OK) { // put it into the active/inactive queue move_page_to_appropriate_queue(fPage); @@ -1677,11 +1675,15 @@ PageWriteWrapper::Done(status_t result) fPage->busy_writing = false; DEBUG_PAGE_ACCESS_END(fPage); + + success = false; } } fCache->NotifyPageEvents(fPage, PAGE_EVENT_NOT_BUSY); fIsActive = false; + + return success; } @@ -1842,7 +1844,10 @@ PageWriterRun::AddPage(vm_page* page) } -void +/*! Writes all pages previously added. + \return The number of pages that could not be written or otherwise handled. +*/ +uint32 PageWriterRun::Go() { fPendingTransfers = fTransferCount; @@ -1860,13 +1865,16 @@ PageWriterRun::Go() // mark pages depending on whether they could be written or not + uint32 failedPages = 0; uint32 wrapperIndex = 0; for (uint32 i = 0; i < fTransferCount; i++) { PageWriteTransfer& transfer = fTransfers[i]; transfer.Cache()->Lock(); - for (uint32 j = 0; j < transfer.PageCount(); j++) - fWrappers[wrapperIndex++].Done(transfer.Status()); + for (uint32 j = 0; j < transfer.PageCount(); j++) { + if (!fWrappers[wrapperIndex++].Done(transfer.Status())) + failedPages++; + } transfer.Cache()->Unlock(); } @@ -1885,6 +1893,8 @@ PageWriterRun::Go() cache->ReleaseRef(); } } + + return failedPages; } @@ -1918,8 +1928,7 @@ page_writer(void* /*unused*/) return B_ERROR; } - vm_page marker; - init_page_marker(marker); + page_num_t pagesSinceLastSuccessfulWrite = 0; while (true) { // TODO: Maybe wait shorter when memory is low! @@ -1928,10 +1937,17 @@ page_writer(void* /*unused*/) // all 3 seconds when no one triggers us } - int32 modifiedPages = sModifiedPageQueue.Count(); + page_num_t modifiedPages = sModifiedPageQueue.Count(); if (modifiedPages == 0) continue; + if (modifiedPages <= pagesSinceLastSuccessfulWrite) { + // We ran through the whole queue without being able to write a + // single page. Take a break. + snooze(500000); + pagesSinceLastSuccessfulWrite = 0; + } + #if ENABLE_SWAP_SUPPORT page_stats pageStats; get_page_stats(pageStats); @@ -1962,8 +1978,10 @@ page_writer(void* /*unused*/) // collect pages to be written pageCollectionTime -= system_time(); - while (numPages < kNumPages) { - vm_page *page = next_modified_page(marker); + page_num_t maxPagesToSee = modifiedPages; + + while (numPages < kNumPages && maxPagesToSee > 0) { + vm_page *page = next_modified_page(maxPagesToSee); if (page == NULL) break; @@ -2038,7 +2056,7 @@ page_writer(void* /*unused*/) // write pages to disk and do all the cleanup pageWritingTime -= system_time(); - run.Go(); + uint32 failedPages = run.Go(); pageWritingTime += system_time(); // debug output only... @@ -2054,9 +2072,14 @@ page_writer(void* /*unused*/) pageCollectionTime = 0; pageWritingTime = 0; } + + if (failedPages == numPages) { + pagesSinceLastSuccessfulWrite + += modifiedPages - maxPagesToSee - numPages; + } else + pagesSinceLastSuccessfulWrite = 0; } - remove_page_marker(marker); return B_OK; }