* Renamed vm_page_write_modified() to vm_page_write_modifed_pages() and
introduced a new vm_page_write_modified_page(). * Resolved a TODO: vm_page_write_modified_pages() did not mark a to be written page busy but unlocked its cache which could let someone else steal that page in the mean time. * Moved the logic when to move a page to the active or inactive queue to a new function move_page_to_active_or_inactive_queue(). * Moved page_state_to_string() to vm_page(); it's now also used by the "page" and "page_queue" KDL commands. * Made the output of the "page_queue list" command more useful. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22323 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
20c8bc80d5
commit
8692ec023c
@ -32,7 +32,9 @@ status_t vm_page_set_state(vm_page *page, int state);
|
||||
size_t vm_page_num_pages(void);
|
||||
size_t vm_page_num_free_pages(void);
|
||||
|
||||
status_t vm_page_write_modified(vm_cache *cache, bool fsReenter);
|
||||
status_t vm_page_write_modified_page(vm_cache *cache, struct vm_page *page,
|
||||
bool fsReenter);
|
||||
status_t vm_page_write_modified_pages(vm_cache *cache, bool fsReenter);
|
||||
|
||||
void vm_page_unreserve_pages(uint32 count);
|
||||
void vm_page_reserve_pages(uint32 count);
|
||||
|
@ -50,6 +50,9 @@ void vm_unreserve_memory(size_t bytes);
|
||||
status_t vm_try_reserve_memory(size_t bytes);
|
||||
status_t vm_daemon_init(void);
|
||||
|
||||
const char *page_state_to_string(int state);
|
||||
// for debugging purposes only
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -2690,32 +2690,6 @@ display_mem(int argc, char **argv)
|
||||
}
|
||||
|
||||
|
||||
static const char *
|
||||
page_state_to_string(int state)
|
||||
{
|
||||
switch(state) {
|
||||
case PAGE_STATE_ACTIVE:
|
||||
return "active";
|
||||
case PAGE_STATE_INACTIVE:
|
||||
return "inactive";
|
||||
case PAGE_STATE_BUSY:
|
||||
return "busy";
|
||||
case PAGE_STATE_MODIFIED:
|
||||
return "modified";
|
||||
case PAGE_STATE_FREE:
|
||||
return "free";
|
||||
case PAGE_STATE_CLEAR:
|
||||
return "clear";
|
||||
case PAGE_STATE_WIRED:
|
||||
return "wired";
|
||||
case PAGE_STATE_UNUSED:
|
||||
return "unused";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_cache_tree_recursively(vm_cache* cache, int level,
|
||||
vm_cache* highlightCache)
|
||||
|
@ -443,7 +443,7 @@ vm_cache_write_modified(vm_cache *cache, bool fsReenter)
|
||||
return B_OK;
|
||||
|
||||
mutex_lock(&cache->lock);
|
||||
status = vm_page_write_modified(cache, fsReenter);
|
||||
status = vm_page_write_modified_pages(cache, fsReenter);
|
||||
mutex_unlock(&cache->lock);
|
||||
|
||||
return status;
|
||||
|
@ -217,6 +217,32 @@ find_page(int argc, char **argv)
|
||||
}
|
||||
|
||||
|
||||
const char *
|
||||
page_state_to_string(int state)
|
||||
{
|
||||
switch(state) {
|
||||
case PAGE_STATE_ACTIVE:
|
||||
return "active";
|
||||
case PAGE_STATE_INACTIVE:
|
||||
return "inactive";
|
||||
case PAGE_STATE_BUSY:
|
||||
return "busy";
|
||||
case PAGE_STATE_MODIFIED:
|
||||
return "modified";
|
||||
case PAGE_STATE_FREE:
|
||||
return "free";
|
||||
case PAGE_STATE_CLEAR:
|
||||
return "clear";
|
||||
case PAGE_STATE_WIRED:
|
||||
return "wired";
|
||||
case PAGE_STATE_UNUSED:
|
||||
return "unused";
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
dump_page(int argc, char **argv)
|
||||
{
|
||||
@ -269,9 +295,9 @@ dump_page(int argc, char **argv)
|
||||
kprintf("cache_offset: %ld\n", page->cache_offset);
|
||||
kprintf("cache_next,prev: %p, %p\n", page->cache_next, page->cache_prev);
|
||||
kprintf("type: %d\n", page->type);
|
||||
kprintf("state: %d\n", page->state);
|
||||
kprintf("wired_count: %u\n", page->wired_count);
|
||||
kprintf("usage_count: %u\n", page->usage_count);
|
||||
kprintf("state: %s\n", page_state_to_string(page->state));
|
||||
kprintf("wired_count: %d\n", page->wired_count);
|
||||
kprintf("usage_count: %d\n", page->usage_count);
|
||||
#ifdef DEBUG_PAGE_QUEUE
|
||||
kprintf("queue: %p\n", page->queue);
|
||||
#endif
|
||||
@ -323,11 +349,12 @@ dump_page_queue(int argc, char **argv)
|
||||
if (argc == 3) {
|
||||
struct vm_page *page = queue->head;
|
||||
int i;
|
||||
|
||||
|
||||
kprintf("page cache state wired usage\n");
|
||||
for (i = 0; page; i++, page = page->queue_next) {
|
||||
kprintf("%5d. queue_next = %p, queue_prev = %p, type = %d, "
|
||||
"state = %d\n", i, page->queue_next, page->queue_prev,
|
||||
page->type, page->state);
|
||||
kprintf("%p %p %8s %5d %5d\n", page, page->cache,
|
||||
page_state_to_string(page->state),
|
||||
page->wired_count, page->usage_count);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -352,14 +379,20 @@ dump_page_stats(int argc, char **argv)
|
||||
|
||||
kprintf("page stats:\n");
|
||||
kprintf("active: %lu\ninactive: %lu\nbusy: %lu\nunused: %lu\n",
|
||||
counter[PAGE_STATE_ACTIVE], counter[PAGE_STATE_INACTIVE], counter[PAGE_STATE_BUSY], counter[PAGE_STATE_UNUSED]);
|
||||
counter[PAGE_STATE_ACTIVE], counter[PAGE_STATE_INACTIVE],
|
||||
counter[PAGE_STATE_BUSY], counter[PAGE_STATE_UNUSED]);
|
||||
kprintf("wired: %lu\nmodified: %lu\nfree: %lu\nclear: %lu\n",
|
||||
counter[PAGE_STATE_WIRED], counter[PAGE_STATE_MODIFIED], counter[PAGE_STATE_FREE], counter[PAGE_STATE_CLEAR]);
|
||||
counter[PAGE_STATE_WIRED], counter[PAGE_STATE_MODIFIED],
|
||||
counter[PAGE_STATE_FREE], counter[PAGE_STATE_CLEAR]);
|
||||
|
||||
kprintf("\nfree_queue: %p, count = %ld\n", &sFreePageQueue, sFreePageQueue.count);
|
||||
kprintf("clear_queue: %p, count = %ld\n", &sClearPageQueue, sClearPageQueue.count);
|
||||
kprintf("modified_queue: %p, count = %ld\n", &sModifiedPageQueue, sModifiedPageQueue.count);
|
||||
kprintf("active_queue: %p, count = %ld\n", &sActivePageQueue, sActivePageQueue.count);
|
||||
kprintf("\nfree_queue: %p, count = %ld\n", &sFreePageQueue,
|
||||
sFreePageQueue.count);
|
||||
kprintf("clear_queue: %p, count = %ld\n", &sClearPageQueue,
|
||||
sClearPageQueue.count);
|
||||
kprintf("modified_queue: %p, count = %ld\n", &sModifiedPageQueue,
|
||||
sModifiedPageQueue.count);
|
||||
kprintf("active_queue: %p, count = %ld\n", &sActivePageQueue,
|
||||
sActivePageQueue.count);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -439,7 +472,9 @@ set_page_state_nolock(vm_page *page, int pageState)
|
||||
fromQueue = &sClearPageQueue;
|
||||
break;
|
||||
default:
|
||||
panic("vm_page_set_state: vm_page %p in invalid state %d\n", page, page->state);
|
||||
panic("vm_page_set_state: vm_page %p in invalid state %d\n",
|
||||
page, page->state);
|
||||
break;
|
||||
}
|
||||
|
||||
if (page->state == PAGE_STATE_CLEAR || page->state == PAGE_STATE_FREE) {
|
||||
@ -483,6 +518,25 @@ set_page_state_nolock(vm_page *page, int pageState)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
move_page_to_active_or_inactive_queue(vm_page *page, bool dequeued)
|
||||
{
|
||||
// Note, this logic must be in sync with what the page daemon does
|
||||
int32 state;
|
||||
if (!page->mappings.IsEmpty() || page->usage_count >= 0
|
||||
|| page->wired_count)
|
||||
state = PAGE_STATE_ACTIVE;
|
||||
else
|
||||
state = PAGE_STATE_INACTIVE;
|
||||
|
||||
if (dequeued) {
|
||||
page->state = state;
|
||||
enqueue_page(&sActivePageQueue, page);
|
||||
} else
|
||||
set_page_state_nolock(page, state);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
clear_page(addr_t pa)
|
||||
{
|
||||
@ -751,9 +805,69 @@ page_thief(void* /*unused*/, int32 level)
|
||||
|
||||
/*!
|
||||
You need to hold the vm_cache lock when calling this function.
|
||||
And \a page must obviously be in that cache.
|
||||
Note that the cache lock is released in this function.
|
||||
*/
|
||||
status_t
|
||||
vm_page_write_modified(vm_cache *cache, bool fsReenter)
|
||||
vm_page_write_modified_page(vm_cache *cache, vm_page *page, bool fsReenter)
|
||||
{
|
||||
ASSERT(page->state == PAGE_STATE_MODIFIED);
|
||||
ASSERT(page->cache == cache);
|
||||
|
||||
ConditionVariable<vm_page> busyCondition;
|
||||
InterruptsSpinLocker locker(&sPageLock);
|
||||
|
||||
remove_page_from_queue(&sModifiedPageQueue, page);
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
|
||||
busyCondition.Publish(page, "page");
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
off_t pageOffset = (off_t)page->cache_offset << PAGE_SHIFT;
|
||||
|
||||
for (vm_area *area = cache->areas; area; area = area->cache_next) {
|
||||
if (pageOffset >= area->cache_offset
|
||||
&& pageOffset < area->cache_offset + area->size) {
|
||||
vm_translation_map *map = &area->address_space->translation_map;
|
||||
// clear the modified flag
|
||||
map->ops->lock(map);
|
||||
map->ops->clear_flags(map, pageOffset - area->cache_offset
|
||||
+ area->base, PAGE_MODIFIED);
|
||||
map->ops->unlock(map);
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&cache->lock);
|
||||
|
||||
status_t status = write_page(page, fsReenter);
|
||||
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
locker.Lock();
|
||||
|
||||
if (status == B_OK) {
|
||||
// put it into the active queue
|
||||
move_page_to_active_or_inactive_queue(page, true);
|
||||
} else {
|
||||
// We don't have to put the PAGE_MODIFIED bit back, as it's still
|
||||
// in the modified pages list.
|
||||
page->state = PAGE_STATE_MODIFIED;
|
||||
enqueue_page(&sModifiedPageQueue, page);
|
||||
}
|
||||
|
||||
busyCondition.Unpublish();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*!
|
||||
You need to hold the vm_cache lock when calling this function.
|
||||
Note that the cache lock is released in this function.
|
||||
*/
|
||||
status_t
|
||||
vm_page_write_modified_pages(vm_cache *cache, bool fsReenter)
|
||||
{
|
||||
vm_page *page = cache->page_list;
|
||||
|
||||
@ -805,7 +919,8 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
|
||||
map->ops->query(map, pageOffset - area->cache_offset + area->base,
|
||||
&physicalAddress, &flags);
|
||||
if (flags & PAGE_MODIFIED) {
|
||||
// TODO: Mark busy?
|
||||
page->state = PAGE_STATE_BUSY;
|
||||
busyCondition.Publish(page, "page");
|
||||
gotPage = true;
|
||||
dequeuedPage = false;
|
||||
}
|
||||
@ -829,39 +944,32 @@ vm_page_write_modified(vm_cache *cache, bool fsReenter)
|
||||
mutex_lock(&cache->lock);
|
||||
|
||||
if (status == B_OK) {
|
||||
if (dequeuedPage) {
|
||||
// put it into the active queue
|
||||
// put it into the active/inactive queue
|
||||
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
|
||||
if (!page->mappings.IsEmpty() || page->wired_count)
|
||||
page->state = PAGE_STATE_ACTIVE;
|
||||
else
|
||||
page->state = PAGE_STATE_INACTIVE;
|
||||
move_page_to_active_or_inactive_queue(page, dequeuedPage);
|
||||
busyCondition.Unpublish();
|
||||
|
||||
busyCondition.Unpublish();
|
||||
|
||||
enqueue_page(&sActivePageQueue, page);
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
}
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
} else {
|
||||
// We don't have to put the PAGE_MODIFIED bit back, as it's still
|
||||
// in the modified pages list.
|
||||
if (dequeuedPage) {
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
state = disable_interrupts();
|
||||
acquire_spinlock(&sPageLock);
|
||||
|
||||
if (dequeuedPage) {
|
||||
page->state = PAGE_STATE_MODIFIED;
|
||||
enqueue_page(&sModifiedPageQueue, page);
|
||||
} else
|
||||
set_page_state_nolock(page, PAGE_STATE_MODIFIED);
|
||||
|
||||
busyCondition.Unpublish();
|
||||
busyCondition.Unpublish();
|
||||
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
}
|
||||
release_spinlock(&sPageLock);
|
||||
restore_interrupts(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user