Linux Kernel Timer Deadline Detection: Build a Timeout-Aware Driver (Kernel 6.x)
A hands-on lecture from our free Linux kernel development course — learn how kernel timers catch missed deadlines in real driver code.
If you have ever wondered how a Linux driver can tell that a piece of work took too long, the answer almost always involves kernel timer deadline detection. This lecture, part of our free Linux kernel development course, walks you through a simple, original character driver that starts a kernel timer before doing some work and cancels it the moment the work finishes. If the timer fires first, the driver knows the deadline was missed. It’s a pattern you’ll reuse constantly once you start writing real embedded systems drivers.
We’ll build everything from scratch, using only the current, supported kernel timer APIs — no deprecated calls, no legacy hardware assumptions. Every code snippet below is written fresh for this free Linux device drivers course and tested against a modern 6.x kernel tree.
What You Will Learn
- Why a driver needs its own kernel timer deadline detection logic
- The modern kernel timer API:
timer_setup(),mod_timer(), andtimer_delete_sync() - How to design a misc character driver that arms a timer, does bounded work, and disarms the timer safely
- How to tell, from kernel logs, whether your driver hit its deadline or missed it
- Common mistakes, best practices, and performance/security notes for timer-based code
Prerequisites
- Comfort building and loading a basic loadable kernel module (LKM)
- Familiarity with misc character devices and
ioctl() - A Linux kernel 6.2 or newer for testing (we’ll explain why that version matters)
Why Kernel Timer Deadline Detection Matters
Most driver work is expected to finish quickly — a few microseconds to a few milliseconds. But hardware can hang, a queue can back up, or a shared resource can be held longer than expected. Without some form of deadline detection, a driver has no way to notice this and recover. A kernel timer gives you a cheap, reliable “alarm clock”: arm it for slightly longer than the work should ever take, and if it rings, you know something went wrong.
This is a different use of timers than periodic polling or retransmission. Here the timer is a watchdog for a single operation, and the expected outcome is that it almost never fires — it’s cancelled first, every time, in the happy path.
The Modern Kernel Timer API
Kernel timer APIs have changed over the years. If you’re following an older book or blog, some of the calls you’ll see no longer exist on current kernels. Here’s the up-to-date picture for kernel 6.x:
| Purpose | Old / Legacy API | Current API (6.2+) |
|---|---|---|
| Initialize a timer | init_timer() |
timer_setup() |
| Arm / re-arm a timer | mod_timer() |
mod_timer() (unchanged) |
| Cancel a timer, may still be running | del_timer() |
timer_delete() |
| Cancel and block until any running callback finishes | del_timer_sync() |
timer_delete_sync() |
| Cancel permanently on module unload | (no equivalent) | timer_shutdown_sync() |
Note: the old del_timer() / del_timer_sync() wrapper names were removed from mainline in kernel 6.15, so writing new code against timer_delete() / timer_delete_sync() directly is the safe choice for a free Linux kernel development course aimed at current kernels.
Design: A Timeout-Aware Encrypt/Decrypt Driver
To make kernel timer deadline detection concrete, we’ll design a small misc character driver, kt_deadline_demo, that scrambles a short in-kernel buffer using a trivial byte transform. The transform itself isn’t the point — it’s a stand-in for “some bounded piece of driver work.” What matters is the timing skeleton around it.
The two outcomes at the bottom of the diagram are mutually exclusive by design: either the driver cancels the timer before it expires, or the timer’s callback runs and flips a flag the driver checks afterwards. This is the entire mental model behind kernel timer deadline detection — everything else is bookkeeping.
Original Code Example: The Driver Context and Timer Setup
Here is the driver’s private context structure and its initialization, written for kernel 6.2 and later:
struct kt_deadline_ctx {
struct timer_list watchdog;
atomic_t deadline_missed;
u8 buf[64];
size_t len;
};
static struct kt_deadline_ctx *g_ctx;
/* Timer callback: runs in softirq context if the deadline is hit */
static void kt_deadline_timeout(struct timer_list *t)
{
struct kt_deadline_ctx *ctx = from_timer(ctx, t, watchdog);
atomic_set(&ctx->deadline_missed, 1);
pr_warn("kt_deadline_demo: deadline missed, work took too long\n");
}
static int __init kt_deadline_init(void)
{
g_ctx = kzalloc(sizeof(*g_ctx), GFP_KERNEL);
if (!g_ctx)
return -ENOMEM;
timer_setup(&g_ctx->watchdog, kt_deadline_timeout, 0);
atomic_set(&g_ctx->deadline_missed, 0);
pr_info("kt_deadline_demo: loaded\n");
return 0;
}
Next, the core routine that arms the timer, does the work, and disarms it — the heart of kernel timer deadline detection:
#define KT_DEADLINE_MS 2
static void kt_deadline_process(struct kt_deadline_ctx *ctx)
{
size_t i;
/* Arm the watchdog: expect this loop to finish well inside 2 ms */
mod_timer(&ctx->watchdog, jiffies + msecs_to_jiffies(KT_DEADLINE_MS));
for (i = 0; i len; i++)
ctx->buf[i] ^= 0x5A; /* trivial demo transform */
/* Work is done: cancel the watchdog before it can fire */
if (timer_delete_sync(&ctx->watchdog))
pr_info("kt_deadline_demo: finished inside deadline\n");
else
pr_info("kt_deadline_demo: watchdog had already fired\n");
}
And cleanup on module removal, using the newer shutdown call so the timer can never be re-armed after this point:
static void __exit kt_deadline_exit(void)
{
timer_shutdown_sync(&g_ctx->watchdog);
kfree(g_ctx);
pr_info("kt_deadline_demo: unloaded\n");
}
Notice that timer_delete_sync() is used in the normal operating path (it may be called many times over the driver’s life), while timer_shutdown_sync() is reserved for the exit path, because it also prevents the timer from ever being armed again — exactly what you want when the module is on its way out.
Real-World Use Cases
| Scenario | How the deadline timer helps |
|---|---|
| Waiting on a hardware register bit | Detects a stuck or unresponsive peripheral instead of spinning forever |
| DMA transfer completion | Flags a DMA engine that never raised its completion interrupt |
| Protocol handshake in a bus driver | Times out a peer that stops responding mid-transaction |
Common Mistakes and Troubleshooting
- Using
timer_delete()where you needtimer_delete_sync(): the non-sync version can return while the callback is still running on another CPU, leading to a race with data the callback touches. - Freeing the context before the timer is disarmed: always call
timer_shutdown_sync()beforekfree()in your exit path. - Picking a deadline that’s too tight: normal scheduling jitter can trip a timer set for the bare minimum expected time; leave headroom.
- Forgetting the callback runs in softirq context: it cannot sleep, so don’t call blocking APIs from inside
kt_deadline_timeout()-style callbacks.
Best Practices
- Protect any data shared between the timer callback and process context with a spinlock, not just an atomic flag, once the data is more than a single word.
- Always pair every
mod_timer()with a corresponding cancel call on every exit path of the function, including error returns. - Log deadline misses with enough context (operation type, elapsed time) to actually debug them later.
- Prefer
timer_shutdown_sync()specifically for module/driver teardown, and reservetimer_delete_sync()for normal runtime cancellation.
Performance Considerations
Arming and cancelling a kernel timer on every request has a small but real cost — it touches a per-CPU timer wheel. For very high-frequency operations (thousands of calls per second), consider arming the timer only when you have reason to suspect trouble, or batching deadline checks, rather than wrapping every single call.
Security Considerations
A deadline-detection watchdog is itself a useful defensive mechanism: it stops a misbehaving or compromised peripheral from hanging a kernel thread indefinitely. Make sure the deadline-missed path fails safely — return an error to user space rather than silently returning stale or partially processed buffer contents.
Summary / Key Takeaways
- Kernel timer deadline detection is an “arm, work, cancel” pattern used to catch operations that take too long
- Use
timer_setup(),mod_timer(),timer_delete_sync(), andtimer_shutdown_sync()— the current APIs on kernel 6.2 and newer - The timer callback runs in softirq context, so it must stay short and non-blocking
- This pattern generalizes to any bounded driver operation: register polling, DMA waits, protocol handshakes
Frequently Asked Questions
Q1. What is kernel timer deadline detection used for?
It lets a driver notice when an operation has taken longer than expected, instead of assuming success or hanging indefinitely.
Q2. Is del_timer() still available in modern kernels?
The old del_timer() / del_timer_sync() wrapper names were removed from mainline in kernel 6.15; use timer_delete() and timer_delete_sync() instead.
Q3. Can the timer callback sleep or call blocking functions?
No. Kernel timer callbacks run in softirq (interrupt-like) context, so they must never sleep or block.
Q4. What’s the difference between timer_delete_sync() and timer_shutdown_sync()?
Both cancel a running timer and wait for any in-flight callback. timer_shutdown_sync() additionally guarantees the timer can never be armed again, which is why it belongs in the driver’s exit/removal path.
Q5. How tight should I set the deadline?
Set it comfortably above the worst-case time the work should ever take under normal load, to avoid false alarms from ordinary scheduling jitter.
Q6. Do I need a spinlock around the shared context in this example?
For a single flag, an atomic variable is enough. For anything larger shared between the callback and process context, use a spinlock.
Q7. Is this pattern specific to character drivers?
No — the arm/work/cancel pattern applies to any kernel code path, not just misc character drivers.
Q8. Where can I practice this hands-on?
Build the kt_deadline_demo module from this lecture on a kernel 6.2+ virtual machine or Raspberry Pi, and try triggering the deadline-missed path on purpose by shortening KT_DEADLINE_MS.
Conclusion
Kernel timer deadline detection is a small pattern with outsized value: a handful of API calls give your driver the ability to notice when something has gone wrong, without adding complex logic or extra threads. With the modern timer_delete_sync() / timer_shutdown_sync() APIs in place of the retired legacy calls, this pattern is fully ready for current kernels. Try building kt_deadline_demo yourself as part of this free Linux kernel development course, and carry the same arm/work/cancel idea into your own drivers.
Keep Learning With EmbeddedPathashala
This lecture is part of our ongoing free Linux kernel development course covering timers, threads, workqueues, and device driver fundamentals on modern kernels.
Explore More Lectures
2 Comments