2002-07-09 16:24:59 +04:00
|
|
|
/*
|
2005-03-19 04:58:05 +03:00
|
|
|
* Copyright 2004-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
|
|
|
|
* Distributed under the terms of the MIT License.
|
|
|
|
*
|
|
|
|
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
|
|
|
|
* Distributed under the terms of the NewOS License.
|
|
|
|
*/
|
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
#include <kernel.h>
|
|
|
|
#include <thread.h>
|
|
|
|
#include <debug.h>
|
|
|
|
#include <OS.h>
|
|
|
|
#include <arch/cpu.h>
|
|
|
|
#include <vm.h>
|
|
|
|
#include <vm_priv.h>
|
|
|
|
#include <vm_cache.h>
|
|
|
|
#include <vm_page.h>
|
|
|
|
|
2005-03-19 04:58:05 +03:00
|
|
|
|
2002-07-09 16:24:59 +04:00
|
|
|
bool trimming_cycle;
|
2004-10-20 04:19:38 +04:00
|
|
|
static addr_t free_memory_low_water;
|
|
|
|
static addr_t free_memory_high_water;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
|
|
|
|
static void
|
2004-10-20 04:19:38 +04:00
|
|
|
scan_pages(vm_address_space *aspace, addr_t free_target)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2004-11-08 17:25:09 +03:00
|
|
|
vm_area *firstArea;
|
|
|
|
vm_area *area;
|
2002-07-09 16:24:59 +04:00
|
|
|
vm_page *page;
|
2004-10-20 04:19:38 +04:00
|
|
|
addr_t va;
|
|
|
|
addr_t pa;
|
2004-06-17 17:23:30 +04:00
|
|
|
uint32 flags, flags2;
|
2002-07-09 16:24:59 +04:00
|
|
|
// int err;
|
|
|
|
int quantum = PAGE_SCAN_QUANTUM;
|
|
|
|
|
|
|
|
// dprintf("scan_pages called on aspace 0x%x, id 0x%x, free_target %d\n", aspace, aspace->id, free_target);
|
|
|
|
|
2005-12-20 16:29:11 +03:00
|
|
|
acquire_sem_etc(aspace->sem, READ_COUNT, 0, 0);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2005-12-20 16:29:11 +03:00
|
|
|
firstArea = aspace->areas;
|
2004-11-08 17:25:09 +03:00
|
|
|
while (firstArea && (firstArea->base + (firstArea->size - 1)) < aspace->scan_va)
|
2005-12-20 16:29:11 +03:00
|
|
|
firstArea = firstArea->address_space_next;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-11-08 17:25:09 +03:00
|
|
|
if (!firstArea)
|
2005-12-20 16:29:11 +03:00
|
|
|
firstArea = aspace->areas;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-11-08 17:25:09 +03:00
|
|
|
if (!firstArea) {
|
2005-12-20 16:29:11 +03:00
|
|
|
release_sem_etc(aspace->sem, READ_COUNT, 0);
|
2002-07-09 16:24:59 +04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-11-08 17:25:09 +03:00
|
|
|
area = firstArea;
|
2004-06-10 06:03:55 +04:00
|
|
|
for (;;) {
|
|
|
|
// ignore reserved ranges
|
2005-05-15 19:06:57 +04:00
|
|
|
while (area != NULL && area->id == RESERVED_AREA_ID)
|
2005-12-20 16:29:11 +03:00
|
|
|
area = area->address_space_next;
|
2004-11-08 17:25:09 +03:00
|
|
|
if (area == NULL)
|
2004-06-10 06:03:55 +04:00
|
|
|
break;
|
|
|
|
|
2004-11-08 17:25:09 +03:00
|
|
|
// scan the pages in this area
|
|
|
|
mutex_lock(&area->cache_ref->lock);
|
|
|
|
if (!area->cache_ref->cache->scan_skip) {
|
|
|
|
for(va = area->base; va < (area->base + area->size); va += B_PAGE_SIZE) {
|
2002-07-09 16:24:59 +04:00
|
|
|
aspace->translation_map.ops->lock(&aspace->translation_map);
|
|
|
|
aspace->translation_map.ops->query(&aspace->translation_map, va, &pa, &flags);
|
2004-06-10 06:03:55 +04:00
|
|
|
if ((flags & PAGE_PRESENT) == 0) {
|
2002-07-09 16:24:59 +04:00
|
|
|
aspace->translation_map.ops->unlock(&aspace->translation_map);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-10-20 04:19:38 +04:00
|
|
|
page = vm_lookup_page(pa / B_PAGE_SIZE);
|
2004-06-10 06:03:55 +04:00
|
|
|
if (!page) {
|
2002-07-09 16:24:59 +04:00
|
|
|
aspace->translation_map.ops->unlock(&aspace->translation_map);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// see if this page is busy, if it is lets forget it and move on
|
2004-06-10 06:03:55 +04:00
|
|
|
if (page->state == PAGE_STATE_BUSY || page->state == PAGE_STATE_WIRED) {
|
2002-07-09 16:24:59 +04:00
|
|
|
aspace->translation_map.ops->unlock(&aspace->translation_map);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
flags2 = 0;
|
2004-06-10 06:03:55 +04:00
|
|
|
if (free_target > 0) {
|
2002-07-09 16:24:59 +04:00
|
|
|
// look for a page we can steal
|
2004-06-10 06:03:55 +04:00
|
|
|
if (!(flags & PAGE_ACCESSED) && page->state == PAGE_STATE_ACTIVE) {
|
2002-07-09 16:24:59 +04:00
|
|
|
// unmap the page
|
2004-10-20 04:19:38 +04:00
|
|
|
aspace->translation_map.ops->unmap(&aspace->translation_map, va, va + B_PAGE_SIZE);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
// flush the tlbs of all cpus
|
|
|
|
aspace->translation_map.ops->flush(&aspace->translation_map);
|
|
|
|
|
|
|
|
// re-query the flags on the old pte, to make sure we have accurate modified bit data
|
|
|
|
aspace->translation_map.ops->query(&aspace->translation_map, va, &pa, &flags2);
|
|
|
|
|
|
|
|
// clear the modified and accessed bits on the entries
|
|
|
|
aspace->translation_map.ops->clear_flags(&aspace->translation_map, va, PAGE_MODIFIED|PAGE_ACCESSED);
|
|
|
|
|
|
|
|
// decrement the ref count on the page. If we just unmapped it for the last time,
|
|
|
|
// put the page on the inactive list
|
2004-06-10 06:03:55 +04:00
|
|
|
if (atomic_add(&page->ref_count, -1) == 1) {
|
2002-07-09 16:24:59 +04:00
|
|
|
vm_page_set_state(page, PAGE_STATE_INACTIVE);
|
|
|
|
free_target--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the page is modified, but the state is active or inactive, put it on the modified list
|
2004-06-10 06:03:55 +04:00
|
|
|
if (((flags & PAGE_MODIFIED) || (flags2 & PAGE_MODIFIED))
|
2002-07-09 16:24:59 +04:00
|
|
|
&& (page->state == PAGE_STATE_ACTIVE || page->state == PAGE_STATE_INACTIVE)) {
|
|
|
|
vm_page_set_state(page, PAGE_STATE_MODIFIED);
|
|
|
|
}
|
|
|
|
|
|
|
|
aspace->translation_map.ops->unlock(&aspace->translation_map);
|
2004-06-10 06:03:55 +04:00
|
|
|
if (--quantum == 0)
|
2002-07-09 16:24:59 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2004-11-08 17:25:09 +03:00
|
|
|
mutex_unlock(&area->cache_ref->lock);
|
|
|
|
// move to the next area, wrapping around and stopping if we get back to the first area
|
2005-12-20 16:29:11 +03:00
|
|
|
area = area->address_space_next ? area->address_space_next : aspace->areas;
|
2004-11-08 17:25:09 +03:00
|
|
|
if (area == firstArea)
|
2002-07-09 16:24:59 +04:00
|
|
|
break;
|
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
if (quantum == 0)
|
2002-07-09 16:24:59 +04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2005-12-20 16:29:11 +03:00
|
|
|
aspace->scan_va = area ? (firstArea->base + firstArea->size) : aspace->base;
|
|
|
|
release_sem_etc(aspace->sem, READ_COUNT, 0);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
// dprintf("exiting scan_pages\n");
|
|
|
|
}
|
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
|
2003-01-27 06:02:24 +03:00
|
|
|
static int32
|
|
|
|
page_daemon(void *unused)
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
|
|
|
struct hash_iterator i;
|
|
|
|
vm_address_space *old_aspace;
|
|
|
|
vm_address_space *aspace;
|
2004-10-20 04:19:38 +04:00
|
|
|
addr_t mapped_size;
|
|
|
|
addr_t free_memory_target;
|
2002-07-09 16:24:59 +04:00
|
|
|
int faults_per_second;
|
|
|
|
bigtime_t now;
|
|
|
|
|
|
|
|
dprintf("page daemon starting\n");
|
2003-01-27 06:02:24 +03:00
|
|
|
(void)unused;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
for (;;) {
|
2002-08-04 03:39:50 +04:00
|
|
|
snooze(PAGE_DAEMON_INTERVAL);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
// scan through all of the address spaces
|
|
|
|
vm_aspace_walk_start(&i);
|
|
|
|
aspace = vm_aspace_walk_next(&i);
|
2004-06-10 06:03:55 +04:00
|
|
|
while (aspace) {
|
2002-07-09 16:24:59 +04:00
|
|
|
mapped_size = aspace->translation_map.ops->get_mapped_size(&aspace->translation_map);
|
|
|
|
|
|
|
|
// dprintf("page_daemon: looking at aspace 0x%x, id 0x%x, mapped size %d\n", aspace, aspace->id, mapped_size);
|
|
|
|
|
|
|
|
now = system_time();
|
2004-06-10 06:03:55 +04:00
|
|
|
if (now - aspace->last_working_set_adjust > WORKING_SET_ADJUST_INTERVAL) {
|
2002-07-09 16:24:59 +04:00
|
|
|
faults_per_second = (aspace->fault_count * 1000000) / (now - aspace->last_working_set_adjust);
|
|
|
|
// dprintf(" faults_per_second = %d\n", faults_per_second);
|
|
|
|
aspace->last_working_set_adjust = now;
|
|
|
|
aspace->fault_count = 0;
|
|
|
|
|
2004-06-10 06:03:55 +04:00
|
|
|
if (faults_per_second > MAX_FAULTS_PER_SECOND
|
2002-07-09 16:24:59 +04:00
|
|
|
&& mapped_size >= aspace->working_set_size
|
|
|
|
&& aspace->working_set_size < aspace->max_working_set) {
|
|
|
|
|
|
|
|
aspace->working_set_size += WORKING_SET_INCREMENT;
|
|
|
|
// dprintf(" new working set size = %d\n", aspace->working_set_size);
|
2004-06-10 06:03:55 +04:00
|
|
|
} else if (faults_per_second < MIN_FAULTS_PER_SECOND
|
2002-07-09 16:24:59 +04:00
|
|
|
&& mapped_size <= aspace->working_set_size
|
|
|
|
&& aspace->working_set_size > aspace->min_working_set) {
|
|
|
|
|
|
|
|
aspace->working_set_size -= WORKING_SET_DECREMENT;
|
|
|
|
// dprintf(" new working set size = %d\n", aspace->working_set_size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// decide if we need to enter or leave the trimming cycle
|
2004-06-10 06:03:55 +04:00
|
|
|
if (!trimming_cycle && vm_page_num_free_pages() < free_memory_low_water)
|
2002-07-09 16:24:59 +04:00
|
|
|
trimming_cycle = true;
|
2004-06-10 06:03:55 +04:00
|
|
|
else if (trimming_cycle && vm_page_num_free_pages() > free_memory_high_water)
|
2002-07-09 16:24:59 +04:00
|
|
|
trimming_cycle = false;
|
|
|
|
|
|
|
|
// scan some pages, trying to free some if needed
|
|
|
|
free_memory_target = 0;
|
2004-06-10 06:03:55 +04:00
|
|
|
if (trimming_cycle && mapped_size > aspace->working_set_size)
|
2002-07-09 16:24:59 +04:00
|
|
|
free_memory_target = mapped_size - aspace->working_set_size;
|
|
|
|
|
|
|
|
scan_pages(aspace, free_memory_target);
|
|
|
|
|
|
|
|
// must hold a ref to the old aspace while we grab the next one,
|
|
|
|
// otherwise the iterator becomes out of date.
|
|
|
|
old_aspace = aspace;
|
|
|
|
aspace = vm_aspace_walk_next(&i);
|
2005-12-20 16:29:11 +03:00
|
|
|
vm_put_address_space(old_aspace);
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-01-27 06:02:24 +03:00
|
|
|
|
2004-11-03 20:24:41 +03:00
|
|
|
status_t
|
2003-01-27 06:02:24 +03:00
|
|
|
vm_daemon_init()
|
2002-07-09 16:24:59 +04:00
|
|
|
{
|
2003-01-27 06:02:24 +03:00
|
|
|
thread_id thread;
|
2002-07-09 16:24:59 +04:00
|
|
|
|
|
|
|
trimming_cycle = false;
|
|
|
|
|
|
|
|
// calculate the free memory low and high water at which point we enter/leave trimming phase
|
|
|
|
free_memory_low_water = vm_page_num_pages() / 8;
|
|
|
|
free_memory_high_water = vm_page_num_pages() / 4;
|
|
|
|
|
|
|
|
// create a kernel thread to select pages for pageout
|
2005-12-04 19:47:52 +03:00
|
|
|
//thread = spawn_kernel_thread(&page_daemon, "page daemon", B_FIRST_REAL_TIME_PRIORITY, NULL);
|
|
|
|
//send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
2002-07-09 16:24:59 +04:00
|
|
|
|
2004-11-08 17:25:09 +03:00
|
|
|
return B_OK;
|
2002-07-09 16:24:59 +04:00
|
|
|
}
|
|
|
|
|