* Decoupled the page_thief() from the low memory handler chain. This is
necessary because low memory handlers might need to wait for other services while we need to make sure the page thief can always steal pages. * Made the page thief more aggressive when memory is critical: added a third run where it will also steal a small amount of active pages to keep things going. * The page thief did not maintain the cache's reference correctly in all situations. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22382 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -61,6 +61,7 @@ static size_t sReservedPages;
|
|||||||
static ConditionVariable<page_queue> sFreePageCondition;
|
static ConditionVariable<page_queue> sFreePageCondition;
|
||||||
static spinlock sPageLock;
|
static spinlock sPageLock;
|
||||||
|
|
||||||
|
static sem_id sThiefWaitSem;
|
||||||
static sem_id sWriterWaitSem;
|
static sem_id sWriterWaitSem;
|
||||||
|
|
||||||
|
|
||||||
@@ -713,7 +714,7 @@ page_writer(void* /*unused*/)
|
|||||||
|
|
||||||
locker.Unlock();
|
locker.Unlock();
|
||||||
|
|
||||||
//dprintf("write page %p\n", page);
|
//dprintf("write page %p, cache %p (%ld)\n", page, page->cache, page->cache->ref_count);
|
||||||
vm_clear_map_flags(page, PAGE_MODIFIED);
|
vm_clear_map_flags(page, PAGE_MODIFIED);
|
||||||
vm_cache_acquire_ref(page->cache);
|
vm_cache_acquire_ref(page->cache);
|
||||||
pages[numPages++] = page;
|
pages[numPages++] = page;
|
||||||
@@ -759,29 +760,42 @@ page_writer(void* /*unused*/)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static status_t
|
||||||
page_thief(void* /*unused*/, int32 level)
|
page_thief(void* /*unused*/)
|
||||||
{
|
{
|
||||||
|
bigtime_t timeout = B_INFINITE_TIMEOUT;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
acquire_sem_etc(sThiefWaitSem, 1, B_RELATIVE_TIMEOUT, timeout);
|
||||||
|
|
||||||
|
int32 state = vm_low_memory_state();
|
||||||
uint32 steal;
|
uint32 steal;
|
||||||
int32 score;
|
int32 score;
|
||||||
|
switch (state) {
|
||||||
switch (level) {
|
|
||||||
default:
|
default:
|
||||||
|
timeout = B_INFINITE_TIMEOUT;
|
||||||
|
continue;
|
||||||
|
|
||||||
case B_LOW_MEMORY_NOTE:
|
case B_LOW_MEMORY_NOTE:
|
||||||
steal = 10;
|
steal = 10;
|
||||||
score = -20;
|
score = -20;
|
||||||
|
timeout = 1000000;
|
||||||
break;
|
break;
|
||||||
case B_LOW_MEMORY_WARNING:
|
case B_LOW_MEMORY_WARNING:
|
||||||
steal = 50;
|
steal = 50;
|
||||||
score = -5;
|
score = -5;
|
||||||
|
timeout = 1000000;
|
||||||
break;
|
break;
|
||||||
case B_LOW_MEMORY_CRITICAL:
|
case B_LOW_MEMORY_CRITICAL:
|
||||||
steal = 500;
|
steal = 500;
|
||||||
score = -1;
|
score = -1;
|
||||||
|
timeout = 100000;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
//dprintf("page thief running - target %ld...\n", steal);
|
||||||
|
|
||||||
vm_page* page = NULL;
|
vm_page* page = NULL;
|
||||||
|
bool stealActive = false;
|
||||||
InterruptsSpinLocker locker(sPageLock);
|
InterruptsSpinLocker locker(sPageLock);
|
||||||
|
|
||||||
while (steal > 0) {
|
while (steal > 0) {
|
||||||
@@ -796,14 +810,29 @@ page_thief(void* /*unused*/, int32 level)
|
|||||||
page = dequeue_page(&sActivePageQueue);
|
page = dequeue_page(&sActivePageQueue);
|
||||||
enqueue_page(&sActivePageQueue, page);
|
enqueue_page(&sActivePageQueue, page);
|
||||||
|
|
||||||
if (page->state == PAGE_STATE_INACTIVE
|
if ((page->state == PAGE_STATE_INACTIVE
|
||||||
|
|| stealActive && page->state == PAGE_STATE_ACTIVE)
|
||||||
&& page->usage_count <= score)
|
&& page->usage_count <= score)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (page == NULL) {
|
if (page == NULL) {
|
||||||
if (score == 0)
|
if (score == 0) {
|
||||||
|
if (state != B_LOW_MEMORY_CRITICAL)
|
||||||
break;
|
break;
|
||||||
|
if (stealActive && score > 5)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// when memory is really low, we really want pages
|
||||||
|
if (stealActive)
|
||||||
|
score = 127;
|
||||||
|
else {
|
||||||
|
stealActive = true;
|
||||||
|
score = 5;
|
||||||
|
steal = 5;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
score = 0;
|
score = 0;
|
||||||
continue;
|
continue;
|
||||||
@@ -817,14 +846,20 @@ page_thief(void* /*unused*/, int32 level)
|
|||||||
if (cache == NULL)
|
if (cache == NULL)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mutex_trylock(&cache->lock) != B_OK
|
if (mutex_trylock(&cache->lock) != B_OK) {
|
||||||
|| page->state != PAGE_STATE_INACTIVE) {
|
vm_cache_release_ref(cache);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (page->state != PAGE_STATE_INACTIVE) {
|
||||||
|
mutex_unlock(&cache->lock);
|
||||||
vm_cache_release_ref(cache);
|
vm_cache_release_ref(cache);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we can now steal this page
|
// we can now steal this page
|
||||||
|
|
||||||
|
//dprintf(" steal page %p from cache %p\n", page, cache);
|
||||||
|
vm_remove_all_page_mappings(page);
|
||||||
vm_cache_remove_page(cache, page);
|
vm_cache_remove_page(cache, page);
|
||||||
vm_page_set_state(page, PAGE_STATE_FREE);
|
vm_page_set_state(page, PAGE_STATE_FREE);
|
||||||
steal--;
|
steal--;
|
||||||
@@ -834,6 +869,16 @@ page_thief(void* /*unused*/, int32 level)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return B_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
schedule_page_thief(void* /*unused*/, int32 level)
|
||||||
|
{
|
||||||
|
release_sem(sThiefWaitSem);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark - private kernel API
|
// #pragma mark - private kernel API
|
||||||
|
|
||||||
@@ -878,9 +923,7 @@ vm_page_write_modified_pages(vm_cache *cache, bool fsReenter)
|
|||||||
vm_clear_map_flags(page, PAGE_MODIFIED);
|
vm_clear_map_flags(page, PAGE_MODIFIED);
|
||||||
|
|
||||||
mutex_unlock(&cache->lock);
|
mutex_unlock(&cache->lock);
|
||||||
|
|
||||||
status_t status = write_page(page, fsReenter);
|
status_t status = write_page(page, fsReenter);
|
||||||
|
|
||||||
mutex_lock(&cache->lock);
|
mutex_lock(&cache->lock);
|
||||||
|
|
||||||
InterruptsSpinLocker locker(&sPageLock);
|
InterruptsSpinLocker locker(&sPageLock);
|
||||||
@@ -1032,20 +1075,25 @@ vm_page_init_post_thread(kernel_args *args)
|
|||||||
thread_id thread;
|
thread_id thread;
|
||||||
|
|
||||||
// create a kernel thread to clear out pages
|
// create a kernel thread to clear out pages
|
||||||
thread = spawn_kernel_thread(&page_scrubber, "page scrubber", B_LOWEST_ACTIVE_PRIORITY, NULL);
|
thread = spawn_kernel_thread(&page_scrubber, "page scrubber",
|
||||||
|
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>;
|
new (&sFreePageCondition) ConditionVariable<page_queue>;
|
||||||
sFreePageCondition.Publish(&sFreePageQueue, "free page");
|
sFreePageCondition.Publish(&sFreePageQueue, "free page");
|
||||||
|
|
||||||
register_low_memory_handler(page_thief, NULL, 0);
|
|
||||||
|
|
||||||
sWriterWaitSem = create_sem(0, "page writer");
|
sWriterWaitSem = create_sem(0, "page writer");
|
||||||
|
sThiefWaitSem = create_sem(0, "page thief");
|
||||||
|
|
||||||
thread = spawn_kernel_thread(&page_writer, "page writer",
|
thread = spawn_kernel_thread(&page_writer, "page writer",
|
||||||
B_LOW_PRIORITY, NULL);
|
B_LOW_PRIORITY, NULL);
|
||||||
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
||||||
|
|
||||||
|
thread = spawn_kernel_thread(&page_thief, "page thief",
|
||||||
|
B_LOW_PRIORITY, NULL);
|
||||||
|
send_signal_etc(thread, SIGCONT, B_DO_NOT_RESCHEDULE);
|
||||||
|
|
||||||
|
register_low_memory_handler(schedule_page_thief, NULL, 100);
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user