How to do it...

The following main parts of the hires_timer.c file contain a simple example about high resolution kernel timers.

  1. Let's start from the end of the file, with the module init() function:
static int __init hires_timer_init(void)
{
/* Set up hires timer delay */

pr_info("delay is set to %dns\n", delay_ns);

/* Setup and start the hires timer */
hrtimer_init(&hires_tinfo.timer, CLOCK_MONOTONIC,
HRTIMER_MODE_REL | HRTIMER_MODE_SOFT);
hires_tinfo.timer.function = hires_timer_handler;
hrtimer_start(&hires_tinfo.timer, ns_to_ktime(delay_ns),
HRTIMER_MODE_REL | HRTIMER_MODE_SOFT);

pr_info("hires timer module loaded\n");
return 0;
}

Let's see where the module exit() function is located:

static void __exit hires_timer_exit(void)
{
hrtimer_cancel(&hires_tinfo.timer);

pr_info("hires timer module unloaded\n");
}

module_init(hires_timer_init);
module_exit(hires_timer_exit);

As we can see in module hires_timer_init() initialization function, we read the delay_ns parameter, and, using the hrtimer_init() function, we first initialize the timer by specifying some features:

/* Initialize timers: */
extern void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
enum hrtimer_mode mode);

Using the which_clock argument, we ask the kernel to use a particular clock. In our example, we used CLOCK_MONOTONIC, which is very useful for reliable timestamps and measuring short time intervals accurately (it starts at system boot time but stops during suspend), but we can use other values (see linux/include/uapi/linux/time.h header file for the complete list), for example:

  1. After the timer initialization, we have to set up the callback or handler function by using the function pointer as follows, where we've set timer.function to hires_timer_handler:
hires_tinfo.timer.function = hires_timer_handler;

This time, the hires_tinfo module data structure is defined as follows:

static struct hires_timer_data {
struct hrtimer timer;
unsigned int data;
} hires_tinfo;
  1. After the timer has been initialized, we can start it by calling hrtimer_start() where we just set the expiry time with a function like ns_to_ktime() , in case we have a time interval, or by using ktime_set() , in case we have a seconds/nanoseconds value.
See the linux/include/linux/ktime.h header for more of the ktime*() functions.

If we take a look at the linux/include/linux/hrtimer.h file, we discover that the main function to start a high-resolution timer is hrtimer_start_range_ns() and hrtimer_start() is a particular case of that function, as can be seen in the following:

/* Basic timer operations: */
extern void hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
u64 range_ns, const enum hrtimer_mode mode);

/**
* hrtimer_start - (re)start an hrtimer
* @timer: the timer to be added
* @tim: expiry time
* @mode: timer mode: absolute (HRTIMER_MODE_ABS) or
* relative (HRTIMER_MODE_REL), and pinned (HRTIMER_MODE_PINNED);
* softirq based mode is considered for debug purpose only!
*/
static inline void hrtimer_start(struct hrtimer *timer, ktime_t tim,
const enum hrtimer_mode mode)
{
hrtimer_start_range_ns(timer, tim, 0, mode);
}
We also discover that the HRTIMER_MODE_SOFT mode should not be used apart from for debugging purposes.

By using the hrtimer_start_range_ns() function, we allow a range_ns delta time, which gives the kernel the freedom to schedule the actual wake up to a time that is both power and performance friendly. The kernel gives the normal best effort behavior for expiry time plus delta, but may decide to fire the timer earlier, but no earlier than the tim expiry time.

  1. The hires_timer_handler() function from the hires_timer.c file is an example of the callback function:
static enum hrtimer_restart hires_timer_handler(struct hrtimer *ptr)
{
struct hires_timer_data *info = container_of(ptr,
struct hires_timer_data, timer);

pr_info("kernel timer expired at %ld (data=%d)\n",
jiffies, info->data++);

/* Now forward the expiration time and ask to be rescheduled */
hrtimer_forward_now(&info->timer, ns_to_ktime(delay_ns));
return HRTIMER_RESTART;
}

By using the container_of() operator, we can take a pointer to our data structure (defined in the example as struct hires_timer_data) and, then, having completed our job, we call hrtimer_forward_now() to set up a new expiry time, and, by returning the HRTIMER_RESTART value, we ask the kernel to restart the timer. For one-shot timers, we can return HRTIMER_NORESTART.

  1. On the module exit, within the hires_timer_exit() function, we must use the hrtimer_cancel() function to wait for the timer to stop. It's really important to wait for the timer to stop, because timers are asynchronous events and it may happen that we remove the struct hires_timer_data module freeing structure while the timer callback is executing, which can result in a severe memory corruption!

Note that syncing is implemented as a sleep (or suspend) process, and it means that the hrtimer_cancel() function cannot be called when we're in interrupt context (hard or soft). However, in these situations, we can use hrtimer_try_to_cancel(), which simply returns a non-negative value if the timer has been correctly stopped (or is simply not active).