Skip to content

Memory Barriers

Overview

Modern CPUs and compilers may execute or reorder memory accesses differently from the original program order.

Although these optimizations improve performance, they can cause other CPUs or hardware devices to observe memory accesses in an unexpected order.

Memory barriers introduce ordering constraints that guarantee the required visibility between CPUs, shared memory, MMIO registers, and DMA devices.


Why Memory Ordering Matters

Program order is the order in which instructions appear in source code.

For example:

data = 100;
ready = 1;

The compiler or CPU may make ready visible before data, allowing another CPU to observe:

ready == 1
data  == 0

This is known as a memory ordering problem.


Sources of Reordering

Memory reordering may occur at multiple levels.

Compiler Reordering

The compiler may reorder memory accesses during optimization while preserving the behavior of a single thread.

Compiler barriers prevent these optimizations.


CPU Reordering

Modern processors may execute memory accesses out of program order.

Common reasons include:

  • Store Buffer
  • Load Buffer
  • Out-of-Order Execution
  • Speculative Execution

Memory barriers prevent the CPU from exposing these reordered accesses to other observers.


Memory Ordering Model

Conceptually, memory barriers define the relationship between program order and the order observed by other CPUs or devices.

Program Order
Memory Barrier
Visible Memory Order

Without a memory barrier, visible memory order may differ from program order.


Types of Memory Barriers

Memory barriers can be divided into two categories.

Compiler Barrier

Prevents compiler optimizations that move memory accesses across the barrier.

Linux API:

  • barrier()

CPU Memory Barrier

Prevents the CPU from making specific memory accesses visible out of order.

Linux APIs include:

  • mb()
  • rmb()
  • wmb()

For SMP systems:

  • smp_mb()
  • smp_rmb()
  • smp_wmb()

Acquire and Release Ordering

Acquire and Release provide one-way ordering guarantees.

Release:

Store shared data


Release


Publish flag

Acquire:

Observe publish flag


Acquire


Read shared data

These operations synchronize producer-consumer communication without requiring a full memory barrier.


MMIO Ordering

Memory ordering is required when programming hardware registers.

Typical driver sequence:

Write configuration registers


Write Barrier


Write START register

The barrier guarantees that the device observes the configuration before receiving the START command.


DMA Ordering

DMA engines access normal system memory independently of the CPU.

Typical driver sequence:

Fill DMA descriptor


wmb()


Write DMA doorbell

Without the write barrier, the device may receive the notification before the descriptor contents become globally visible.


Common Linux Memory Barrier APIs

API Purpose
barrier() Compiler barrier
mb() Full CPU memory barrier
rmb() Read memory barrier
wmb() Write memory barrier
smp_mb() Full memory barrier for SMP synchronization
smp_rmb() SMP read memory barrier
smp_wmb() SMP write memory barrier
smp_store_release() Publish data using Release ordering
smp_load_acquire() Consume published data using Acquire ordering

Summary

Memory barriers do not change program logic.

Instead, they define when memory accesses are allowed to become visible to other CPUs and hardware devices.

Correct use of memory barriers is essential for:

  • CPU-to-CPU synchronization
  • Lock-free algorithms
  • MMIO register programming
  • DMA descriptor publication
  • Device driver development