From 9d845483cf2ced7587f734aa082b3c001795dca2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Mon, 19 Dec 2005 23:01:11 +0000 Subject: [PATCH] Got rid of two ToDo-items: instead of clearing the PAGE_MODIFIED flag after having written the page, we now do it before, so that it cannot lose any changed data anymore; it doesn't matter if the page is written to while writing it back, the worst thing that can happen is that we write the same page twice. Also, we don't rely on the PAGE_MODIFIED bit anymore, we now check all mappings of that page to find all modified pages, no matter how far the (currently disabled) page daemon had come. Also, destroying an area will now result in writing back changed pages - this is only really important for memory mapped files, though, and should probably be avoided for other vm_store types. Minor cleanup. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@15597 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/vm_types.h | 8 ++-- src/system/kernel/vm/vm.cpp | 7 +++- src/system/kernel/vm/vm_page.c | 62 +++++++++++++++++++------------ 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/headers/private/kernel/vm_types.h b/headers/private/kernel/vm_types.h index 28f34f266d..f0bdae1367 100644 --- a/headers/private/kernel/vm_types.h +++ b/headers/private/kernel/vm_types.h @@ -91,11 +91,11 @@ typedef struct vm_area { off_t cache_offset; struct vm_address_space *aspace; - struct vm_area *aspace_next; + struct vm_area *aspace_next; struct vm_virtual_map *map; - struct vm_area *cache_next; - struct vm_area *cache_prev; - struct vm_area *hash_next; + struct vm_area *cache_next; + struct vm_area *cache_prev; + struct vm_area *hash_next; } vm_area; // virtual map (1 per address space) diff --git a/src/system/kernel/vm/vm.cpp b/src/system/kernel/vm/vm.cpp index a7444b6c5b..5e95a37756 100644 --- a/src/system/kernel/vm/vm.cpp +++ b/src/system/kernel/vm/vm.cpp @@ -832,7 +832,8 @@ vm_create_anonymous_area(aspace_id aid, const char *name, void **address, switch (wiring) { case B_NO_LOCK: case B_LAZY_LOCK: - break; // do nothing + // do nothing - the pages are mapped in as needed + break; case B_FULL_LOCK: { @@ -893,6 +894,8 @@ vm_create_anonymous_area(aspace_id aid, const char *name, void **address, case B_CONTIGUOUS: { + // We have already allocated our continuous pages run, so we can now just + // map them in the address space addr_t physicalAddress = page->ppn * B_PAGE_SIZE; addr_t virtualAddress; off_t offset = 0; @@ -1338,6 +1341,8 @@ _vm_put_area(vm_area *area, bool aspaceLocked) aspace = area->aspace; + vm_cache_write_modified(area->cache_ref); + arch_vm_unset_memory_type(area); remove_area_from_virtual_map(aspace, area, aspaceLocked); diff --git a/src/system/kernel/vm/vm_page.c b/src/system/kernel/vm/vm_page.c index 466f774ec3..0223471b71 100644 --- a/src/system/kernel/vm/vm_page.c +++ b/src/system/kernel/vm/vm_page.c @@ -163,52 +163,63 @@ vm_page_write_modified(vm_cache *cache) // ToDo: join adjacent pages into one vec list for (; page; page = page->cache_next) { - status_t status; bool gotPage = false; + status_t status; + vm_area *area; cpu_status state = disable_interrupts(); acquire_spinlock(&page_lock); - // ToDo: if this is a memory mapped page, the PAGE_MODIFIED bit might - // not yet have been propagated to the page state! - if (page->state == PAGE_STATE_MODIFIED) { remove_page_from_queue(&page_modified_queue, page); page->state = PAGE_STATE_BUSY; gotPage = true; - - // ToDo: just setting PAGE_STAGE_BUSY is not enough, we would also - // need to remove all mappings of this page - else, you could still - // write to this page. } release_spinlock(&page_lock); restore_interrupts(state); + // We may have a modified page - however, while we're writing it back, the page + // is still mapped. In order not to lose any changes to the page, we mark it clean + // before actually writing it back; if writing the page fails for some reason, we + // just keep it in the modified page list, but that should happen only rarely. + + // If the page is changed after we cleared the dirty flag, but before we had + // the chance to write it back, then we'll write it again later - that will + // probably not happen that often, though. + + for (area = page->cache->ref->areas; area; area = area->cache_next) { + if (page->offset >= area->cache_offset + && page->offset < area->cache_offset + area->size) { + vm_translation_map *map = &area->aspace->translation_map; + map->ops->lock(map); + + if (!gotPage) { + // Check if the PAGE_MODIFIED bit hasn't been propagated yet + addr_t physicalAddress; + uint32 flags; + map->ops->query(map, page->offset - area->cache_offset + area->base, + &physicalAddress, &flags); + if (flags & PAGE_MODIFIED) + gotPage = true; + } + if (gotPage) { + // clear the modified flag + map->ops->clear_flags(map, page->offset - area->cache_offset + + area->base, PAGE_MODIFIED); + } + map->ops->unlock(map); + } + } + if (!gotPage) continue; - // got modified page, let's write it back - mutex_unlock(&cache->ref->lock); status = write_page(page); mutex_lock(&cache->ref->lock); if (status == B_OK) { - vm_area *area; - - // It's written back now, so we can clear the modified flag in all mappings - for (area = page->cache->ref->areas; area; area = area->cache_next) { - if (page->offset >= area->cache_offset - && page->offset < area->cache_offset + area->size) { - vm_translation_map *map = &area->aspace->translation_map; - map->ops->lock(map); - map->ops->clear_flags(map, page->offset - area->cache_offset + area->base, - PAGE_MODIFIED); - map->ops->unlock(map); - } - } - // put it into the active queue state = disable_interrupts(); @@ -223,6 +234,9 @@ vm_page_write_modified(vm_cache *cache) release_spinlock(&page_lock); restore_interrupts(state); + } else { + // We don't have to put the PAGE_MODIFIED bit back, as it's still + // in the modified pages list. } }