Skip to content

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_t for simple counters, flags, or small integers.
  • Use cmpxchg for 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 cmpxchg with memory barriers (smp_mb() or mb()) to ensure visibility of changes across CPUs.

Key takeaways

  • atomic_t provides safe, atomic integer operations for kernel modules.
  • cmpxchg enables lock-free algorithms by atomically comparing and updating values.
  • Atomic operations are essential for preventing race conditions in concurrent code.
  • Use cmpxchg with retry loops to handle contention gracefully.