* Made page_writer() use a marker page as well, so that it won't try to get

the same pages over and over.
* Increased the priority of the page writer a bit, so that it is higher than
  the one of the page daemon.
* Added a sModifiedTemporaryPages counter to let the page_writer decide how
  many pages are there to write back (temporary pages would go to the swap file
  and are only written back when memory is low).
* In case there are more than 1024 modified (non-temporary) pages around, the
  page writer will constantly write out pages, not only when being pushed.
* The page writer now temporarily always leave out temporary pages (as long as
  we don't have a swap file).
* Shuffled functions around a bit.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22852 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-11-07 17:09:05 +00:00
parent a425a5ae4d
commit f7e414f22b
3 changed files with 157 additions and 117 deletions

View File

@ -13,12 +13,12 @@ struct vm_page;
class PageCacheLocker { class PageCacheLocker {
public: public:
PageCacheLocker(vm_page* page); PageCacheLocker(vm_page* page, bool dontWait = true);
~PageCacheLocker(); ~PageCacheLocker();
bool IsLocked() { return fPage != NULL; } bool IsLocked() { return fPage != NULL; }
bool Lock(vm_page* page); bool Lock(vm_page* page, bool dontWait = true);
void Unlock(); void Unlock();
private: private:

View File

@ -27,7 +27,7 @@ static sem_id sPageDaemonSem;
static uint32 sNumPages; static uint32 sNumPages;
PageCacheLocker::PageCacheLocker(vm_page* page) PageCacheLocker::PageCacheLocker(vm_page* page, bool dontWait)
: :
fPage(NULL) fPage(NULL)
{ {
@ -55,7 +55,7 @@ PageCacheLocker::_IgnorePage(vm_page* page)
bool bool
PageCacheLocker::Lock(vm_page* page) PageCacheLocker::Lock(vm_page* page, bool dontWait)
{ {
if (_IgnorePage(page)) if (_IgnorePage(page))
return false; return false;
@ -65,14 +65,13 @@ PageCacheLocker::Lock(vm_page* page)
if (cache == NULL) if (cache == NULL)
return false; return false;
#if 0 if (dontWait) {
mutex_lock(&cache->lock); if (mutex_trylock(&cache->lock) != B_OK) {
#else vm_cache_release_ref(cache);
if (mutex_trylock(&cache->lock) != B_OK) { return false;
vm_cache_release_ref(cache); }
return false; } else
} mutex_lock(&cache->lock);
#endif
if (cache != page->cache || _IgnorePage(page)) { if (cache != page->cache || _IgnorePage(page)) {
mutex_unlock(&cache->lock); mutex_unlock(&cache->lock);

View File

@ -61,6 +61,7 @@ static addr_t sPhysicalPageOffset;
static size_t sNumPages; static size_t sNumPages;
static size_t sReservedPages; static size_t sReservedPages;
static vint32 sPageDeficit; static vint32 sPageDeficit;
static size_t sModifiedTemporaryPages;
static ConditionVariable<page_queue> sFreePageCondition; static ConditionVariable<page_queue> sFreePageCondition;
static spinlock sPageLock; static spinlock sPageLock;
@ -596,6 +597,9 @@ free_page_queue_count(void)
static status_t static status_t
set_page_state_nolock(vm_page *page, int pageState) set_page_state_nolock(vm_page *page, int pageState)
{ {
if (pageState == page->state)
return B_OK;
page_queue *fromQueue = NULL; page_queue *fromQueue = NULL;
page_queue *toQueue = NULL; page_queue *toQueue = NULL;
@ -658,7 +662,10 @@ set_page_state_nolock(vm_page *page, int pageState)
if (pageState != PAGE_STATE_INACTIVE && page->cache != NULL) if (pageState != PAGE_STATE_INACTIVE && page->cache != NULL)
panic("to be freed page %p has cache", page); panic("to be freed page %p has cache", page);
} } else if (pageState == PAGE_STATE_MODIFIED && page->cache->temporary)
sModifiedTemporaryPages++;
else if (page->state == PAGE_STATE_MODIFIED && page->cache->temporary)
sModifiedTemporaryPages--;
#if TRACK_PAGE_ALLOCATIONS #if TRACK_PAGE_ALLOCATIONS
if ((pageState == PAGE_STATE_CLEAR || pageState == PAGE_STATE_FREE) if ((pageState == PAGE_STATE_CLEAR || pageState == PAGE_STATE_FREE)
@ -690,6 +697,8 @@ move_page_to_active_or_inactive_queue(vm_page *page, bool dequeued)
page->state = state; page->state = state;
enqueue_page(state == PAGE_STATE_ACTIVE enqueue_page(state == PAGE_STATE_ACTIVE
? &sActivePageQueue : &sInactivePageQueue, page); ? &sActivePageQueue : &sInactivePageQueue, page);
if (page->cache->temporary)
sModifiedTemporaryPages--;
} else } else
set_page_state_nolock(page, state); set_page_state_nolock(page, state);
} }
@ -823,6 +832,61 @@ write_page(vm_page *page, bool mayBlock, bool fsReenter)
} }
static void
remove_page_marker(struct vm_page &marker)
{
if (marker.state == PAGE_STATE_UNUSED)
return;
page_queue *queue;
vm_page *page;
switch (marker.state) {
case PAGE_STATE_ACTIVE:
queue = &sActivePageQueue;
break;
case PAGE_STATE_INACTIVE:
queue = &sInactivePageQueue;
break;
case PAGE_STATE_MODIFIED:
queue = &sModifiedPageQueue;
break;
default:
return;
}
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
}
static vm_page *
next_modified_page(struct vm_page &marker)
{
InterruptsSpinLocker locker(sPageLock);
vm_page *page;
if (marker.state == PAGE_STATE_MODIFIED) {
page = marker.queue_next;
remove_page_from_queue(&sModifiedPageQueue, &marker);
marker.state = PAGE_STATE_UNUSED;
} else
page = sModifiedPageQueue.head;
for (; page != NULL; page = page->queue_next) {
if (page->type != PAGE_TYPE_DUMMY && page->state != PAGE_STATE_BUSY) {
// insert marker
marker.state = PAGE_STATE_MODIFIED;
insert_page_after(&sModifiedPageQueue, page, &marker);
return page;
}
}
return NULL;
}
/*! The page writer continuously takes some pages from the modified /*! The page writer continuously takes some pages from the modified
queue, writes them back, and moves them back to the active queue. queue, writes them back, and moves them back to the active queue.
It runs in its own thread, and is only there to keep the number It runs in its own thread, and is only there to keep the number
@ -832,14 +896,22 @@ write_page(vm_page *page, bool mayBlock, bool fsReenter)
status_t status_t
page_writer(void* /*unused*/) page_writer(void* /*unused*/)
{ {
while (true) { vm_page marker;
int32 count = 0; marker.type = PAGE_TYPE_DUMMY;
get_sem_count(sWriterWaitSem, &count); marker.cache = NULL;
if (count == 0) marker.state = PAGE_STATE_UNUSED;
count = 1;
acquire_sem_etc(sWriterWaitSem, count, B_RELATIVE_TIMEOUT, 3000000); while (true) {
// all 3 seconds when no one triggers us if (sModifiedPageQueue.count - sModifiedTemporaryPages < 1024
|| free_page_queue_count() > 1024) {
int32 count = 0;
get_sem_count(sWriterWaitSem, &count);
if (count == 0)
count = 1;
acquire_sem_etc(sWriterWaitSem, count, B_RELATIVE_TIMEOUT, 3000000);
// all 3 seconds when no one triggers us
}
const uint32 kNumPages = 32; const uint32 kNumPages = 32;
ConditionVariable<vm_page> busyConditions[kNumPages]; ConditionVariable<vm_page> busyConditions[kNumPages];
@ -855,38 +927,31 @@ page_writer(void* /*unused*/)
// collect pages to be written // collect pages to be written
while (numPages < kNumPages) { while (numPages < kNumPages) {
InterruptsSpinLocker locker(sPageLock); vm_page *page = next_modified_page(marker);
vm_page *page = sModifiedPageQueue.head;
while (page != NULL && page->state == PAGE_STATE_BUSY) {
// skip busy pages
page = page->queue_next;
}
if (page == NULL) if (page == NULL)
break; break;
locker.Unlock(); PageCacheLocker cacheLocker(page, false);
PageCacheLocker cacheLocker(page);
if (!cacheLocker.IsLocked()) if (!cacheLocker.IsLocked())
continue; continue;
vm_cache *cache = page->cache; vm_cache *cache = page->cache;
// TODO: write back temporary ones as soon as we have swap file support
if (cache->temporary/* && vm_low_memory_state() == B_NO_LOW_MEMORY*/)
continue;
if (cache->store->ops->acquire_unreferenced_ref != NULL) { if (cache->store->ops->acquire_unreferenced_ref != NULL) {
// we need our own reference to the store, as it might // we need our own reference to the store, as it might
// currently be destructed // currently be destructed
if (cache->store->ops->acquire_unreferenced_ref(cache->store) if (cache->store->ops->acquire_unreferenced_ref(cache->store)
!= B_OK) { != B_OK) {
// put it to the tail of the queue, then, so that we
// won't touch it too soon again
vm_page_requeue(page, true);
cacheLocker.Unlock(); cacheLocker.Unlock();
thread_yield(); thread_yield();
continue; continue;
} }
} }
locker.Lock(); InterruptsSpinLocker locker(sPageLock);
remove_page_from_queue(&sModifiedPageQueue, page); remove_page_from_queue(&sModifiedPageQueue, page);
page->state = PAGE_STATE_BUSY; page->state = PAGE_STATE_BUSY;
@ -938,10 +1003,63 @@ page_writer(void* /*unused*/)
} }
} }
remove_page_marker(marker);
return B_OK; return B_OK;
} }
static vm_page *
find_page_candidate(struct vm_page &marker, bool stealActive)
{
InterruptsSpinLocker locker(sPageLock);
page_queue *queue;
vm_page *page;
switch (marker.state) {
case PAGE_STATE_ACTIVE:
queue = &sActivePageQueue;
page = marker.queue_next;
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
break;
case PAGE_STATE_INACTIVE:
queue = &sInactivePageQueue;
page = marker.queue_next;
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
break;
default:
queue = &sInactivePageQueue;
page = sInactivePageQueue.head;
if (page == NULL && stealActive) {
queue = &sActivePageQueue;
page = sActivePageQueue.head;
}
break;
}
while (page != NULL) {
if (page->type != PAGE_TYPE_DUMMY
&& (page->state == PAGE_STATE_INACTIVE
|| (stealActive && page->state == PAGE_STATE_ACTIVE
&& page->wired_count == 0))) {
// insert marker
marker.state = queue == &sActivePageQueue ? PAGE_STATE_ACTIVE : PAGE_STATE_INACTIVE;
insert_page_after(queue, page, &marker);
return page;
}
page = page->queue_next;
if (page == NULL && stealActive && queue != &sActivePageQueue) {
queue = &sActivePageQueue;
page = sActivePageQueue.head;
}
}
return NULL;
}
static bool static bool
steal_page(vm_page *page, bool stealActive) steal_page(vm_page *page, bool stealActive)
{ {
@ -1018,84 +1136,6 @@ steal_page(vm_page *page, bool stealActive)
} }
static void
remove_page_marker(struct vm_page &marker)
{
if (marker.state == PAGE_STATE_UNUSED)
return;
page_queue *queue;
vm_page *page;
switch (marker.state) {
case PAGE_STATE_ACTIVE:
queue = &sActivePageQueue;
break;
case PAGE_STATE_INACTIVE:
queue = &sInactivePageQueue;
break;
default:
return;
}
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
}
static vm_page *
find_page_candidate(struct vm_page &marker, bool stealActive)
{
InterruptsSpinLocker locker(sPageLock);
page_queue *queue;
vm_page *page;
switch (marker.state) {
case PAGE_STATE_ACTIVE:
queue = &sActivePageQueue;
page = marker.queue_next;
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
break;
case PAGE_STATE_INACTIVE:
queue = &sInactivePageQueue;
page = marker.queue_next;
remove_page_from_queue(queue, &marker);
marker.state = PAGE_STATE_UNUSED;
break;
default:
queue = &sInactivePageQueue;
page = sInactivePageQueue.head;
if (page == NULL && stealActive) {
queue = &sActivePageQueue;
page = sActivePageQueue.head;
}
break;
}
while (page != NULL) {
if (page->type != PAGE_TYPE_DUMMY
&& (page->state == PAGE_STATE_INACTIVE
|| (stealActive && page->state == PAGE_STATE_ACTIVE
&& page->wired_count == 0))) {
// insert marker
marker.state = queue == &sActivePageQueue ? PAGE_STATE_ACTIVE : PAGE_STATE_INACTIVE;
insert_page_after(queue, page, &marker);
return page;
}
page = page->queue_next;
if (page == NULL && stealActive && queue != &sActivePageQueue) {
queue = &sActivePageQueue;
page = sActivePageQueue.head;
}
}
return NULL;
}
static size_t static size_t
steal_pages(vm_page **pages, size_t count, bool reserve) steal_pages(vm_page **pages, size_t count, bool reserve)
{ {
@ -1351,21 +1391,21 @@ vm_page_init_post_area(kernel_args *args)
status_t status_t
vm_page_init_post_thread(kernel_args *args) vm_page_init_post_thread(kernel_args *args)
{ {
new (&sFreePageCondition) ConditionVariable<page_queue>;
sFreePageCondition.Publish(&sFreePageQueue, "free page");
// create a kernel thread to clear out pages // create a kernel thread to clear out pages
thread_id thread = spawn_kernel_thread(&page_scrubber, "page scrubber", thread_id thread = spawn_kernel_thread(&page_scrubber, "page scrubber",
B_LOWEST_ACTIVE_PRIORITY, NULL); B_LOWEST_ACTIVE_PRIORITY, NULL);
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
new (&sFreePageCondition) ConditionVariable<page_queue>; // start page writer
sFreePageCondition.Publish(&sFreePageQueue, "free page");
// start page writer and page thief
sWriterWaitSem = create_sem(0, "page writer"); sWriterWaitSem = create_sem(0, "page writer");
thread = spawn_kernel_thread(&page_writer, "page writer", thread = spawn_kernel_thread(&page_writer, "page writer",
B_NORMAL_PRIORITY, NULL); B_NORMAL_PRIORITY + 1, NULL);
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
return B_OK; return B_OK;
@ -1593,6 +1633,7 @@ vm_page_allocate_page_run(int pageState, addr_t length)
InterruptsSpinLocker locker(sPageLock); InterruptsSpinLocker locker(sPageLock);
if (sFreePageQueue.count + sClearPageQueue.count - sReservedPages < length) { if (sFreePageQueue.count + sClearPageQueue.count - sReservedPages < length) {
// TODO: add more tries, ie. free some inactive, ...
// no free space // no free space
return NULL; return NULL;
} }