From 27d37d4d245fd5ab042d89b393d76876ffb66f28 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Fri, 30 Mar 2007 19:48:51 +0000 Subject: [PATCH] * Optional debug feature for tracking which page queue a page should be in. * New debugger command "find_page", which searches all page queues to find out, which one a page is actually in. * Solved nasty race condition between the page scrubber and vm_page_allocate_page_run(): The page scrubber didn't mark the pages it was processing busy, so that vm_page_allocate_page_run() could claim them in the meantime. They would end up in the clear pages queue, although being assigned to a cache at the same time. This should finally solve bug #1056. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@20474 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/vm_types.h | 9 ++++ src/system/kernel/vm/vm_page.c | 90 ++++++++++++++++++++++++++++++- 2 files changed, 98 insertions(+), 1 deletion(-) diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index 1ad1049f9f..722d7e5dd9 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -17,6 +17,11 @@ #include +// Enables the vm_page::queue, i.e. it is tracked which queue the page should +// be in. +//#define DEBUG_PAGE_QUEUE 1 + + #ifdef __cplusplus struct vm_page_mapping; typedef DoublyLinkedListLink vm_page_mapping_link; @@ -83,6 +88,10 @@ typedef struct vm_page { vm_page_mappings mappings; + #ifdef DEBUG_PAGE_QUEUE + void* queue; + #endif + uint8 type : 2; uint8 state : 3; uint8 busy_reading : 1; diff --git a/src/system/kernel/vm/vm_page.c b/src/system/kernel/vm/vm_page.c index fc47d6cf10..8bfbee4932 100644 --- a/src/system/kernel/vm/vm_page.c +++ b/src/system/kernel/vm/vm_page.c @@ -72,6 +72,15 @@ dequeue_page(page_queue *q) q->tail = page->queue_prev; q->count--; + + #ifdef DEBUG_PAGE_QUEUE + if (page->queue != q) { + panic("dequeue_page(queue: %p): page %p thinks it is in queue " + "%p", q, page, page->queue); + } + + page->queue = NULL; + #endif // DEBUG_PAGE_QUEUE } return page; @@ -82,6 +91,13 @@ dequeue_page(page_queue *q) static void enqueue_page(page_queue *q, vm_page *page) { + #ifdef DEBUG_PAGE_QUEUE + if (page->queue != NULL) { + panic("enqueue_page(queue: %p, page: %p): page thinks it is " + "already in queue %p", q, page, page->queue); + } + #endif // DEBUG_PAGE_QUEUE + if (q->head != NULL) q->head->queue_prev = page; page->queue_next = q->head; @@ -91,6 +107,10 @@ enqueue_page(page_queue *q, vm_page *page) q->tail = page; q->count++; + #ifdef DEBUG_PAGE_QUEUE + page->queue = q; + #endif + if (q == &page_modified_queue) { if (q->count == 1) release_sem_etc(modified_pages_available, 1, B_DO_NOT_RESCHEDULE); @@ -101,6 +121,13 @@ enqueue_page(page_queue *q, vm_page *page) static void remove_page_from_queue(page_queue *queue, vm_page *page) { + #ifdef DEBUG_PAGE_QUEUE + if (page->queue != queue) { + panic("remove_page_from_queue(queue: %p, page: %p): page thinks it " + "is in queue %p", queue, page, page->queue); + } + #endif // DEBUG_PAGE_QUEUE + if (page->queue_prev != NULL) page->queue_prev->queue_next = page->queue_next; else @@ -112,6 +139,10 @@ remove_page_from_queue(page_queue *queue, vm_page *page) queue->tail = page->queue_prev; queue->count--; + + #ifdef DEBUG_PAGE_QUEUE + page->queue = NULL; + #endif } @@ -133,6 +164,54 @@ dump_free_page_table(int argc, char **argv) } +static int +find_page(int argc, char **argv) +{ + struct vm_page *page; + addr_t address; + int32 index = 1; + int i; + + struct { + const char* name; + page_queue* queue; + } pageQueueInfos[] = { + { "free", &page_free_queue }, + { "clear", &page_clear_queue }, + { "modified", &page_modified_queue }, + { "active", &page_active_queue }, + { NULL, NULL } + }; + + if (argc < 2 + || strlen(argv[index]) <= 2 + || argv[index][0] != '0' + || argv[index][1] != 'x') { + kprintf("usage: find_page
\n"); + return 0; + } + + address = strtoul(argv[index], NULL, 0); + page = (vm_page*)address; + + for (i = 0; pageQueueInfos[i].name; i++) { + vm_page* p = pageQueueInfos[i].queue->head; + while (p) { + if (p == page) { + kprintf("found page %p in queue %p (%s)\n", page, + pageQueueInfos[i].queue, pageQueueInfos[i].name); + return 0; + } + p = p->queue_next; + } + } + + kprintf("page %p isn't in any queue\n", page); + + return 0; +} + + static int dump_page(int argc, char **argv) { @@ -189,6 +268,9 @@ dump_page(int argc, char **argv) kprintf("state: %d\n", page->state); kprintf("wired_count: %u\n", page->wired_count); kprintf("usage_count: %u\n", page->usage_count); + #ifdef DEBUG_PAGE_QUEUE + kprintf("queue: %p\n", page->queue); + #endif kprintf("area mappings:\n"); mapping = page->mappings; @@ -226,7 +308,7 @@ dump_page_queue(int argc, char **argv) return 0; } - kprintf("queue->head = %p, queue->tail = %p, queue->count = %d\n", queue->head, queue->tail, queue->count); + kprintf("queue = %p, queue->head = %p, queue->tail = %p, queue->count = %d\n", queue, queue->head, queue->tail, queue->count); if (argc == 3) { struct vm_page *page = queue->head; @@ -429,6 +511,7 @@ page_scrubber(void *unused) page[i] = dequeue_page(&page_free_queue); if (page[i] == NULL) break; + page[i]->state = PAGE_STATE_BUSY; } release_spinlock(&sPageLock); @@ -747,6 +830,9 @@ vm_page_init(kernel_args *args) sPages[i].wired_count = 0; sPages[i].usage_count = 0; sPages[i].cache = NULL; + #ifdef DEBUG_PAGE_QUEUE + sPages[i].queue = NULL; + #endif enqueue_page(&page_free_queue, &sPages[i]); } @@ -778,6 +864,8 @@ vm_page_init_post_area(kernel_args *args) add_debugger_command("free_pages", &dump_free_page_table, "Dump list of free pages"); add_debugger_command("page", &dump_page, "Dump page info"); add_debugger_command("page_queue", &dump_page_queue, "Dump page queue"); + add_debugger_command("find_page", &find_page, + "Find out which queue a page is actually in"); return B_OK; }