* 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
This commit is contained in:
Ingo Weinhold
2010-07-22 11:32:42 +00:00
parent 4e08fb8589
commit 2ab433ba90
+52 -29
View File
@@ -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;
}