From f7e414f22b68451ecb9201f435630753117976fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Wed, 7 Nov 2007 17:09:05 +0000 Subject: [PATCH] * 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 --- src/system/kernel/vm/PageCacheLocker.h | 4 +- src/system/kernel/vm/vm_daemons.cpp | 19 +- src/system/kernel/vm/vm_page.cpp | 251 ++++++++++++++----------- 3 files changed, 157 insertions(+), 117 deletions(-) diff --git a/src/system/kernel/vm/PageCacheLocker.h b/src/system/kernel/vm/PageCacheLocker.h index a215a61fa5..c3008050bd 100644 --- a/src/system/kernel/vm/PageCacheLocker.h +++ b/src/system/kernel/vm/PageCacheLocker.h @@ -13,12 +13,12 @@ struct vm_page; class PageCacheLocker { public: - PageCacheLocker(vm_page* page); + PageCacheLocker(vm_page* page, bool dontWait = true); ~PageCacheLocker(); bool IsLocked() { return fPage != NULL; } - bool Lock(vm_page* page); + bool Lock(vm_page* page, bool dontWait = true); void Unlock(); private: diff --git a/src/system/kernel/vm/vm_daemons.cpp b/src/system/kernel/vm/vm_daemons.cpp index ecd6856b4d..43886f5445 100644 --- a/src/system/kernel/vm/vm_daemons.cpp +++ b/src/system/kernel/vm/vm_daemons.cpp @@ -27,7 +27,7 @@ static sem_id sPageDaemonSem; static uint32 sNumPages; -PageCacheLocker::PageCacheLocker(vm_page* page) +PageCacheLocker::PageCacheLocker(vm_page* page, bool dontWait) : fPage(NULL) { @@ -55,7 +55,7 @@ PageCacheLocker::_IgnorePage(vm_page* page) bool -PageCacheLocker::Lock(vm_page* page) +PageCacheLocker::Lock(vm_page* page, bool dontWait) { if (_IgnorePage(page)) return false; @@ -65,14 +65,13 @@ PageCacheLocker::Lock(vm_page* page) if (cache == NULL) return false; -#if 0 - mutex_lock(&cache->lock); -#else - if (mutex_trylock(&cache->lock) != B_OK) { - vm_cache_release_ref(cache); - return false; - } -#endif + if (dontWait) { + if (mutex_trylock(&cache->lock) != B_OK) { + vm_cache_release_ref(cache); + return false; + } + } else + mutex_lock(&cache->lock); if (cache != page->cache || _IgnorePage(page)) { mutex_unlock(&cache->lock); diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index c81887160b..35b3fc2564 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -61,6 +61,7 @@ static addr_t sPhysicalPageOffset; static size_t sNumPages; static size_t sReservedPages; static vint32 sPageDeficit; +static size_t sModifiedTemporaryPages; static ConditionVariable sFreePageCondition; static spinlock sPageLock; @@ -596,6 +597,9 @@ free_page_queue_count(void) static status_t set_page_state_nolock(vm_page *page, int pageState) { + if (pageState == page->state) + return B_OK; + page_queue *fromQueue = 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) 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 ((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; enqueue_page(state == PAGE_STATE_ACTIVE ? &sActivePageQueue : &sInactivePageQueue, page); + if (page->cache->temporary) + sModifiedTemporaryPages--; } else 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 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 @@ -832,14 +896,22 @@ write_page(vm_page *page, bool mayBlock, bool fsReenter) status_t page_writer(void* /*unused*/) { - while (true) { - int32 count = 0; - get_sem_count(sWriterWaitSem, &count); - if (count == 0) - count = 1; + vm_page marker; + marker.type = PAGE_TYPE_DUMMY; + marker.cache = NULL; + marker.state = PAGE_STATE_UNUSED; - acquire_sem_etc(sWriterWaitSem, count, B_RELATIVE_TIMEOUT, 3000000); - // all 3 seconds when no one triggers us + while (true) { + 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; ConditionVariable busyConditions[kNumPages]; @@ -855,38 +927,31 @@ page_writer(void* /*unused*/) // collect pages to be written while (numPages < kNumPages) { - InterruptsSpinLocker locker(sPageLock); - - vm_page *page = sModifiedPageQueue.head; - while (page != NULL && page->state == PAGE_STATE_BUSY) { - // skip busy pages - page = page->queue_next; - } + vm_page *page = next_modified_page(marker); if (page == NULL) break; - locker.Unlock(); - - PageCacheLocker cacheLocker(page); + PageCacheLocker cacheLocker(page, false); if (!cacheLocker.IsLocked()) continue; 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) { // we need our own reference to the store, as it might // currently be destructed if (cache->store->ops->acquire_unreferenced_ref(cache->store) != 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(); thread_yield(); continue; } } - locker.Lock(); + InterruptsSpinLocker locker(sPageLock); remove_page_from_queue(&sModifiedPageQueue, page); page->state = PAGE_STATE_BUSY; @@ -938,10 +1003,63 @@ page_writer(void* /*unused*/) } } + remove_page_marker(marker); 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 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 steal_pages(vm_page **pages, size_t count, bool reserve) { @@ -1351,21 +1391,21 @@ vm_page_init_post_area(kernel_args *args) status_t vm_page_init_post_thread(kernel_args *args) { + new (&sFreePageCondition) ConditionVariable; + sFreePageCondition.Publish(&sFreePageQueue, "free page"); + // create a kernel thread to clear out pages thread_id thread = spawn_kernel_thread(&page_scrubber, "page scrubber", B_LOWEST_ACTIVE_PRIORITY, NULL); send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE); - new (&sFreePageCondition) ConditionVariable; - sFreePageCondition.Publish(&sFreePageQueue, "free page"); - - // start page writer and page thief + // start page writer sWriterWaitSem = create_sem(0, "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); return B_OK; @@ -1593,6 +1633,7 @@ vm_page_allocate_page_run(int pageState, addr_t length) InterruptsSpinLocker locker(sPageLock); if (sFreePageQueue.count + sClearPageQueue.count - sReservedPages < length) { + // TODO: add more tries, ie. free some inactive, ... // no free space return NULL; }