Course contentsShow
← Rust
Lesson 1507 of 1,91012. Unsafe Rust and Low-Level AccessFree lesson

Error Log Management

You will design efficient circular buffers for persisting kernel logs to non-volatile memory.

Efficient Circular Buffers for Persistent Error Logs

In this lesson, you will learn how to implement a lock-free circular buffer in no_std environments to persist kernel error logs directly to non-volatile memory (NVM) without triggering costly interrupts.

The Problem of Persistence

In a kernel context, logs often vanish during a crash or power failure. To capture them, we must write directly to a dedicated region of NVM (like FRAM or MRAM). Since we cannot rely on complex OS-level file systems or locks, we use a circular buffer structure mapped directly to memory.

Designing the Circular Buffer

To achieve efficiency, we treat the NVM as a fixed-size byte array. We define a header that tracks the head (where the next entry begins) and the tail (the oldest entry).

#[repr(C)]
struct LogHeader {
    head: usize,
    tail: usize,
    capacity: usize,
}

struct PersistentBuffer {
    header: *mut LogHeader,
    data: *mut u8,
}

When an error occurs, we calculate the write offset using the head pointer. Because multiple CPUs might attempt to log simultaneously, we utilize atomic operations (specifically core::sync::atomic::AtomicUsize) to increment the head pointer. This ensures that even if a panic occurs mid-write, the indices remain internally consistent.

Managing Wraparound

When writing to the buffer, you must handle the "wraparound" condition. If the entry exceeds the physical end of the memory region, the data is split: part is written to the end of the buffer, and the remainder is written to the beginning.

Crucially, because this is NVM, we must ensure memory ordering. We use core::sync::atomic::Ordering::Release when updating the head pointer after a write to guarantee that the data bytes are visible in NVM before the index is updated. If the system crashes, the "next boot" logic simply checks if the head matches the expected state, allowing us to recover logs post-mortem.

This approach minimizes latency by avoiding expensive synchronization primitives, relying instead on atomic pointer manipulation mapped directly to the hardware's memory address space.

Key Takeaway: Use atomic indices to manage circular buffers in NVM, ensuring data consistency across reboots without the overhead of heavy locking mechanisms.