KT – use kernel timers in the Linux kernel

Compared to start a timer directly in certain kernel thread which will make the thread sleep for a while, using a kernel timer may be much more desired without stopping the current working thread. This post demonstrates a live example using the kernel timer without talking about details. Some pitfalls are discussed in case that the kernel is panic().

0. R.t.D.C.

/*
 * kt.c
 * Kernel module using the kernel timer
 * Reference: http://lwn.net/images/pdf/LDD3/ch07.pdf
 * Jul 7, 2014
 * root@davejingtian.org
 * http://davejingtian.org
 */
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/spinlock.h>

/* A global struct acting as critical section */
struct global_data {
	/* Spinlock */
	spinlock_t lock;
	/* Counter */
	int count;
};
struct global_data kt_global_data;

/* A timer list */
struct timer_list kt_timer;

/* Timer callback */
void timer_callback(unsigned long arg)
{
	printk(KERN_INFO "Entering: %s\n", __FUNCTION__);

	struct global_data *data = (struct global_data *)arg;
	spin_lock(&(data->lock));
	data->count++;
	spin_unlock(&(data->lock));
	mod_timer(&kt_timer, jiffies + 10*HZ); /* restarting timer */
}

/* Init the timer */
static void kt_init_timer(void)
{
	init_timer(&kt_timer);
	kt_timer.function = timer_callback;
	kt_timer.data = (unsigned long)(&kt_global_data);
	kt_timer.expires = jiffies + 10*HZ; /* 10 second */
	add_timer(&kt_timer); /* Starting the timer */

	printk(KERN_INFO "kt_timer is started\n");
}

/* The normal module worker */
static void kt_do_the_work(void)
{
	printk(KERN_INFO "Before %s, count = %d\n", __FUNCTION__,
		kt_global_data.count);

	spin_lock(&(kt_global_data.lock));
	kt_global_data.count++;
	spin_unlock(&(kt_global_data.lock));

	printk(KERN_INFO "After %s, count = %d\n", __FUNCTION__,
		kt_global_data.count);
}

static int __init kt_init(void)
{
	printk(KERN_INFO "Entering: %s\n", __FUNCTION__);
	
	/* Init the global data */
	memset(&kt_global_data, 0x0, sizeof(struct global_data));

	/* Init the spinlock */
	spin_lock_init(&(kt_global_data.lock));

	/* Init the timer */
	kt_init_timer();

	/* Do our job */
	kt_do_the_work();

	return 0;
}

static void __exit kt_exit(void)
{
	printk(KERN_INFO "exiting kt module\n");

	/* Delete the timer */
	del_timer_sync(&kt_timer);

	printk(KERN_INFO "kt_global_data.count = [%d]\n",
		kt_global_data.count);
}

module_init(kt_init);
module_exit(kt_exit);

MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("KT Module");
MODULE_AUTHOR("daveti");

1. Pitfalls

LDD 3 says – “A kernel timer is a data structure that instructs the kernel to execute a user-defined function with a user-defined argument at a user-defined time.“. One can imagine a real kernel timer worker thread maintaining its timer queue for a certain CPU and doing the work accordingly. However, kernel timers are not free to use. Be sure to understand its restrictions (still from LDD 3):

No access to user space is allowed. Because there is no process context, there is no path to the user space associated with any particular process.
The current pointer is not meaningful in atomic mode and cannot be used since the relevant code has no connection with the process that has been interrupted.
No sleeping or scheduling may be performed. Atomic code may not call schedule or a form of wait_event, nor may it call any other function that could sleep. For example, calling kmalloc(…, GFP_KERNEL) is against the rules. Semaphores also must not be used since they can sleep.

About daveti

Interested in kernel hacking, compilers, machine learning and guitars.
This entry was posted in OS and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.