mm: implement mremap(2) - #1432
Conversation
da653f5 to
68e7e9b
Compare
There was a problem hiding this comment.
Pull request overview
Adds initial support for Linux mremap(2) in OSv, wiring it end-to-end (MMU implementation → syscall plumbing → libc entry points) and introducing a dedicated test to validate expected grow/shrink/move behaviors.
Changes:
- Implement
mmu::mremap()using existing VMA primitives, supporting shrink, in-place grow, and MAYMOVE-based relocation (withMREMAP_FIXEDrejected). - Wire
SYS_mremapvialong_mremap, add a syscall tracepoint, and add libcmremap()/__mremap()entry points. - Add
tst-mremapand integrate it into the tests module build.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/tst-mremap.cc | New functional test coverage for anon and file-backed mremap() behaviors and error paths. |
| syscalls/syscalls.cc.in | Adds long_mremap syscall declaration to the generated syscall table. |
| syscalls/syscall_tracepoints.cc.in | Adds a syscall tracepoint for long_mremap. |
| modules/tests/Makefile | Registers the new tst-mremap.so test module. |
| linux.cc | Adds a Linux-hosted long_mremap() shim for syscall emulation/testing. |
| libc/mman.cc | Adds libc mremap() wrapper and internal __mremap() symbol. |
| include/osv/mmu.hh | Declares the new mmu::mremap() API. |
| exported_symbols/osv_libc.so.6.symbols | Exports mremap from OSv’s glibc-compat libc. |
| exported_symbols/osv_ld-musl.so.1.symbols | Exports mremap from OSv’s musl loader. |
| core/mmu.cc | Implements mmu::mremap() and supporting helpers using VMA machinery. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| #include <sys/mman.h> | ||
| #include <unistd.h> | ||
| #include <fcntl.h> | ||
| #include <string.h> | ||
| #include <stdlib.h> |
| // mapped source vma is supported (Linux requires the old range to fall in one | ||
| // mapping too); a range spanning several vmas or a hole yields EFAULT. | ||
| // | ||
| // ponytail: anon and file-backed mappings are handled; the move path preserves |
| auto* fvma = dynamic_cast<file_vma*>(&*i); | ||
| s.is_file = fvma != nullptr; | ||
| if (fvma) { | ||
| s.file = fvma->file(); | ||
| s.offset = fvma->offset(); | ||
| } |
| if (new_size == 0 || (flags & MREMAP_FIXED)) { | ||
| errno = EINVAL; | ||
| return MAP_FAILED; | ||
| } |
| if (new_size <= old_size) { | ||
| if (new_size < old_size) { | ||
| munmap(reinterpret_cast<void*>(old_start + new_size), | ||
| old_size - new_size); | ||
| } | ||
| return old_addr; | ||
| } |
| if (src.is_file) { | ||
| // File-backed move: remap the file at the new address with the same |
| mount | ||
| mprotect | ||
| mremap | ||
| mrand48 |
| mount | ||
| mprotect | ||
| mremap | ||
| mrand48 |
|
Thanks for the review (and Copilot). Addressed in the current tip (3f8152e, rebased onto master and squashed into one coherent commit):
Needs a build to confirm it compiles and the test passes on the OSv image; I have queued that. |
871c20c to
89a822a
Compare
Add Linux mremap(2): resize (and optionally move) an existing mapping. Wired end-to-end through mmu::mremap(), the syscall dispatch/tracepoints, and the libc entry points (mremap and __mremap), with a dedicated test. Only a single, wholly mapped source vma is supported (Linux likewise requires the old range to fall in one mapping); a range spanning several vmas or a hole yields EFAULT. Both anon and file-backed mappings are handled, and the move path preserves the mapping type/perm/flags (and the correct file offset when the source starts mid-vma). MREMAP_FIXED is not supported yet and returns EINVAL. Correctness and robustness: - Reject sizes that overflow when page-aligned and address ranges that wrap before any range arithmetic uses them; old_size == 0 and new_size == 0 are EINVAL. - The grow-in-place path re-verifies the tail range is free under the write lock and inserts the tail vma atomically, so a concurrent mmap/stack allocation cannot be clobbered (evacuated) by mmap_fixed. - The shrink path checks munmap()'s return and fails rather than reporting a success that left the mapping unchanged. - Moving a writable MAP_PRIVATE file mapping is refused with EINVAL rather than silently losing COW-dirtied private pages; a correct private-dirty-page copy is left as a TODO. Export both mremap and __mremap so callers that resolve the internal musl name link correctly. Signed-off-by: Greg Burd <greg@burd.me>
|
Pushed a couple of build fixes on top (now
Rebuilt |
What
Implements
mremap(2), which was missing entirely on OSv: it was not in thesyscall table and had no libc entry point, so applications and allocators that
resize an mmap region failed (glibc/musl
reallocof an mmap-backed chunk callsthe internal
__mremap).How
mmu::mremap()(core/mmu.cc) is built on the existing VMA machinery and thepublic, self-locking
map_anon()/map_file()/munmap()primitives. It handlesa single, wholly mapped source vma (Linux likewise requires the old range to
fall within one mapping; a range spanning several vmas or a hole yields
EFAULT):MREMAP_MAYMOVE: allocate a new region of the new size, migratethe contents (anonymous: copy; file-backed: remap the file at the same offset
so the grown tail is served by the fault path with no copy), then unmap the old
range.
The source vma is inspected once under
vma_list_mutexto snapshot itsperm/flags/type/file/offset and whether its tail is free, so the rest of the
function can call the self-locking primitives without nesting the non-recursive
vma write lock.
MREMAP_FIXEDis not supported yet and returnsEINVALrather than doing thewrong thing; the anonymous and file-backed paths cover the callers we have.
Wired as syscall
SYS_mremap(vialong_mremap, mirroringlong_mmapsince thereturn value is a pointer) with a matching tracepoint, plus the libc
mremap()/__mremap()entry points, and both symbols exported fromlibc.so.6andld-musl.so.1.Testing
tests/tst-mremap.cccovers grow, shrink, in-place-blocked (ENOMEMwithoutMREMAP_MAYMOVE), file-backed move, and theEINVAL/EFAULTerror paths. Itpasses on OSv under KVM and on native Linux (semantics match).
tst-mmapcontinues to pass, so there is no regression in the VMA path.
(Recreated from #1412, which GitHub auto-closed when its branch was rebased onto current master. Same change, rebased and build/boot-verified on master 3aba46c.)