From b62c71f3c9f8f6b02896d377b816ab23f2678ad1 Mon Sep 17 00:00:00 2001 From: Matheus Date: Wed, 22 Apr 2026 19:53:58 -0300 Subject: [PATCH] vmmcache: fix dirty list race --- kernel-src/include/kernel/pmm.h | 1 + kernel-src/mm/vmmcache.c | 21 +++++++++++++-------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/kernel-src/include/kernel/pmm.h b/kernel-src/include/kernel/pmm.h index fc5b9f6b..23b3d340 100644 --- a/kernel-src/include/kernel/pmm.h +++ b/kernel-src/include/kernel/pmm.h @@ -15,6 +15,7 @@ #define PAGE_FLAGS_DIRTY 8 #define PAGE_FLAGS_READY 16 #define PAGE_FLAGS_ERROR 32 +#define PAGE_FLAGS_VNODE_SYNCING 64 typedef struct page_t { struct vnode_t *backing; diff --git a/kernel-src/mm/vmmcache.c b/kernel-src/mm/vmmcache.c index e7a3494c..9a5f8988 100644 --- a/kernel-src/mm/vmmcache.c +++ b/kernel-src/mm/vmmcache.c @@ -338,7 +338,7 @@ int vmmcache_syncvnode(vnode_t *vnode, uintmax_t offset, size_t size) { page_t *page = vnode->pages; page_t *vnodedirtylist = NULL; for (; page; page = page->vnodenext) { - if (page->offset < offset || page->offset >= top || (page->flags & PAGE_FLAGS_DIRTY) == 0) + if (page->offset < offset || page->offset >= top || (page->flags & PAGE_FLAGS_DIRTY) == 0 || (page->flags & PAGE_FLAGS_VNODE_SYNCING)) continue; // remove from write list and add to an internal list using the write pointers @@ -355,6 +355,7 @@ int vmmcache_syncvnode(vnode_t *vnode, uintmax_t offset, size_t size) { page->writenext = vnodedirtylist; page->writeprev = NULL; + page->flags |= PAGE_FLAGS_VNODE_SYNCING; vnodedirtylist = page; } @@ -368,6 +369,7 @@ int vmmcache_syncvnode(vnode_t *vnode, uintmax_t offset, size_t size) { page_t *page = vnodedirtylist; vnodedirtylist = vnodedirtylist->writenext; page->writenext = NULL; + page->flags &= ~PAGE_FLAGS_VNODE_SYNCING; // another thread could already have synced this page, verify if it is still dirty if (page->flags & PAGE_FLAGS_DIRTY) { @@ -414,14 +416,17 @@ int vmmcache_makedirty(page_t *page) { // page is neither dirty nor truncated, add to dirty list and hold the page and vnode page->flags |= PAGE_FLAGS_DIRTY; - page->writeprev = NULL; - page->writenext = dirtylist; - if (dirtylist) - dirtylist->writeprev = page; - else - dirtylistend = page; + if ((page->flags & PAGE_FLAGS_VNODE_SYNCING) == 0) { + page->writeprev = NULL; + page->writenext = dirtylist; + if (dirtylist) + dirtylist->writeprev = page; + else + dirtylistend = page; + + dirtylist = page; + } - dirtylist = page; pmm_hold(pmm_getpageaddress(page)); __assert(page->backing); VOP_HOLD(page->backing);