$RodHat_
Syscall reference · Memory

membarrier(2)

Issue a memory barrier across all threads in the calling process, offloading the barrier cost from readers to the writer.

Since
Linux 4.3check your kernel →
glibc
glibc 2.35; use syscall(2) on older systems
System
Linux
Man page
membarrier(2)

Synopsis

#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <unistd.h>

int ret = syscall(SYS_membarrier, int cmd, unsigned int flags, int cpu_id);

Rod's take

membarrier(2) lets the writer pay the memory-ordering cost so readers need no LOCK-prefix or DMB. Register with MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED at startup, then call MEMBARRIER_CMD_PRIVATE_EXPEDITED after each store. This is the foundation of liburcu's sys_membarrier scheme. The SYNC_CORE variant is the right tool after JIT code patching on ARM64 and RISC-V.

Flags and arguments that matter

NameWhat it does
MEMBARRIER_CMD_QUERYReturns a bitmask of commands supported by this kernel.
MEMBARRIER_CMD_GLOBALLinux 4.3Wait for every CPU on the system to pass through a memory barrier. Slow (includes all processes); no registration required.
MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITEDLinux 4.16Register to receive global expedited barriers from other processes.
MEMBARRIER_CMD_GLOBAL_EXPEDITEDLinux 4.16IPI every CPU running a thread that called MEMBARRIER_CMD_REGISTER_GLOBAL_EXPEDITED. Faster than GLOBAL but requires coordination.
MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITEDLinux 4.14Register intent to use MEMBARRIER_CMD_PRIVATE_EXPEDITED. Must be called before any reader threads are spawned.
MEMBARRIER_CMD_PRIVATE_EXPEDITEDLinux 4.14IPI every thread in the calling process. Readers execute a barrier on the IPI return path. Requires prior REGISTER call; returns EPERM otherwise.
MEMBARRIER_CMD_PRIVATE_EXPEDITED_SYNC_CORELinux 4.16Like PRIVATE_EXPEDITED plus an icache-serializing instruction on each IPI return. Required after JIT code patching on non-x86 CPUs.
MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQLinux 5.10Interrupt any rseq critical section running on a remote CPU. cpu_id arg selects the target CPU; pass -1 for all.

Errors you will actually hit

errnoMeaning
EPERMPRIVATE_EXPEDITED called without prior REGISTER call.
EINVALUnknown command or nonzero flags for a command that does not accept flags.
ENOSYSKernel older than 4.3 or CONFIG_MEMBARRIER not set.

Example

#include <linux/membarrier.h>
#include <sys/syscall.h>
#include <unistd.h>

/* at startup, before reader threads exist */
syscall(SYS_membarrier, MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0);

/* writer: store, then IPI all threads */
atomic_store_explicit(&shared_ptr, new_val, memory_order_relaxed);
syscall(SYS_membarrier, MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0);

/* reader: no barrier needed */
void *val = atomic_load_explicit(&shared_ptr, memory_order_acquire);

Trimmed from the full walkthrough in membarrier(2): flip the memory-ordering tax from readers to writers.

BSD equivalent

No equivalent on FreeBSD. liburcu falls back to sending a signal to each reader thread, whose signal handler executes the barrier. Correct but slower than kernel-assisted IPIs.

The long version, from the console

Sources

  1. membarrier(2), man7.org
  2. Userspace RCU: membarrier-based scheme, LWN.net

Version numbers are read off the cited man pages, not memory. Spot one that's wrong? Tell Rod.