What is Linux Kernel Timers Tutorial-Linux Device Driver Training

Linux Kernel Timers Tutorial
Free Linux Kernel Development Course • Kernel 6.x • timer_setup, mod_timer, del_timer_sync
100% Free
Kernel 6.x API
Hands-On Examples

This Linux kernel timers tutorial is the second lecture in EmbeddedPathashala’s free Linux kernel development course. In the previous lecture we saw how to insert short delays with udelay() and msleep(). But what happens when you need something to run repeatedly, every second, in the background, without your driver code sitting there and blocking? That is exactly the job of a kernel timer, and this Linux kernel timers tutorial walks through the modern, kernel 6.x-compatible API from scratch.

What You Will Learn

  • What a kernel timer is and why it is different from a delay
  • The modern timer_setup() / struct timer_list API used in kernel 6.x
  • How to arm, rearm, and safely tear down a kernel timer
  • Why timer callbacks run in softirq context and what that means for your code
  • A complete, original one-second periodic timer example

Prerequisites

Read the previous lecture on Linux kernel delay functions first, and make sure you’re comfortable building and loading a basic kernel module. This Linux kernel timers tutorial assumes both.

What a Kernel Timer Actually Is

A kernel timer lets you say “run this function once, after N jiffies have passed” without blocking the caller. Internally, the kernel maintains the timer in a per-CPU wheel data structure and fires your callback from softirq context once the deadline is reached. Because it never blocks the thread that armed it, this is the standard building block for any periodic or one-shot background activity in a driver — watchdog checks, polling a status register, debouncing a switch, or timing out a stalled operation.

Kernel Timer Lifecycle
timer_setup(&t, callback, 0) — prepare the struct, bind the callback | v mod_timer(&t, jiffies + delay) — arm / rearm the timer | v [ time passes, softirq fires ] | v callback(struct timer_list *t) — your function runs here | v (optional) mod_timer() again to repeat, or del_timer_sync(&t) — cleanly stop and wait for completion

The Modern Kernel Timer API

Older kernel code you may find online (including in books written before kernel 4.15) uses setup_timer() with a raw unsigned long data field. That interface was removed years ago. Kernel 6.x only supports the timer_setup() style, where your callback receives a pointer to the struct timer_list itself, and you use the container_of-based from_timer() helper to reach your own driver structure.

FunctionPurpose
timer_setup(timer, fn, flags)Initialize a timer and bind its callback
mod_timer(timer, expires)Arm or rearm the timer for a given jiffies value
del_timer_sync(timer)Stop the timer and block until any running callback finishes
timer_pending(timer)Check whether the timer is currently armed

A Complete One-Second Periodic Timer Example

Here is an original, self-contained example that logs a heartbeat message once every second using a kernel timer:

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

struct ep_timer_ctx {
    struct timer_list timer;
    unsigned int ticks;
};

static struct ep_timer_ctx ep_ctx;

static void ep_timer_callback(struct timer_list *t)
{
    struct ep_timer_ctx *ctx = from_timer(ctx, t, timer);

    ctx->ticks++;
    pr_info("ep_timer: heartbeat #%u\n", ctx->ticks);

    /* Rearm for another second to make it periodic */
    mod_timer(&ctx->timer, jiffies + msecs_to_jiffies(1000));
}

static int __init ep_timer_init(void)
{
    timer_setup(&ep_ctx.timer, ep_timer_callback, 0);
    mod_timer(&ep_ctx.timer, jiffies + msecs_to_jiffies(1000));
    pr_info("ep_timer: module loaded\n");
    return 0;
}

static void __exit ep_timer_exit(void)
{
    del_timer_sync(&ep_ctx.timer);
    pr_info("ep_timer: module unloaded\n");
}

module_init(ep_timer_init);
module_exit(ep_timer_exit);
MODULE_LICENSE("GPL");

Notice the pattern: the callback rearms itself with mod_timer() to become periodic. There is no separate “periodic timer” API in the kernel — every recurring timer is really a one-shot timer that keeps rearming itself.

Timer Callback Context Matters

Kernel timer callbacks run in softirq context, which is a form of atomic context. That means inside a timer callback you must never sleep, never call blocking allocation with GFP_KERNEL, and never call the blocking delay functions covered in the previous lecture of this Linux kernel timers tutorial series. If your callback needs to do something that can sleep, schedule a workqueue item from inside it instead — which is exactly the topic of a later lecture.

Common Mistakes

  • Forgetting to call del_timer_sync() in the module’s exit path, which can leave a stale timer pointing at freed memory.
  • Calling del_timer() instead of del_timer_sync() during unload, which does not wait for an in-flight callback to finish, risking a race.
  • Sleeping or allocating with GFP_KERNEL inside a timer callback.
  • Assuming timer expiry is exact — jiffies-based timers have granularity tied to HZ and are not hard real-time.

Best Practices

  • Always pair timer_setup() with a guaranteed del_timer_sync() on every exit path, including error paths.
  • Keep timer callbacks short; hand off real work to a workqueue if it might sleep or take a while.
  • Use msecs_to_jiffies() rather than hand-computing jiffies from HZ, so your code stays portable across kernel configurations.

Performance Considerations

Kernel timers are lightweight, but arming thousands of independent timers across many driver instances adds real overhead to the timer wheel. If you need many similar periodic tasks, consider whether a single shared timer or a workqueue with delayed work would scale better.

Key Takeaways

  • Kernel timers schedule a callback to run later without blocking the caller.
  • Kernel 6.x uses timer_setup() and from_timer(), not the old data-based API.
  • Timer callbacks run in atomic (softirq) context — no sleeping allowed.
  • Always tear down timers with del_timer_sync() before your module exits.

Conclusion

This Linux kernel timers tutorial covered everything you need to safely schedule background work with jiffies-based timers on a modern kernel. Next in this free Linux kernel development course, we move on to kernel threads — for when you need a full background loop rather than a short periodic callback.

Frequently Asked Questions

Q1. What replaced setup_timer() in modern kernels?

timer_setup() replaced setup_timer(); the callback now receives a struct timer_list * pointer instead of an unsigned long data value.

Q2. How do I make a kernel timer repeat?

Call mod_timer() again from inside the callback itself to rearm it for the next interval — there is no built-in “repeating timer” type.

Q3. Can I sleep inside a kernel timer callback?

No, timer callbacks run in softirq (atomic) context, so sleeping or GFP_KERNEL allocation is not allowed. Offload such work to a workqueue.

Q4. What is the difference between del_timer() and del_timer_sync()?

del_timer_sync() blocks until any currently running callback finishes before returning, making it safe to use during module unload; plain del_timer() does not wait.

Q5. How accurate are Linux kernel timers?

Their resolution is tied to the kernel’s jiffies/HZ configuration, so they are suitable for general-purpose background scheduling but not for hard real-time deadlines.

Q6. Is this Linux kernel timers tutorial part of a free course?

Yes, it is part of EmbeddedPathashala’s free Linux kernel development course, alongside a free Linux device drivers course and a free embedded systems course.

linux kernel timers tutorial free linux kernel development course free linux device drivers course timer_setup mod_timer
Continue This Free Linux Kernel Development Course

Next up: Linux Kernel Threads — running background loops with kthread.

Next Lecture →

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *