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 withGFP_KERNEL. - Memory lifetime: Ensure the
struct tick_monitorremains valid for the timer’s entire lifetime — avoid stack allocation or prematurekfree(). - 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.