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.
This commit is contained in:
Matheus
2026-10-06 10:47:05 -03:00
parent 0cdd03eba8
commit 6c65a90a5f
+17 -9
View File
@@ -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 writeable = arch_mmu_iswritable(old_context->pagetable, vaddr);
bool private = !(range->flags & MM_RANGE_FLAGS_SHARED); bool private = !(range->flags & MM_RANGE_FLAGS_SHARED);
if ((range->flags & MM_RANGE_FLAGS_FILE) && range->vnode->type == V_TYPE_CHDEV && !private) { // the semantics of device mappings in general are:
// TODO: implement fork semantics for device mappings properly. // - a device mapping always gets *fully* mapped on VOP_MMAP
if (arch_mmu_map(new_context->pagetable, phys, vaddr, new_range->mmuflags) == false) // - if a device mapping is private, it gets a valid
goto error; // 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; continue;
}
page_t *page = mm_get_page(phys); page_t *page = mm_get_page(phys);
if (mm_is_page_locked(page) && writeable && private) { 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); void *new_phys = mm_alloc_page(MEMORY_SECTION_DEFAULT);
if (new_phys == NULL) if (new_phys == NULL)
goto error; 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); mm_release_page(new_phys);
goto error; 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); memcpy(MAKE_HHDM(new_phys), MAKE_HHDM(phys), PAGE_SIZE);
} else { } else {
// go through the normal CoW path // 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; goto error;
mm_hold_page(phys); 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);
} }
} }