mm: implement the membarrier(2) syscall - #1434
Conversation
There was a problem hiding this comment.
Pull request overview
Adds support for the Linux membarrier(2) syscall to OSv (including libc exposure and syscall plumbing) so runtimes/libraries that rely on it (e.g., Go/.NET/JVM/RCU-style libraries) can run correctly on OSv.
Changes:
- Implements
membarrier()inruntime.ccand wires it into the syscall table and syscall tracepoints. - Adds the public
sys/membarrier.hAPI header and exports the symbol fromlibc.so.6andld-musl.so.1. - Introduces a dedicated test (
tst-membarrier) and adds it to the tests module build.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/tst-membarrier.cc | Adds a new test covering basic membarrier command behavior and error paths. |
| syscalls/syscalls.cc.in | Registers membarrier in the syscall table generation. |
| syscalls/syscall_tracepoints.cc.in | Adds a syscall tracepoint for membarrier. |
| runtime.cc | Implements the membarrier() libc/syscall behavior using flush_tlb_all() + fence. |
| modules/tests/Makefile | Includes the new membarrier test in the tests build. |
| linux.cc | Declares the membarrier entry point for the Linux syscall layer. |
| include/api/x64/bits/syscall.h | Defines __NR_membarrier / SYS_membarrier for x86-64. |
| include/api/sys/membarrier.h | Introduces the public API header with command/flag definitions and prototype. |
| exported_symbols/osv_libc.so.6.symbols | Exports the membarrier symbol from OSv libc. |
| exported_symbols/osv_ld-musl.so.1.symbols | Exports the membarrier symbol from the musl loader. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
nyh
left a comment
There was a problem hiding this comment.
Looks good. I got used to the word "ponytail" ;-) and I'll see if github lets me squash the two patches together instead of asking you to do it.
|
Oh, I see copilot had some comments, and also there is now a conflict, so please rebase, squash the two patches together and address (or dispute) copilot's commnets. Thanks. |
|
Thanks for the approve. Addressed Copilot's points and squashed into one coherent commit (I did the squash so you don't have to fight GitHub's UI):
Rebuilt, tst-membarrier passes (QUERY, all barrier commands, and the EINVAL paths for unknown command / nonzero flags / nonzero cpu_id). |
Implement membarrier(2) for OSv's single-process model, where all threads share
one address space so the GLOBAL and PRIVATE barriers are equivalent.
- MEMBARRIER_CMD_QUERY reports the supported commands.
- REGISTER_{GLOBAL,PRIVATE}_EXPEDITED are no-ops (any thread may issue).
- GLOBAL / GLOBAL_EXPEDITED / PRIVATE_EXPEDITED force every other CPU through
an IPI via mmu::flush_tlb_all() (returning from that interrupt is a
context-synchronizing full barrier on x86-64 and aarch64) plus a local
seq_cst fence. This is a correct superset of the barrier membarrier promises
(it also flushes TLBs, harmless). Reusing the proven arch-portable
flush_tlb_all() broadcast avoids a per-arch dedicated membarrier IPI; a
lighter dedicated IPI can be added later if it ever shows up hot.
- flags and cpu_id must be 0 (the SYNC_CORE / RSEQ / CPU-targeted variants are
not implemented); a nonzero cpu_id, only meaningful with the unsupported CPU
flag, is rejected with EINVAL like an unknown command.
Wired into the syscall table, exported from libc and the musl loader, and
covered by tst-membarrier (QUERY, the barrier commands, and the
EINVAL paths for unknown command / nonzero flags / nonzero cpu_id); builds and
runs on Linux too.
|
Rebased onto current master (now conflict-free) and confirmed it's already a single squashed commit. The copilot points from the earlier round are addressed in it: nonzero cpu_id returns EINVAL (with a comment explaining cpu_id is only meaningful with the MEMBARRIER_CMD_FLAG_CPU flag we reject), the leftover marker comment is reworded to a plain Note, the aarch64-vs-x64 barrier rationale is spelled out, and the test now covers the cpu_id validation path. Ready for another look. |
What
Implements the
membarrier(2)syscall, which was missing entirely (not in thesyscall table, no libc entry, no prototype). Runtimes that use it for cheap
asymmetric synchronization (Go, .NET, some JVMs, RCU-style user libraries) could
not use it on OSv.
How
OSv is a single process whose threads all share one address space, so a "global"
and a "private" membarrier are equivalent here. The expedited barrier forces
every other CPU through an IPI:
mmu::flush_tlb_all()already broadcasts an IPIand waits for each CPU to run its handler (a full memory barrier on that CPU),
and we add a local
seq_cstfence. This is a correct superset of whatmembarrier promises (it also flushes TLBs, which is harmless), and reuses a
proven, arch-portable primitive rather than adding a dedicated per-arch IPI (x64
inter_processor_interruptvs aarch64sgi_interrupt). A lighter dedicated IPIcan replace it if membarrier ever shows up hot in a profile.
MEMBARRIER_CMD_QUERYreturns the supported command mask.REGISTER_{GLOBAL,PRIVATE}_EXPEDITEDare accepted as no-ops (any thread mayissue the barrier).
{GLOBAL, GLOBAL_EXPEDITED, PRIVATE_EXPEDITED}run the barrier.SYNC_CORE/RSEQ/ CPU-flag variants and unknown commands returnEINVAL,as does a nonzero
flagsargument.Wired as syscall
SYS_membarrier(adding__NR_membarrier/SYS_membarrierfor x86-64) with a tracepoint, plus the
sys/membarrier.hheader with thecommand enum and prototype, and the symbol exported from
libc.so.6andld-musl.so.1.Testing
tests/tst-membarrier.cccovers QUERY, register, the three barrier commands,and the EINVAL paths. Passes on OSv under KVM with 1, 2 and 4 vCPUs (exercising
the cross-CPU IPI broadcast).
(Recreated from #1414, which GitHub auto-closed when its branch was rebased onto current master. Same change, rebased and verified on master 3aba46c.)