From 6c65a90a5f618ee11e54d84e42eb1236af524556 Mon Sep 17 00:00:00 2001 From: Matheus Date: Tue, 6 Oct 2026 10:47:05 -0300 Subject: [PATCH] mm: properly fork shared file mappings mm_fork_context() currently maps shared chdev mappings 1:1 when forking a range. The issue with this approach is that chdev mappings are not guaranteed to be backed by physical memory, and thus cannot safely be refcounted. This can lead to issues on devices with more complex buffer lifetimes. To allow for internal book-keeping, do not map shared chdev memory on fork and instead allow it to go through the entire fault-in process again. --- kernel-src/mm/mm.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/kernel-src/mm/mm.c b/kernel-src/mm/mm.c index 158382f1..7a26ccd7 100644 --- a/kernel-src/mm/mm.c +++ b/kernel-src/mm/mm.c @@ -590,21 +590,27 @@ mm_context_t *mm_fork_context(mm_context_t *old_context) { bool writeable = arch_mmu_iswritable(old_context->pagetable, vaddr); bool private = !(range->flags & MM_RANGE_FLAGS_SHARED); - if ((range->flags & MM_RANGE_FLAGS_FILE) && range->vnode->type == V_TYPE_CHDEV && !private) { - // TODO: implement fork semantics for device mappings properly. - if (arch_mmu_map(new_context->pagetable, phys, vaddr, new_range->mmuflags) == false) - goto error; + // the semantics of device mappings in general are: + // - a device mapping always gets *fully* mapped on VOP_MMAP + // - if a device mapping is private, it gets a valid + // physical page and goes through the normal fork path. + // - if a device mapping is shared, it does not have a valid + // physical page and must have VOP_MMAP called for it again. + // defer that until its actually accessed in a new page fault + if ((range->flags & MM_RANGE_FLAGS_FILE) && + range->vnode->type == V_TYPE_CHDEV && !private) continue; - } page_t *page = mm_get_page(phys); if (mm_is_page_locked(page) && writeable && private) { - // the page is a private locked page, eagerly copy it to ensure fork/futex correctness + // the page is a private locked page, eagerly copy it + // to ensure fork/futex correctness void *new_phys = mm_alloc_page(MEMORY_SECTION_DEFAULT); if (new_phys == NULL) goto error; - if (arch_mmu_map(new_context->pagetable, new_phys, vaddr, new_range->mmuflags) == false) { + if (arch_mmu_map(new_context->pagetable, new_phys, vaddr, + new_range->mmuflags) == false) { mm_release_page(new_phys); goto error; } @@ -612,12 +618,14 @@ mm_context_t *mm_fork_context(mm_context_t *old_context) { memcpy(MAKE_HHDM(new_phys), MAKE_HHDM(phys), PAGE_SIZE); } else { // go through the normal CoW path - if (arch_mmu_map(new_context->pagetable, phys, vaddr, new_range->mmuflags & ~ARCH_MMU_FLAGS_WRITE) == false) + if (arch_mmu_map(new_context->pagetable, phys, vaddr, + new_range->mmuflags & ~ARCH_MMU_FLAGS_WRITE) == false) goto error; mm_hold_page(phys); - arch_mmu_remap(old_context->pagetable, phys, vaddr, new_range->mmuflags & ~ARCH_MMU_FLAGS_WRITE); + arch_mmu_remap(old_context->pagetable, phys, vaddr, + new_range->mmuflags & ~ARCH_MMU_FLAGS_WRITE); } }