Device Drivers & Kernel Modules

the MMIO barrier discipline

When you write code that controls hardware, you usually assume the machine does exactly what you wrote, in order. But the compiler and CPU are allowed to reorder, combine, or even skip memory operations that they think do not affect the result — a fine assumption for normal RAM, a disaster for device registers. The MMIO barrier discipline is the set of rules that force device accesses to happen exactly as written, in the right order, so the hardware sees the sequence the driver intends.

There are two distinct problems. First, the compiler must not optimize MMIO accesses away or reorder them: a status register read in a loop must really re-read the hardware each time, not reuse a cached value — this is what volatile is for, and the kernel's writel/readl accessors enforce it so you do not hand-roll volatile pointers. Second, even if the compiler emits the writes in order, the CPU and the bus may let them complete out of order. Consider: you write a data buffer's address into one register, then write a 'go' bit into another; if the 'go' write reaches the device before the address write, the device starts reading from a stale address. A memory barrier between them — wmb() for writes, or the implied barriers in the I/O accessors and functions like mmiowb in older code — forces the first write to be visible to the device before the second.

This discipline matters because without it a driver can be correct on paper and broken in practice, with bugs that appear only under optimization or on certain CPUs and are maddening to debug. The honest core: volatile alone is NOT enough — volatile stops the COMPILER from reordering and caching, but it does not stop the CPU or bus from reordering, so you need real barriers for ordering between accesses, and you need volatile-style accessors so the compiler does not eliminate them. Use the kernel's readl/writel and barrier helpers; do not invent your own with a bare volatile pointer.

// WRONG order can start the device on a stale address: writel(buf_addr, base + ADDR); // tell device where wmb(); // barrier: make ADDR land first writel(GO_BIT, base + CTRL); // then say 'go' // volatile alone stops the compiler reusing a value, // but only a barrier orders the two writes for the device.

A barrier between the address write and the 'go' write guarantees the device sees them in the intended order.

volatile stops only COMPILER reordering and caching, not CPU/bus reordering — it is not a barrier and not atomic. Use the kernel's readl/writel accessors plus explicit barriers (wmb/rmb) for ordering between device accesses.

Also called
volatile and barriers for MMIOdevice-write ordering裝置寫入排序紀律