Module with Two Timers - spinlock

  • In this program, you are going to learn

  • How to schedule the timer ?

  • Here is the explanation of the source code.

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
  • Include all the headers file to refer the APIs used in this program.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Two Timers with spinlock");
  • Add the modules macro which gives information about the license, author and description of the modules.

struct node {
        int data;
    struct list_head list;
};

LIST_HEAD(my_list);

DEFINE_SPINLOCK(sp);

static struct timer_list timer1;
static struct timer_list timer2;
  • Initialize all the variables and functions used in this program.

static int __init timer_init(void)
{
    struct node *node1;
    struct node *node2;

    pr_info("Driver Loaded...\n");

    node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
    node1->data = 1;
    INIT_LIST_HEAD(&node1->list);
    list_add_tail(&node1->list, &my_list);

    node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
    node2->data = 2;
    INIT_LIST_HEAD(&node2->list);
    list_add_tail(&node2->list, &my_list);

    timer_setup(&timer1, timer1_callback, 0);
    mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));

    timer_setup(&timer2, timer2_callback, 0);
    mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));

    return 0;

}
  • Create module init function which is exectued once the module is loaded to kernel.

static void __exit timer_exit(void)
{
    del_timer(&timer1);
        del_timer(&timer2);
        pr_info("Driver Unloaded...\n");
}
  • Create module exit function which will be executed once the module is unloaded from kernel.

static void timer1_callback(struct timer_list *timer)
{
    struct node *new_node;

    spin_lock(&sp);
    new_node = kmalloc(sizeof(struct node), GFP_KERNEL);
    if (!new_node) {
        pr_err("Failed to allocate memory for new_node\n");
        return;
    }
    list_for_each_entry(new_node, &my_list, list) {
        new_node->data++;
        pr_info("Executing timer function 1 : %d\n", new_node->data);
    }
    spin_unlock(&sp);

    mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
}

static void timer2_callback(struct timer_list *timer)
{
    struct node *new_node;

    spin_lock(&sp);
    list_for_each_entry(new_node, &my_list, list) {
        new_node->data++;
        pr_info("Executing timer function 2 : %d\n", new_node->data);
    }
    spin_unlock(&sp);
    mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
}
  • Create the timer APIs which will be executed once the work is scheduled.

module_init(timer_init);
module_exit(timer_exit);
  • Add the module init and exit functions which needs to executed once the module is loaded and unloaded.

  1#include <linux/init.h>
  2#include <linux/kernel.h>
  3#include <linux/module.h>
  4#include <linux/fs.h>
  5#include <linux/cdev.h>
  6#include <linux/device.h>
  7#include <linux/uaccess.h>
  8#include <linux/delay.h>
  9#include <linux/list.h>
 10#include <linux/slab.h>
 11#include <linux/timer.h>
 12#include <linux/jiffies.h>
 13
 14#define TIMEOUT 5000
 15
 16MODULE_LICENSE("GPL");
 17MODULE_AUTHOR("Linux_usr");
 18MODULE_DESCRIPTION("Two Timers with spinlock");
 19
 20struct node {
 21	int data;
 22	struct list_head list;
 23};
 24
 25LIST_HEAD(my_list);
 26
 27DEFINE_SPINLOCK(sp);
 28
 29static struct timer_list timer1;
 30static struct timer_list timer2;
 31
 32
 33static void timer1_callback(struct timer_list *timer)
 34{
 35	struct node *new_node;
 36
 37	spin_lock(&sp);
 38	new_node = kmalloc(sizeof(struct node), GFP_KERNEL);
 39	if (!new_node) {
 40		pr_err("Failed to allocate memory for new_node\n");
 41		return;
 42	}
 43	list_for_each_entry(new_node, &my_list, list) {
 44		new_node->data++;
 45		pr_info("Executing timer function 1 : %d\n", new_node->data);
 46	}
 47	spin_unlock(&sp);
 48
 49	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 50}
 51
 52static void timer2_callback(struct timer_list *timer)
 53{
 54	struct node *new_node;
 55
 56	spin_lock(&sp);
 57	list_for_each_entry(new_node, &my_list, list) {
 58		new_node->data++;
 59		pr_info("Executing timer function 2 : %d\n", new_node->data);
 60	}
 61	spin_unlock(&sp);
 62	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 63}
 64
 65static int __init timer_init(void)
 66{
 67	struct node *node1;
 68	struct node *node2;
 69
 70	pr_info("Driver Loaded...\n");
 71
 72	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 73	node1->data = 1;
 74	INIT_LIST_HEAD(&node1->list);
 75	list_add_tail(&node1->list, &my_list);
 76
 77	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 78	node2->data = 2;
 79	INIT_LIST_HEAD(&node2->list);
 80	list_add_tail(&node2->list, &my_list);
 81
 82	timer_setup(&timer1, timer1_callback, 0);
 83	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 84
 85	timer_setup(&timer2, timer2_callback, 0);
 86	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 87
 88	return 0;
 89}
 90
 91static void __exit timer_exit(void)
 92{
 93	del_timer(&timer1);
 94	del_timer(&timer2);
 95	pr_info("Driver Unloaded...\n");
 96
 97}
 98
 99module_init(timer_init);
100module_exit(timer_exit);
1obj-m += timer.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to compile the module.

$make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_timer modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
  CC [M]  $HOME/kernel_timer/timer.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  $HOME/kernel_timer/timer.mod.o
  LD [M]  $HOME/kernel_timer/timer.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
  • Run lsmod to check if timer.ko is generated or not.

$ ls -l
total 36
-rw-rw-r-- 1 test test  154 Feb 26 13:18 Makefile
-rw-rw-r-- 1 test test   47 Feb 26 13:18 modules.order
-rw-rw-r-- 1 test test    0 Feb 26 13:18 Module.symvers
-rw-rw-r-- 1 test test  820 Feb 26 13:18 timer.c
-rw-rw-r-- 1 test test 5880 Feb 26 13:18 timer.ko
-rw-rw-r-- 1 test test   47 Feb 26 13:18 timer.mod
-rw-rw-r-- 1 test test  919 Feb 26 13:18 timer.mod.c
-rw-rw-r-- 1 test test 3448 Feb 26 13:18 timer.mod.o
-rw-rw-r-- 1 test test 3320 Feb 26 13:18 timer.o
  • Run insmod to load the module to kernel.

$ sudo insmod ./timer.ko
  • Check the kernel messages to see if the module is loaded or not.

$ dmesg
[94508.895107] Driver Loaded...
[94514.046571] Executing timer function 2 : 2
[94514.046603] Executing timer function 2 : 3
[94514.046609] Executing timer function 1 : 3
[94514.046612] Executing timer function 1 : 4
[94519.166667] Executing timer function 1 : 4
[94519.166669] Executing timer function 1 : 5
[94519.166672] Executing timer function 2 : 5
[94519.166672] Executing timer function 2 : 6
[94524.286761] Executing timer function 2 : 6
[94524.286764] Executing timer function 2 : 7
[94524.286768] Executing timer function 1 : 7
[94524.286769] Executing timer function 1 : 8
  • Check the module is loaded using lsmod command

$ lsmod | grep timer
timer                  16384  0
  • Remove the kernel module using rmmod command.

$ sudo rmmod timer
  • Check the kernel messages to see if the module is unloaded or not.

$ dmesg
[94508.895107] Driver Loaded...
[94514.046571] Executing timer function 2 : 2
[94514.046603] Executing timer function 2 : 3
[94514.046609] Executing timer function 1 : 3
[94514.046612] Executing timer function 1 : 4
[94519.166667] Executing timer function 1 : 4
[94519.166669] Executing timer function 1 : 5
[94519.166672] Executing timer function 2 : 5
[94519.166672] Executing timer function 2 : 6
[94524.286761] Executing timer function 2 : 6
[94524.286764] Executing timer function 2 : 7
[94524.286768] Executing timer function 1 : 7
[94524.286769] Executing timer function 1 : 8
[94529.195453] Driver Unloaded...
  • Run modinfo to get information about the kernel module.

$ modinfo timer.ko
filename:       timer.ko
description:    Two timers with spinlock
author:         Linux_usr
license:        GPL
srcversion:     54CA24183446C7BE0B5F22A
depends:
retpoline:      Y
name:           timer
vermagic:       5.4.0-150-generic SMP mod_unload modversions

API

Learning

MODULE_DESCRIPTION

used to describe what the module does

DEFINE_SPINLOCK

To declare a spinlock variable and initialize it in one line

LIST_HEAD

A list is headed by a structure defined by the LIST_HEAD macro

spin_lock

Acquires given lock

list_for_each_entry

iterate over list of given type

spin_unlock

Releases given lock

INIT_LIST_HEAD

Initialize a list_head structure

list_add_tail

add a new entry

kmalloc

kernel memory allocation function

IS_ERR

used to check, Returns non-0 value if the ptr is an error, Otherwise 0 if it’s not an error

pr_alert

Print an alert-level message

PTR_ERR

used to print, Current value of the pointer

list_for_each_entry_safe

iterate over list of given type safe against removal of list entry

list_del

deletes entry from list

kfree

free previously allocated memory by kmalloc

timer_setup

To initialize the timer

mod_timer

To modify a timer’s timeout

del_timer

To stop a running timer

msecs_to_jiffies

To convert milliseconds