Files
haiku-beta6/src/system/kernel/vm/vm_daemons.cpp
T
Axel Dörfler f8941839c3 * Reworked stealing pages: the page thief thread is gone now,
vm_page_reserve_pages() and vm_page_allocate_page() will now steal pages from
  the inactive queue as needed.
* We currently never steal active pages anymore, but this might need to be
  revised later (therefore, the page scanner never waits anymore, but uses
  mutex_trylock() to lock a cache).
* The page scanner and writer now both run at normal priority - let's see how
  that will work out.
* Introduced an inactive queue.
* Instead of shuffling pages around in the queue (and therefore destroying LRU)
  the page stealing mechanism now uses a marker page to be able to release the
  page lock without losing its position in the queue.
* The page writer now always grabs the whole release count of the semaphore, so
  that there won't be a huge backlog to catch up with. 
* vm_page_num_free_pages() now also includes the inactive queue as well as the
  reserved pages (they are no longer regarded as free pages).
* Added a insert_page_after() function that inserts a page after another one,
  needed by the marker code.
* clear_page() now gets a vm_page instead of a physical address which simplified
  some code.
* Removed superfluous initialization of the queues (if those aren't zeroed on
  start, we would have serious problems, anyway).
* Removed old and unimplemented dump_free_page_table() ("free_pages") KDL
  command.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22506 a95241bf-73f2-0310-859d-f6bbb57e9c96
2007-10-11 08:01:18 +00:00

245 lines
5.1 KiB
C++

/*
* Copyright 2007, Axel Dörfler, [email protected]. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include "PageCacheLocker.h"
#include <signal.h>
#include <OS.h>
#include <vm.h>
#include <vm_priv.h>
#include <vm_cache.h>
#include <vm_low_memory.h>
#include <vm_page.h>
const static uint32 kMinScanPagesCount = 512;
const static uint32 kMaxScanPagesCount = 8192;
const static bigtime_t kMinScanWaitInterval = 50000LL; // 50 ms
const static bigtime_t kMaxScanWaitInterval = 1000000LL; // 1 sec
static uint32 sLowPagesCount;
static sem_id sPageDaemonSem;
static uint32 sNumPages;
PageCacheLocker::PageCacheLocker(vm_page* page)
:
fPage(NULL)
{
Lock(page);
}
PageCacheLocker::~PageCacheLocker()
{
Unlock();
}
bool
PageCacheLocker::_IgnorePage(vm_page* page)
{
if (page->state == PAGE_STATE_WIRED || page->state == PAGE_STATE_BUSY
|| page->state == PAGE_STATE_FREE || page->state == PAGE_STATE_CLEAR
|| page->state == PAGE_STATE_UNUSED || page->wired_count > 0
|| page->cache == NULL)
return true;
return false;
}
bool
PageCacheLocker::Lock(vm_page* page)
{
if (_IgnorePage(page))
return false;
// Grab a reference to this cache.
vm_cache* cache = vm_cache_acquire_page_cache_ref(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 (cache != page->cache || _IgnorePage(page)) {
mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
return false;
}
fPage = page;
return true;
}
void
PageCacheLocker::Unlock()
{
if (fPage == NULL)
return;
vm_cache* cache = fPage->cache;
mutex_unlock(&cache->lock);
vm_cache_release_ref(cache);
fPage = NULL;
}
// #pragma mark -
static void
clear_page_activation(int32 index)
{
vm_page *page = vm_page_at_index(index);
PageCacheLocker locker(page);
if (!locker.IsLocked())
return;
if (page->state == PAGE_STATE_ACTIVE)
vm_clear_map_flags(page, PAGE_ACCESSED);
}
static bool
check_page_activation(int32 index)
{
vm_page *page = vm_page_at_index(index);
PageCacheLocker locker(page);
if (!locker.IsLocked())
return false;
bool modified;
int32 activation = vm_test_map_activation(page, &modified);
if (modified && page->state != PAGE_STATE_MODIFIED) {
//dprintf("page %p -> move to modified\n", page);
vm_page_set_state(page, PAGE_STATE_MODIFIED);
}
if (activation > 0) {
// page is still in active use
if (page->usage_count < 0) {
if (page->state != PAGE_STATE_MODIFIED)
vm_page_set_state(page, PAGE_STATE_ACTIVE);
page->usage_count = 1;
//dprintf("page %p -> move to active\n", page);
} else if (page->usage_count < 127)
page->usage_count++;
return false;
}
if (page->usage_count > -128)
page->usage_count--;
if (page->usage_count < 0) {
uint32 flags;
vm_remove_all_page_mappings(page, &flags);
// recheck eventual last minute changes
if ((flags & PAGE_MODIFIED) != 0 && page->state != PAGE_STATE_MODIFIED)
vm_page_set_state(page, PAGE_STATE_MODIFIED);
if ((flags & PAGE_ACCESSED) != 0 && ++page->usage_count >= 0)
return false;
if (page->state == PAGE_STATE_MODIFIED)
vm_page_schedule_write_page(page);
else
vm_page_set_state(page, PAGE_STATE_INACTIVE);
//dprintf("page %p -> move to inactive\n", page);
}
return true;
}
static status_t
page_daemon(void* /*unused*/)
{
bigtime_t scanWaitInterval = kMaxScanWaitInterval;
uint32 scanPagesCount = kMinScanPagesCount;
uint32 clearPage = 0;
uint32 checkPage = sNumPages / 2;
while (true) {
acquire_sem_etc(sPageDaemonSem, 1, B_RELATIVE_TIMEOUT,
scanWaitInterval);
// Compute next run time
uint32 pagesLeft = vm_page_num_free_pages();
if (pagesLeft > sLowPagesCount) {
// don't do anything if we have enough free memory left
continue;
}
scanWaitInterval = kMinScanWaitInterval
+ (kMaxScanWaitInterval - kMinScanWaitInterval)
* pagesLeft / sLowPagesCount;
scanPagesCount = kMaxScanPagesCount
- (kMaxScanPagesCount - kMinScanPagesCount)
* pagesLeft / sLowPagesCount;
uint32 leftToFree = sLowPagesCount - pagesLeft;
dprintf("wait interval %Ld, scan pages %lu, free %lu, target %lu\n",
scanWaitInterval, scanPagesCount, pagesLeft, leftToFree);
for (uint32 i = 0; i < scanPagesCount && leftToFree > 0; i++) {
if (clearPage == 0)
dprintf("clear through\n");
if (checkPage == 0)
dprintf("check through\n");
clear_page_activation(clearPage);
if (check_page_activation(checkPage))
leftToFree--;
if (++clearPage == sNumPages)
clearPage = 0;
if (++checkPage == sNumPages)
checkPage = 0;
}
}
return B_OK;
}
void
vm_schedule_page_scanner(uint32 target)
{
release_sem(sPageDaemonSem);
}
status_t
vm_daemon_init()
{
sPageDaemonSem = create_sem(0, "page daemon");
sNumPages = vm_page_num_pages();
sLowPagesCount = sNumPages / 16;
if (sLowPagesCount < 1024)
sLowPagesCount = 1024;
// create a kernel thread to select pages for pageout
thread_id thread = spawn_kernel_thread(&page_daemon, "page daemon",
B_NORMAL_PRIORITY, NULL);
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
return B_OK;
}