Atomic Operations
Atomic Operations in Kernel Concurrency¶
In kernel modules, concurrent access to shared variables by multiple threads or interrupts requires atomic operations to prevent race conditions. Atomic operations ensure that read-modify-write sequences are executed as a single, uninterruptible unit. The Linux kernel provides atomic_t and cmpxchg (compare-and-exchange) as core primitives for safe integer manipulation.
atomic_t for Simple Atomic Operations¶
The atomic_t type is a wrapper around a signed integer, enabling atomic increments, decrements, and reads. The kernel provides helper functions to manipulate these values safely:
#include <linux/atomic.h>
atomic_t my_counter;
// Initialize
atomic_init(&my_counter, 0);
// Increment
atomic_inc(&my_counter);
// Decrement
atomic_dec(&my_counter);
// Read value
int value = atomic_read(&my_counter);
These operations are guaranteed to be atomic, even across CPU cores or interrupt contexts. For example, atomic_inc ensures that the increment operation is not interrupted by other threads, preventing partial updates.
cmpxchg for Compare-and-Exchange Logic¶
The cmpxchg (compare-and-exchange) operation is critical for implementing lock-free algorithms. It atomically compares a value with an expected value and updates it if the comparison succeeds. This is often used in spinlocks or counters with conditional updates:
#include <linux/atomic.h>
atomic_t my_value;
int expected = 0;
int new_value = 1;
// Atomically replace value if it matches expected
if (cmpxchg(&my_value, expected, new_value)) {
// Success: value was updated
} else {
// Failure: value was modified by another thread
}
The cmpxbed function returns true if the exchange occurred, allowing retry logic in loops. This is essential for implementing synchronization primitives like mutexes or reference counters.
Example: Safe Counter with cmpxchg¶
Here’s a scenario where cmpxchg ensures a counter is incremented without race conditions:
atomic_t counter = ATOMIC_INIT(0);
void increment_counter(void) {
int expected = atomic_read(&counter);
int new = expected + 1;
// Retry until exchange succeeds
while (!cmpxchg(&counter, expected, new)) {
expected = atomic_read(&counter);
new = expected + 1;
}
}
This loop ensures that even if another thread modifies the counter, the update will eventually succeed.
Use Cases and Best Practices¶
- Use
atomic_tfor simple counters, flags, or small integers. - Use
cmpxchgfor conditional updates or implementing lock-free data structures. - Avoid using atomic operations for complex data types (e.g., structs) or operations that require multiple steps.
- Always pair
cmpxchgwith memory barriers (smp_mb()ormb()) to ensure visibility of changes across CPUs.
Key takeaways¶
atomic_tprovides safe, atomic integer operations for kernel modules.cmpxchgenables lock-free algorithms by atomically comparing and updating values.- Atomic operations are essential for preventing race conditions in concurrent code.
- Use
cmpxchgwith retry loops to handle contention gracefully.