Implementing a Kernel-Mode Second-Tick Device Using Timer Lists

To build a kernel module that emulates a real-time second-counter device using the Linux kernel’s timer infrastructure, follow these precise, production-ready steps. This implementation leverages struct timer_list, avoids deprecated APIs (e.g., setup_timer), and ensures thread-safe, interupt-context–compatible behavior.

Step 1: Define the Device Context Structure

Create a dedicated structure to encapsulate state and timer metadata. Prefer struct timer_list over legacy struct timer_struct, and embed it directly:

#include <linux/timer.h>
#include <linux/jiffies.h>

struct tick_monitor {
    struct timer_list tick_timer;
    atomic_t elapsed_seconds;
    bool active;
};

Step 2: Implement the Timer Callback

The callbacck runs in atomic (interrupt) context — avoid sleeping, blocking I/O, or acquiring mutexes. Use container_of to recover the enclosing structure:

static void tick_handler(struct timer_list *t)
{
    struct tick_monitor *ctx = from_timer(ctx, t, tick_timer);

    // Atomically increment counter — safe for concurrent access
    atomic_inc(&ctx->elapsed_seconds);

    // Reschedule for next second: jiffies + HZ
    mod_timer(&ctx->tick_timer, jiffies + HZ);
}

Note: from_timer() is the modern replacement for container_of() with timer lists (introduced in kernel v4.15+), providing type safety and eliminating manual casting.

Step 3: Initialize and Arm the Timer

Initialize the timer during device setup using timer_setup(), which enforces proper function signature and disables auto-rearming:

void monitor_start(struct tick_monitor *ctx)
{
    if (WARN_ON(!ctx))
        return;

    // Initialize timer with callback and no data argument (self-reference via from_timer)
    timer_setup(&ctx->tick_timer, tick_handler, 0);
    
    // Start first tick after one second
    mod_timer(&ctx->tick_timer, jiffies + HZ);
    ctx->active = true;
}

Step 4: Safely Deactivate the Timer

Always cancel before releasing memory. Use del_timer_sync() to guarantee the handler has completed — critical when unloading modules or freeing context:

void monitor_stop(struct tick_monitor *ctx)
{
    if (!ctx || !ctx->active)
        return;

    del_timer_sync(&ctx->tick_timer);
    ctx->active = false;
}

Step 5: Access Runtime State

Expose the current count safely from process context (e.g., sysfs or ioctl handler):

unsigned int monitor_get_count(const struct tick_monitor *ctx)
{
    return ctx ? atomic_read(&ctx->elapsed_seconds) : 0;
}

Key Safety Considerations

  • Context awareness: Timer callbacks execute in softirq context — never call msleep(), mutex_lock(), or allocate memory with GFP_KERNEL.
  • Memory lifetime: Ensure the struct tick_monitor remains valid for the timer’s entire lifetime — avoid stack allocation or premature kfree().
  • Synchronization: Use atomic operations (atomic_t) for shared counters. For complex state, pair timers with spinlocks (if accessed from both timer and syscall paths).
  • Header dependencies: Include <linux/timer.h> and <linux/jiffies.h> explicitly — do not rely on transitive includes.

Tags: linux-kernel Timers device-driver

Posted on Thu, 24 Sep 2026 16:33:31 +0000 by Patrick