* 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
This commit is contained in:
@@ -17,6 +17,11 @@
|
|||||||
#include <sys/uio.h>
|
#include <sys/uio.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Enables the vm_page::queue, i.e. it is tracked which queue the page should
|
||||||
|
// be in.
|
||||||
|
//#define DEBUG_PAGE_QUEUE 1
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
struct vm_page_mapping;
|
struct vm_page_mapping;
|
||||||
typedef DoublyLinkedListLink<vm_page_mapping> vm_page_mapping_link;
|
typedef DoublyLinkedListLink<vm_page_mapping> vm_page_mapping_link;
|
||||||
@@ -83,6 +88,10 @@ typedef struct vm_page {
|
|||||||
|
|
||||||
vm_page_mappings mappings;
|
vm_page_mappings mappings;
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAGE_QUEUE
|
||||||
|
void* queue;
|
||||||
|
#endif
|
||||||
|
|
||||||
uint8 type : 2;
|
uint8 type : 2;
|
||||||
uint8 state : 3;
|
uint8 state : 3;
|
||||||
uint8 busy_reading : 1;
|
uint8 busy_reading : 1;
|
||||||
|
|||||||
@@ -72,6 +72,15 @@ dequeue_page(page_queue *q)
|
|||||||
|
|
||||||
q->tail = page->queue_prev;
|
q->tail = page->queue_prev;
|
||||||
q->count--;
|
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;
|
return page;
|
||||||
@@ -82,6 +91,13 @@ dequeue_page(page_queue *q)
|
|||||||
static void
|
static void
|
||||||
enqueue_page(page_queue *q, vm_page *page)
|
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)
|
if (q->head != NULL)
|
||||||
q->head->queue_prev = page;
|
q->head->queue_prev = page;
|
||||||
page->queue_next = q->head;
|
page->queue_next = q->head;
|
||||||
@@ -91,6 +107,10 @@ enqueue_page(page_queue *q, vm_page *page)
|
|||||||
q->tail = page;
|
q->tail = page;
|
||||||
q->count++;
|
q->count++;
|
||||||
|
|
||||||
|
#ifdef DEBUG_PAGE_QUEUE
|
||||||
|
page->queue = q;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (q == &page_modified_queue) {
|
if (q == &page_modified_queue) {
|
||||||
if (q->count == 1)
|
if (q->count == 1)
|
||||||
release_sem_etc(modified_pages_available, 1, B_DO_NOT_RESCHEDULE);
|
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
|
static void
|
||||||
remove_page_from_queue(page_queue *queue, vm_page *page)
|
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)
|
if (page->queue_prev != NULL)
|
||||||
page->queue_prev->queue_next = page->queue_next;
|
page->queue_prev->queue_next = page->queue_next;
|
||||||
else
|
else
|
||||||
@@ -112,6 +139,10 @@ remove_page_from_queue(page_queue *queue, vm_page *page)
|
|||||||
queue->tail = page->queue_prev;
|
queue->tail = page->queue_prev;
|
||||||
|
|
||||||
queue->count--;
|
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 <address>\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
|
static int
|
||||||
dump_page(int argc, char **argv)
|
dump_page(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@@ -189,6 +268,9 @@ dump_page(int argc, char **argv)
|
|||||||
kprintf("state: %d\n", page->state);
|
kprintf("state: %d\n", page->state);
|
||||||
kprintf("wired_count: %u\n", page->wired_count);
|
kprintf("wired_count: %u\n", page->wired_count);
|
||||||
kprintf("usage_count: %u\n", page->usage_count);
|
kprintf("usage_count: %u\n", page->usage_count);
|
||||||
|
#ifdef DEBUG_PAGE_QUEUE
|
||||||
|
kprintf("queue: %p\n", page->queue);
|
||||||
|
#endif
|
||||||
kprintf("area mappings:\n");
|
kprintf("area mappings:\n");
|
||||||
|
|
||||||
mapping = page->mappings;
|
mapping = page->mappings;
|
||||||
@@ -226,7 +308,7 @@ dump_page_queue(int argc, char **argv)
|
|||||||
return 0;
|
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) {
|
if (argc == 3) {
|
||||||
struct vm_page *page = queue->head;
|
struct vm_page *page = queue->head;
|
||||||
@@ -429,6 +511,7 @@ page_scrubber(void *unused)
|
|||||||
page[i] = dequeue_page(&page_free_queue);
|
page[i] = dequeue_page(&page_free_queue);
|
||||||
if (page[i] == NULL)
|
if (page[i] == NULL)
|
||||||
break;
|
break;
|
||||||
|
page[i]->state = PAGE_STATE_BUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
release_spinlock(&sPageLock);
|
release_spinlock(&sPageLock);
|
||||||
@@ -747,6 +830,9 @@ vm_page_init(kernel_args *args)
|
|||||||
sPages[i].wired_count = 0;
|
sPages[i].wired_count = 0;
|
||||||
sPages[i].usage_count = 0;
|
sPages[i].usage_count = 0;
|
||||||
sPages[i].cache = NULL;
|
sPages[i].cache = NULL;
|
||||||
|
#ifdef DEBUG_PAGE_QUEUE
|
||||||
|
sPages[i].queue = NULL;
|
||||||
|
#endif
|
||||||
enqueue_page(&page_free_queue, &sPages[i]);
|
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("free_pages", &dump_free_page_table, "Dump list of free pages");
|
||||||
add_debugger_command("page", &dump_page, "Dump page info");
|
add_debugger_command("page", &dump_page, "Dump page info");
|
||||||
add_debugger_command("page_queue", &dump_page_queue, "Dump page queue");
|
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;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user