Linux Kernel Timer Module Example: Build a Working Timer Driver on Kernel 6.x-Free Linux Kernel Development Course

Linux Kernel Timer Module Example: Build a Working Timer Driver on Kernel 6.x | EmbeddedPathashala

« Previous Lecture  |  Next Lecture »

Linux Kernel Timer Module Example
A hands-on, kernel 6.x-ready walkthrough from our free Linux kernel development course
Level: Beginner–Intermediate
Kernel: 6.x (tested logic, 6.2+ APIs)
Series: Kernel Timers, Threads & Workqueues
Topics covered in this free embedded systems course lecture:
linux kernel timer module timer_setup add_timer mod_timer timer_shutdown_sync free linux device drivers course

If you are following our free linux kernel development course, this lecture is where theory turns into a running driver. Building a working linux kernel timer module is the single best way to understand how the kernel measures time, schedules deferred work, and safely tears that work down again on module removal. In this lecture we design, write, and test a complete, original timer-driven kernel module — not a toy snippet — and we update every API call to match the current kernel 6.x timer subsystem, including the newer timer_shutdown_sync() cleanup path that replaced the old deletion helpers.

What You Will Learn

  • How a linux kernel timer module is structured, end to end
  • How to arm a timer with timer_setup() and add_timer()
  • How to safely re-arm a repeating timer using mod_timer()
  • How to retrieve your driver’s private context from inside a timer callback
  • Why cleanup on kernel 6.2+ needs timer_shutdown_sync(), not the older deletion calls
  • Common bugs, race conditions, and performance/security pitfalls with kernel timers

Prerequisites

  • Comfort building and loading a basic Loadable Kernel Module (LKM) with insmod/rmmod
  • A Linux VM or machine running kernel 6.2 or later (check with uname -r)
  • Kernel headers installed for your running kernel
  • Basic C pointer and struct knowledge

What Is a Linux Kernel Timer Module, Really?

At its core, a kernel timer is a request you hand to the kernel: “run this function once a certain number of jiffies have passed.” A linux kernel timer module wraps that request inside a loadable driver so you can arm it, watch it fire, and clean it up safely. Unlike a userspace timer, the callback here runs in softirq (atomic) context — no sleeping, no blocking allocations, no copying to or from user space.

Why Build Your Own Timer Module Instead of Just Reading About It?

Reading about timer_setup() and mod_timer() only gets you so far. Real-world drivers — watchdogs, polling-based sensor drivers, connection keep-alives, LED blink patterns, debounce logic — all lean on exactly this pattern. Writing your own linux kernel timer module from scratch is what makes the API click.

Architecture and Workflow of Our Timer Module

Here is the lifecycle we are about to implement, shown as an inline diagram rather than a text-based drawing:

Timer Module Lifecycle
insmod
→
module init()
→
timer_setup()
→
add_timer()
→
jiffies countdown
→
softirq: callback fires
→
mod_timer() reload
rmmod
→
module exit()
→
timer_shutdown_sync()
→
safe unload

Notice the loop between “callback fires” and “mod_timer() reload” — this is what makes the timer repeat without calling add_timer() again. And notice that unload always goes through a synchronous shutdown call before the module is actually removed; skipping that step is one of the most common causes of kernel crashes on unload.

Complete Original Linux Kernel Timer Module Example

Below is an original driver written for this lecture — a “heartbeat” timer module that logs a heartbeat message a fixed number of times and then stops, demonstrating arm, repeat, and safe shutdown in one place.

// heartbeat_timer.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define HEARTBEAT_INTERVAL_MS 500
#define HEARTBEAT_MAX_BEATS   5

struct heartbeat_dev {
    struct timer_list beat_timer;
    unsigned int beats_left;
};

static struct heartbeat_dev hb;

static void heartbeat_fire(struct timer_list *t)
{
    struct heartbeat_dev *dev = from_timer(dev, t, beat_timer);

    pr_info("heartbeat_timer: beat! (%u remaining)\n", dev->beats_left);

    if (dev->beats_left > 0) {
        dev->beats_left--;
        mod_timer(&dev->beat_timer,
                  jiffies + msecs_to_jiffies(HEARTBEAT_INTERVAL_MS));
    } else {
        pr_info("heartbeat_timer: sequence complete\n");
    }
}

static int __init heartbeat_init(void)
{
    hb.beats_left = HEARTBEAT_MAX_BEATS;

    timer_setup(&hb.beat_timer, heartbeat_fire, 0);
    hb.beat_timer.expires = jiffies + msecs_to_jiffies(HEARTBEAT_INTERVAL_MS);

    add_timer(&hb.beat_timer);
    pr_info("heartbeat_timer: armed, interval=%ums\n", HEARTBEAT_INTERVAL_MS);

    return 0;
}

static void __exit heartbeat_exit(void)
{
    /* kernel 6.2+: replaces the old del_timer_sync() */
    timer_shutdown_sync(&hb.beat_timer);
    pr_info("heartbeat_timer: unloaded cleanly\n");
}

module_init(heartbeat_init);
module_exit(heartbeat_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Original heartbeat linux kernel timer module example");

Step-by-Step Explanation

  • Struct design: the timer_list lives inside our own heartbeat_dev struct, so the callback can recover our private data via from_timer().
  • Arming: timer_setup() binds the callback; add_timer() starts the countdown once expires is set.
  • Repeating: instead of calling add_timer() again, the callback calls mod_timer(), which both reschedules an active timer and can arm an inactive one.
  • Shutdown: timer_shutdown_sync() guarantees the timer is fully stopped and no callback is mid-flight before the module’s memory is freed on unload.

Building and Testing the Module

make
sudo insmod heartbeat_timer.ko
dmesg | tail
sudo rmmod heartbeat_timer
dmesg | tail

You should see five heartbeat lines roughly 500ms apart, followed by a completion message, and a clean unload log on removal.

Kernel 6.x Timer Cleanup: What Changed

APIOlder KernelsKernel 6.2+
Synchronous stop on unloaddel_timer_sync()timer_shutdown_sync()
Non-blocking stopdel_timer()Deprecated; prefer timer_shutdown_sync() in exit paths
Old wrappers removed—del_timer()/del_timer_sync() wrappers removed starting kernel 6.15

Real-World Use Cases for Kernel Timers

  • Hardware watchdog “kick” or timeout detection
  • Polling a sensor register at a fixed interval when interrupts aren’t available
  • Debouncing a GPIO input line
  • LED blink patterns and status indicators
  • Connection or link keep-alive checks in network drivers

Common Mistakes and Troubleshooting

  • Sleeping in the callback: the callback runs in softirq context — never call msleep(), kmalloc(GFP_KERNEL), or copy_to/from_user here.
  • Forgetting shutdown before free: freeing the struct holding your timer_list before stopping the timer is a classic use-after-free.
  • Using stale jiffies math: always recompute jiffies + msecs_to_jiffies(...) at the time you arm or re-arm, don’t cache an old expiry value.
  • Ignoring the return value of mod_timer(): a 0 means it armed an inactive timer; a 1 means it modified one already running — useful for debugging double-arm bugs.

Best Practices for a Linux Kernel Timer Module

  • Keep callback logic minimal — hand heavier work off to a workqueue or kernel thread.
  • Always pair every add_timer() with a matching shutdown call in the exit path.
  • Use TIMER_DEFERRABLE when timing precision doesn’t matter but power savings do.
  • Log both arm and fire events during development; remove noisy logs before production.

Performance Considerations

Kernel timers are cheap, but very short, high-frequency timers (sub-millisecond) can add measurable softirq overhead across many CPUs. If you need sub-millisecond precision, look at hrtimer instead of the classic timer wheel used here.

Security Considerations

Never let a timer callback act on user-controlled pointers directly, and always validate that the private context recovered via from_timer() hasn’t already been torn down — this matters most in drivers that can be opened, closed, and removed concurrently by multiple userspace processes.

Summary / Key Takeaways

  • A linux kernel timer module follows a simple arm → fire → optionally re-arm → shutdown lifecycle.
  • timer_setup() + add_timer() start it; mod_timer() repeats it.
  • On kernel 6.2+, use timer_shutdown_sync() for safe teardown.
  • Callbacks run in atomic context — no sleeping, no blocking work.

Conclusion

You’ve now built, run, and understood a complete linux kernel timer module updated for modern kernel 6.x APIs. This pattern — private context struct, timer_setup(), self-rearming via mod_timer(), and safe shutdown via timer_shutdown_sync() — is the same one you’ll reuse across watchdogs, polling drivers, and countless other real drivers. Continue with the next lecture in our free linux kernel development course to see how kernel threads compare to timers for deferred work.

Frequently Asked Questions

1. What is a linux kernel timer module used for?

It lets a driver schedule a function to run after a set delay, without blocking, for tasks like polling, watchdogs, or timeouts.

2. Can a kernel timer callback sleep?

No. The callback runs in softirq/atomic context, so blocking calls and GFP_KERNEL allocations are not allowed.

3. What replaced del_timer_sync() in newer kernels?

timer_shutdown_sync() is the recommended replacement starting kernel 6.2, and the old wrappers were removed in kernel 6.15.

4. What’s the difference between add_timer() and mod_timer()?

add_timer() arms a freshly initialized, inactive timer. mod_timer() can both re-arm an active timer and start an inactive one, returning 0 or 1 accordingly.

5. When should I use hrtimer instead of a classic kernel timer?

When you need sub-millisecond or high-precision timing; classic timers are tied to the jiffies tick and are coarser.

6. Is this free linux kernel development course beginner friendly?

Yes — this lecture assumes only basic LKM experience and builds every concept from first principles with original code.

Continue the Free Linux Kernel Development Course

More hands-on lectures on kernel timers, threads, and workqueues are coming — part of EmbeddedPathashala’s free embedded systems course.

« Previous Lecture  |  Next Lecture »

2 Comments

Leave a Reply

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