Motivation
Issue #2181 can be solved with DragonOS's existing AnonSharedMapping, but that fix also exposes a broader architectural gap: DragonOS does not yet model shared anonymous memory as an internal, unlinked shmem file.
Linux 6.6 uses one common model:
MAP_SHARED | MAP_ANONYMOUS is converted to an internal shmem file by shmem_zero_setup() in mmap_region().
/dev/zero + MAP_SHARED calls the same shmem_zero_setup() path from mmap_zero().
shmem_zero_setup() creates a new unlinked tmpfs inode/file for each independent mmap and replaces vma->vm_file with it.
- VMAs derived through fork, split, or mremap retain the same file identity and page cache, while a separate mmap receives a different identity.
References:
DragonOS currently has two separate representations:
AnonSharedMapping in kernel/src/mm/ucontext/vma.rs, with its own ID, page hash map, allocation race handling, backing pins, and destruction logic. It is used by shared anonymous mmap.
TmpfsShmemFile in kernel/src/filesystem/tmpfs/mod.rs, backed by the normal shmem PageCache, an unlinked tmpfs inode, inode identity, accounting, and file operations. It is currently used by System V SHM.
Supporting #2181 with the first representation requires a hybrid VMA that retains the original /dev/zero vm_file while also carrying shared_anon. Generic MM consumers must then know which identity is authoritative:
- fault and fault-around dispatch
- shared futex key generation
mincore() residency
msync()
- VMA split/mremap bookkeeping
/proc/<pid>/maps
This duplicates backing and lifetime logic and makes it easy for a new VMA consumer to accidentally use the wrong identity. It also cannot naturally expose Linux's internal unlinked-file identity, such as the /dev/zero (deleted) maps entry.
Proposed feature
Introduce a small, general internal-shmem-file abstraction on top of the existing DragonOS tmpfs and shmem PageCache. This should be a refactoring and completion of the existing TmpfsShmemFile path, not a new shmem filesystem.
The resulting constructor should conceptually provide:
- a new unlinked tmpfs regular inode for each requested object;
- a stable inode/file identity and a fixed initial size;
- a caller-provided diagnostic name, such as
dev/zero or a System V SHM name;
- a shmem
PageCache as the single page-content authority;
- correct reservation/quota accounting and rollback on creation failure;
- lifetime ownership tied to the internal file/inode, so accounting cannot be released while a VMA still retains the file;
- an
Arc<File> that can be installed as the VMA's authoritative vm_file without publishing a pathname or userspace fd.
The mmap contract should allow an inode mmap implementation to replace the original mapping file with the internal file after normal access checks, analogous to Linux's mmap_zero() -> shmem_zero_setup(). Generic MM code should not downcast /dev/zero or contain device-specific behavior.
Intended migration
- Generalize the existing unlinked tmpfs/shmem constructor and make its ownership/accounting safe for direct VMA retention.
- Back
MAP_SHARED | MAP_ANONYMOUS with a newly created internal shmem file.
- Back each independent
/dev/zero + MAP_SHARED mmap with its own internal file; keep MAP_PRIVATE behavior unchanged.
- Reuse the same underlying constructor for System V SHM while preserving IPC permissions,
IPC_RMID, attach accounting, and SHM_LOCK responsibilities in the IPC layer.
- Route page faults, futex identity,
mincore(), msync(), split, fork, and mremap through the ordinary file/page-cache paths.
- Remove
AnonSharedMapping and all hybrid vm_file + shared_anon special cases only after every consumer has migrated.
- Teach procfs to derive the diagnostic name and deleted state from the real unlinked internal inode. Do not hard-code
/dev/zero (deleted) in procfs.
The VMA must have one authoritative backing identity at the end of the migration. Keeping both representations indefinitely would preserve the current ambiguity rather than solve it.
Required invariants
- One independent mmap creates one internal object.
- Forked, split, and remapped descendants retain the same object and page offsets.
- Two mmap calls do not share contents, even when they use the same
/dev/zero fd.
- Page creation for a given offset is race-safe and does not allocate or sleep while holding an IRQ-disabled spinlock.
- Sparse mappings remain lazy; fault-around maps only already-resident neighboring pages.
- The last file/VMA reference releases cached pages and accounting exactly once.
- VMA merging must require matching internal file identity and contiguous page offsets.
- Access checks apply to the originating operation before an internal private inode replaces the visible file.
- No internal object becomes reachable through pathname lookup unless a separate feature explicitly exposes it.
Acceptance criteria
- A reusable internal shmem file constructor exists in the tmpfs/shmem layer.
- Shared anonymous mmap and shared
/dev/zero use ordinary vm_file plus shmem page-cache semantics, without a parallel shared_anon identity.
- System V SHM uses the same low-level internal-shmem creation path or has a documented reason for retaining only IPC-specific wrapper state.
AnonSharedMapping and its consumer-specific branches are removed.
/proc/<pid>/maps reports a real internal unlinked identity rather than a synthetic string workaround.
- Regression tests cover:
- lazy faults across fork;
- independent mappings through the same and different
/dev/zero fds;
- shared anonymous mmap;
- split and mremap page-offset continuity;
- futex wait/wake identity;
mincore() and msync();
- sparse fault-around behavior;
- final-reference page/accounting release;
- existing System V SHM lifecycle and
SHM_LOCK behavior.
- Relevant DragonOS dunitests and Linux/gVisor mmap tests pass in a DragonOS QEMU guest.
Non-goals
This feature should not grow into a full Linux shmem rewrite. The following are explicitly out of scope unless separately requested:
- swap support;
- transparent huge pages;
- NUMA policy;
- file seals;
memfd_create() syscall implementation;
- tmpfs quota feature expansion beyond the accounting needed by existing objects.
A unified internal file is a useful future foundation for memfd_create(), but implementing memfd or seals in this issue would be premature and unnecessarily enlarge the regression surface.
Why this should be separate from #2181
#2181 is a concrete Linux-compatibility bug and should remain narrowly deliverable. Replacing the backing representation affects shared-anonymous mmap, tmpfs/page cache, VFS mmap ownership, procfs, futex, and System V SHM. Tracking that architectural migration separately allows #2181 to land without forcing a broad refactor, while making the intended long-term direction explicit.
Motivation
Issue #2181 can be solved with DragonOS's existing
AnonSharedMapping, but that fix also exposes a broader architectural gap: DragonOS does not yet model shared anonymous memory as an internal, unlinked shmem file.Linux 6.6 uses one common model:
MAP_SHARED | MAP_ANONYMOUSis converted to an internal shmem file byshmem_zero_setup()inmmap_region()./dev/zero + MAP_SHAREDcalls the sameshmem_zero_setup()path frommmap_zero().shmem_zero_setup()creates a new unlinked tmpfs inode/file for each independent mmap and replacesvma->vm_filewith it.References:
mmap_zero()mmap_region()shared-anonymous path__shmem_file_setup()andshmem_zero_setup()DragonOS currently has two separate representations:
AnonSharedMappinginkernel/src/mm/ucontext/vma.rs, with its own ID, page hash map, allocation race handling, backing pins, and destruction logic. It is used by shared anonymous mmap.TmpfsShmemFileinkernel/src/filesystem/tmpfs/mod.rs, backed by the normal shmemPageCache, an unlinked tmpfs inode, inode identity, accounting, and file operations. It is currently used by System V SHM.Supporting #2181 with the first representation requires a hybrid VMA that retains the original
/dev/zerovm_filewhile also carryingshared_anon. Generic MM consumers must then know which identity is authoritative:mincore()residencymsync()/proc/<pid>/mapsThis duplicates backing and lifetime logic and makes it easy for a new VMA consumer to accidentally use the wrong identity. It also cannot naturally expose Linux's internal unlinked-file identity, such as the
/dev/zero (deleted)maps entry.Proposed feature
Introduce a small, general internal-shmem-file abstraction on top of the existing DragonOS tmpfs and shmem
PageCache. This should be a refactoring and completion of the existingTmpfsShmemFilepath, not a new shmem filesystem.The resulting constructor should conceptually provide:
dev/zeroor a System V SHM name;PageCacheas the single page-content authority;Arc<File>that can be installed as the VMA's authoritativevm_filewithout publishing a pathname or userspace fd.The mmap contract should allow an inode mmap implementation to replace the original mapping file with the internal file after normal access checks, analogous to Linux's
mmap_zero() -> shmem_zero_setup(). Generic MM code should not downcast/dev/zeroor contain device-specific behavior.Intended migration
MAP_SHARED | MAP_ANONYMOUSwith a newly created internal shmem file./dev/zero + MAP_SHAREDmmap with its own internal file; keepMAP_PRIVATEbehavior unchanged.IPC_RMID, attach accounting, andSHM_LOCKresponsibilities in the IPC layer.mincore(),msync(), split, fork, and mremap through the ordinary file/page-cache paths.AnonSharedMappingand all hybridvm_file + shared_anonspecial cases only after every consumer has migrated./dev/zero (deleted)in procfs.The VMA must have one authoritative backing identity at the end of the migration. Keeping both representations indefinitely would preserve the current ambiguity rather than solve it.
Required invariants
/dev/zerofd.Acceptance criteria
/dev/zerouse ordinaryvm_fileplus shmem page-cache semantics, without a parallelshared_anonidentity.AnonSharedMappingand its consumer-specific branches are removed./proc/<pid>/mapsreports a real internal unlinked identity rather than a synthetic string workaround./dev/zerofds;mincore()andmsync();SHM_LOCKbehavior.Non-goals
This feature should not grow into a full Linux shmem rewrite. The following are explicitly out of scope unless separately requested:
memfd_create()syscall implementation;A unified internal file is a useful future foundation for
memfd_create(), but implementing memfd or seals in this issue would be premature and unnecessarily enlarge the regression surface.Why this should be separate from #2181
#2181 is a concrete Linux-compatibility bug and should remain narrowly deliverable. Replacing the backing representation affects shared-anonymous mmap, tmpfs/page cache, VFS mmap ownership, procfs, futex, and System V SHM. Tracking that architectural migration separately allows #2181 to land without forcing a broad refactor, while making the intended long-term direction explicit.