WRITE_ONCE and READ_ONCE in linux kernel
Asked Answered
L

1

28

Can somebody explain the usage of WRITE_ONCE and READ_ONCE?

And internally WRITE_ONCE uses a volatile qualifier. Why?

How does WRITE_ONCE and READ_ONCE solve cache coherency problem?

Difference between *(volatile __u8_alias_t *) p and (volatile __u8_alias_t *) *p?

Leff answered 29/5, 2018 at 17:2 Comment(8)
I suggest to read Documentation/memory-barriers.txt. It contains also example of usage of such macros. These macros are actually compiler-only barriers, so they do nothing with "cache coherency". And difference between expressions is a type of p and of the result. This is completely unrelated to the other part of the question. (Prefer to ask a single question in the question post).Apolitical
It's about consistency. Last question is obviously about pointer to the data vs. data itself by given pointer.Margarettamargarette
@Apolitical the question about cache coherency comes from memory-barriers.txt.Gladsome
@MichaelFoukarakis: You, probably, mean phrase "READ_ONCE() and WRITE_ONCE() provide cache coherence for accesses from multiple CPUs to a single variable.". Not sure what this phrase actually means, implementation of READ_ONCE uses smp_read_barrier_depends() which is non-empty only on Alpha. Implementation of WRITE_ONCE doesn't use CPU barriers at all.Apolitical
@Apolitical .....If the size of the accessed data type exceeds the "word size" of the machine (e.g., 32 bits or 64 bits) READ_ONCE() and WRITE_ONCE() will fall back to memcpy and print a compile-time warning. memcpy is wrapped around barrier.Corpse
@Explorer_N: According to the implementation (in include/linux/compiler.h), __read_once_size and __read_once_size actually use memcpy as a fallback, but that memcpy is wrapped with barrier() calls. That calls provide compiler barrier only and has nothing common with the processor's cache coherency.Apolitical
Linux runs on machines with coherent shared memory. Therefore volatile is about equivalent to atomic_load_explicit(&var, memory_order_relaxed). See my answer on When to use volatile with multi threading?Caterina
Does this answer your question? When to use volatile with multi threading?Caterina
T
3

They don't in themselves do anything to solve concurrency but they do stop the compiler doing silly things like loading a value from the same memory location twice. This is important for example if you are accessing HW and don't want to trigger multiple bus accesses, potentially affecting future reads and writes.

Compilers will do this sort of thing because generally they are allowed to optimise access to aliased variables because they think they know how the whole system behaves.

To truly support concurrency you need reason about memory consistency and what values can be guaranteed to visible to one thread if another value is visible. This include declaring operations to be atomic (to avoid "tears" by reading the value in smaller parts and combining the result) and specifying memory barriers. Memory barriers allow you to ensure that values protected by another field will be visible to the other thread when accessed.

volatile read or write alone is similar to C _Atomic load or store with memory_order_relaxed, and usually compiles to the same asm. See When to use volatile with multi threading? (never, except in the Linux kernel) for some of the low-level details of why this is true.

On ISAs like ARMv8 that have load-acquire and store-release instructions, you'd prefer to use those (via smp_load_acquire / smp_store_release) instead of a volatile READ_ONCE and a separate barrier. Most older ISAs just had plain loads and separate barrier instructions. The kernel's READ_ONCE / WRITE_ONCE model is designed around that.

Tacnode answered 1/12, 2023 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.