diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index 819b05138b..46c62aebcc 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -100,6 +100,7 @@ struct vm_page { uint8 is_cleared : 1; // is currently only used in vm_page_allocate_page_run() + uint8 busy_writing : 1; uint16 wired_count; int8 usage_count; diff --git a/src/system/kernel/vm/vm_cache.cpp b/src/system/kernel/vm/vm_cache.cpp index ca49520886..66ba8b5183 100644 --- a/src/system/kernel/vm/vm_cache.cpp +++ b/src/system/kernel/vm/vm_cache.cpp @@ -500,15 +500,23 @@ vm_cache_resize(vm_cache *cache, off_t newSize) if (page->cache_offset >= newPageCount) { if (page->state == PAGE_STATE_BUSY) { - // wait for page to become unbusy - ConditionVariableEntry entry; - entry.Add(page); - mutex_unlock(&cache->lock); - entry.Wait(); - mutex_lock(&cache->lock); + if (page->busy_writing) { + // We cannot wait for the page to become available + // as we might cause a deadlock this way + page->busy_writing = false; + // this will notify the writer to free the page + page = next; + } else { + // wait for page to become unbusy + ConditionVariableEntry entry; + entry.Add(page); + mutex_unlock(&cache->lock); + entry.Wait(); + mutex_lock(&cache->lock); - // restart from the start of the list - page = cache->page_list; + // restart from the start of the list + page = cache->page_list; + } continue; } diff --git a/src/system/kernel/vm/vm_page.cpp b/src/system/kernel/vm/vm_page.cpp index 489d6144fd..21d6154973 100644 --- a/src/system/kernel/vm/vm_page.cpp +++ b/src/system/kernel/vm/vm_page.cpp @@ -835,6 +835,9 @@ write_page(vm_page *page, bool fsReenter) page, page->cache_offset, status); } #endif + if (status == B_OK && length == 0) + status = B_ERROR; + return status; } @@ -960,6 +963,7 @@ page_writer(void* /*unused*/) InterruptsSpinLocker locker(sPageLock); remove_page_from_queue(&sModifiedPageQueue, page); page->state = PAGE_STATE_BUSY; + page->busy_writing = true; busyConditions[numPages].Publish(page, "page"); @@ -992,12 +996,21 @@ page_writer(void* /*unused*/) // put it into the active queue InterruptsSpinLocker locker(sPageLock); move_page_to_active_or_inactive_queue(pages[i], true); + pages[i]->busy_writing = false; } else { // We don't have to put the PAGE_MODIFIED bit back, as it's // still in the modified pages list. - InterruptsSpinLocker locker(sPageLock); - pages[i]->state = PAGE_STATE_MODIFIED; - enqueue_page(&sModifiedPageQueue, pages[i]); + { + InterruptsSpinLocker locker(sPageLock); + pages[i]->state = PAGE_STATE_MODIFIED; + enqueue_page(&sModifiedPageQueue, pages[i]); + } + if (!pages[i]->busy_writing) { + // someone has cleared the busy_writing flag which tells + // us our page has gone invalid + vm_cache_remove_page(cache, pages[i]); + } else + pages[i]->busy_writing = false; } busyConditions[i].Unpublish(); @@ -1246,6 +1259,7 @@ vm_page_write_modified_pages(vm_cache *cache, bool fsReenter) } page->state = PAGE_STATE_BUSY; + page->busy_writing = true; ConditionVariable busyCondition; busyCondition.Publish(page, "page"); @@ -1272,14 +1286,25 @@ vm_page_write_modified_pages(vm_cache *cache, bool fsReenter) if (status == B_OK) { // put it into the active/inactive queue move_page_to_active_or_inactive_queue(page, dequeuedPage); + page->busy_writing = false; } else { // We don't have to put the PAGE_MODIFIED bit back, as it's still // in the modified pages list. if (dequeuedPage) { page->state = PAGE_STATE_MODIFIED; enqueue_page(&sModifiedPageQueue, page); - } else - set_page_state_nolock(page, PAGE_STATE_MODIFIED); + } + + if (!page->busy_writing) { + // someone has cleared the busy_writing flag which tells + // us our page has gone invalid + vm_cache_remove_page(cache, page); + } else { + if (!dequeuedPage) + set_page_state_nolock(page, PAGE_STATE_MODIFIED); + + page->busy_writing = false; + } } busyCondition.Unpublish(); @@ -1345,6 +1370,7 @@ vm_page_init(kernel_args *args) new(&sPages[i].mappings) vm_page_mappings(); sPages[i].wired_count = 0; sPages[i].usage_count = 0; + sPages[i].busy_writing = false; sPages[i].cache = NULL; #ifdef DEBUG_PAGE_QUEUE sPages[i].queue = NULL;