.\" $NetBSD: membar_ops.3,v 1.5.6.1 2020/09/13 12:20:22 martin Exp $ .\" .\" Copyright (c) 2007, 2008 The NetBSD Foundation, Inc. .\" All rights reserved. .\" .\" This code is derived from software contributed to The NetBSD Foundation .\" by Jason R. Thorpe. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS .\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED .\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR .\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS .\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR .\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF .\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS .\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN .\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) .\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" .Dd September 2, 2020 .Dt MEMBAR_OPS 3 .Os .Sh NAME .Nm membar_ops , .Nm membar_enter , .Nm membar_exit , .Nm membar_producer , .Nm membar_consumer , .Nm membar_datadep_consumer , .Nm membar_sync .Nd memory ordering barriers .\" .Sh LIBRARY .\" .Lb libc .Sh SYNOPSIS .In sys/atomic.h .\" .Ft void .Fn membar_enter "void" .Ft void .Fn membar_exit "void" .Ft void .Fn membar_producer "void" .Ft void .Fn membar_consumer "void" .Ft void .Fn membar_datadep_consumer "void" .Ft void .Fn membar_sync "void" .Sh DESCRIPTION The .Nm family of functions prevent reordering of memory operations, as needed for synchronization in multiprocessor execution environments that have relaxed load and store order. .Pp In general, memory barriers must come in pairs \(em a barrier on one CPU, such as .Fn membar_exit , must pair with a barrier on another CPU, such as .Fn membar_enter , in order to synchronize anything between the two CPUs. Code using .Nm should generally be annotated with comments identifying how they are paired. .Pp .Nm affect only operations on regular memory, not on device memory; see .Xr bus_space 9 and .Xr bus_dma 9 for machine-independent interfaces to handling device memory and DMA operations for device drivers. .Pp Unlike C11, .Em all memory operations \(em that is, all loads and stores on regular memory \(em are affected by .Nm , not just C11 atomic operations on .Vt _Atomic Ns -qualified objects. .Bl -tag -width abcd .It Fn membar_enter Any store preceding .Fn membar_enter will happen before all memory operations following it. .Pp An atomic read/modify/write operation .Pq Xr atomic_ops 3 followed by a .Fn membar_enter implies a .Em load-acquire operation in the language of C11. .Pp .Sy WARNING : A load followed by .Fn membar_enter .Em does not imply a .Em load-acquire operation, even though .Fn membar_exit followed by a store implies a .Em store-release operation; the symmetry of these names and asymmetry of the semantics is a historical mistake. In the .Nx kernel, you can use .Xr atomic_load_acquire 9 for a .Em load-acquire operation without any atomic read/modify/write. .Pp .Fn membar_enter is typically used in code that implements locking primitives to ensure that a lock protects its data, and is typically paired with .Fn membar_exit ; see below for an example. .It Fn membar_exit All memory operations preceding .Fn membar_exit will happen before any store that follows it. .Pp A .Fn membar_exit followed by a store implies a .Em store-release operation in the language of C11. For a regular store, rather than an atomic read/modify/write store, you should use .Xr atomic_store_release 9 instead of .Fn membar_exit followed by the store. .Pp .Fn membar_exit is typically used in code that implements locking primitives to ensure that a lock protects its data, and is typically paired with .Fn membar_enter . For example: .Bd -literal -offset abcdefgh /* thread A */ obj->state.mumblefrotz = 42; KASSERT(valid(&obj->state)); membar_exit(); obj->lock = 0; /* thread B */ if (atomic_cas_uint(&obj->lock, 0, 1) != 0) return; membar_enter(); KASSERT(valid(&obj->state)); obj->state.mumblefrotz--; .Ed .Pp In this example, .Em if the .Fn atomic_cas_uint operation in thread B witnesses the store .Li "obj->lock = 0" from thread A, .Em then everything in thread A before the .Fn membar_exit is guaranteed to happen before everything in thread B after the .Fn membar_enter , as if the machine had sequentially executed: .Bd -literal -offset abcdefgh obj->state.mumblefrotz = 42; /* from thread A */ KASSERT(valid(&obj->state)); \&... KASSERT(valid(&obj->state)); /* from thread B */ obj->state.mumblefrotz--; .Ed .Pp .Fn membar_exit followed by a store, serving as a .Em store-release operation, may also be paired with a subsequent load followed by .Fn membar_sync , serving as the corresponding .Em load-acquire operation. However, you should use .Xr atomic_store_release 9 and .Xr atomic_load_acquire 9 instead in that situation, unless the store is an atomic read/modify/write which requires a separate .Fn membar_exit . .It Fn membar_producer All stores preceding .Fn membar_producer will happen before any stores following it. .Pp .Fn membar_producer has no analogue in C11. .Pp .Fn membar_producer is typically used in code that produces data for read-only consumers which use .Fn membar_consumer , such as .Sq seqlocked snapshots of statistics; see below for an example. .It Fn membar_consumer All loads preceding .Fn membar_consumer will complete before any loads after it. .Pp .Fn membar_consumer has no analogue in C11. .Pp .Fn membar_consumer is typically used in code that reads data from producers which use .Fn membar_producer , such as .Sq seqlocked snapshots of statistics. For example: .Bd -literal struct { /* version number and in-progress bit */ unsigned seq; /* read-only statistics, too large for atomic load */ unsigned foo; int bar; uint64_t baz; } stats; /* producer (must be serialized, e.g. with mutex(9)) */ stats->seq |= 1; /* mark update in progress */ membar_producer(); stats->foo = count_foo(); stats->bar = measure_bar(); stats->baz = enumerate_baz(); membar_producer(); stats->seq++; /* bump version number */ /* consumer (in parallel w/ producer, other consumers) */ restart: while ((seq = stats->seq) & 1) /* wait for update */ SPINLOCK_BACKOFF_HOOK; membar_consumer(); foo = stats->foo; /* read out a candidate snapshot */ bar = stats->bar; baz = stats->baz; membar_consumer(); if (seq != stats->seq) /* try again if version changed */ goto restart; .Ed .It Fn membar_datadep_consumer Same as .Fn membar_consumer , but limited to loads of addresses dependent on prior loads, or .Sq data-dependent loads: .Bd -literal -offset indent int **pp, *p, v; p = *pp; membar_datadep_consumer(); v = *p; consume(v); .Ed .Pp .Fn membar_datadep_consumer is typically paired with .Fn membar_exit by code that initializes an object before publishing it. However, you should use .Xr atomic_store_release 9 and .Xr atomic_load_consume 9 instead, to avoid obscure edge cases in case the consumer is not read-only. .Pp .Fn membar_datadep_consumer does not guarantee ordering of loads in branches, or .Sq control-dependent loads \(em you must use .Fn membar_consumer instead: .Bd -literal -offset indent int *ok, *p, v; if (*ok) { membar_consumer(); v = *p; consume(v); } .Ed .Pp Most CPUs do not reorder data-dependent loads (i.e., most CPUs guarantee that cached values are not stale in that case), so .Fn membar_datadep_consumer is a no-op on those CPUs. .It Fn membar_sync All memory operations preceding .Fn membar_sync will happen before any memory operations following it. .Pp .Fn membar_sync is a sequential consistency acquire/release barrier, analogous to .Li "atomic_thread_fence(memory_order_seq_cst)" in C11. .Pp .Fn membar_sync is typically paired with .Fn membar_sync . .Pp A load followed by .Fn membar_sync , serving as a .Em load-acquire operation, may also be paired with a prior .Fn membar_exit followed by a store, serving as the corresponding .Em store-release operation. However, you should use .Xr atomic_load_acquire 9 instead of .No load-then- Ns Fn membar_sync if it is a regular load, or .Fn membar_enter instead of .Fn membar_sync if the load is in an atomic read/modify/write operation. .El .Sh SEE ALSO .Xr atomic_ops 3 , .Xr atomic_loadstore 9 .Sh HISTORY The .Nm membar_ops functions first appeared in .Nx 5.0 . The data-dependent load barrier, .Fn membar_datadep_consumer , first appeared in .Nx 7.0 .