Module with Two Timers - spinlock irqsave

  • 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_irqsave(&sp, flags);
    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_irqrestore(&sp, flags);

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

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

    spin_lock_irqsave(&sp, flags);
    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_irqrestore(&sp, flags);
    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	unsigned long flags;
 36	struct node *new_node;
 37
 38	spin_lock_irqsave(&sp, flags);
 39	new_node = kmalloc(sizeof(struct node), GFP_KERNEL);
 40	if (!new_node) {
 41		pr_err("Failed to allocate memory for new_node\n");
 42		return;
 43	}
 44	list_for_each_entry(new_node, &my_list, list) {
 45		new_node->data++;
 46		pr_info("Executing timer function 1 : %d\n", new_node->data);
 47	}
 48	spin_unlock_irqrestore(&sp, flags);
 49
 50	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 51}
 52
 53static void timer2_callback(struct timer_list *timer)
 54{
 55	unsigned long flags;
 56	struct node *new_node;
 57
 58	spin_lock_irqsave(&sp, flags);
 59	list_for_each_entry(new_node, &my_list, list) {
 60		new_node->data++;
 61		pr_info("Executing timer function 2 : %d\n", new_node->data);
 62	}
 63	spin_unlock_irqrestore(&sp, flags);
 64	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 65}
 66
 67static int __init timer_init(void)
 68{
 69	struct node *node1;
 70	struct node *node2;
 71
 72	pr_info("Driver Loaded...\n");
 73
 74	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 75	node1->data = 1;
 76	INIT_LIST_HEAD(&node1->list);
 77	list_add_tail(&node1->list, &my_list);
 78
 79	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 80	node2->data = 2;
 81	INIT_LIST_HEAD(&node2->list);
 82	list_add_tail(&node2->list, &my_list);
 83
 84	timer_setup(&timer1, timer1_callback, 0);
 85	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 86
 87	timer_setup(&timer2, timer2_callback, 0);
 88	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 89
 90	return 0;
 91}
 92
 93static void __exit timer_exit(void)
 94{
 95	del_timer(&timer1);
 96	del_timer(&timer2);
 97	pr_info("Driver Unloaded...\n");
 98
 99}
100
101module_init(timer_init);
102module_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
[94753.049905] Driver Loaded...
[94758.275106] Executing timer function 2 : 2
[94758.275110] Executing timer function 2 : 3
[94758.275145] Executing timer function 1 : 3
[94758.275148] Executing timer function 1 : 4
[94763.395185] Executing timer function 1 : 4
[94763.395188] Executing timer function 1 : 5
[94763.395206] Executing timer function 2 : 5
[94763.395207] Executing timer function 2 : 6
[94768.515256] Executing timer function 2 : 6
[94768.515258] Executing timer function 2 : 7
[94768.515260] Executing timer function 1 : 7
[94768.515261] Executing timer function 1 : 8
[94773.635390] Executing timer function 1 : 8
[94773.635394] Executing timer function 1 : 9
[94773.635433] Executing timer function 2 : 9
[94773.635440] Executing timer function 2 : 10
  • 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
[94753.049905] Driver Loaded...
[94758.275106] Executing timer function 2 : 2
[94758.275110] Executing timer function 2 : 3
[94758.275145] Executing timer function 1 : 3
[94758.275148] Executing timer function 1 : 4
[94763.395185] Executing timer function 1 : 4
[94763.395188] Executing timer function 1 : 5
[94763.395206] Executing timer function 2 : 5
[94763.395207] Executing timer function 2 : 6
[94768.515256] Executing timer function 2 : 6
[94768.515258] Executing timer function 2 : 7
[94768.515260] Executing timer function 1 : 7
[94768.515261] Executing timer function 1 : 8
[94773.635390] Executing timer function 1 : 8
[94773.635394] Executing timer function 1 : 9
[94773.635433] Executing timer function 2 : 9
[94773.635440] Executing timer function 2 : 10
[94776.568138] 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:     5F51A1882365F26AE733170
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_irqsave

Acquires given lock

list_for_each_entry

iterate over list of given type

spin_unlock_irqrestore

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