Module with Two Timers - semaphore

  • 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 <linux/semaphore.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 semaphore");
  • 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);

static struct semaphore sema;

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);

    sema_init(&sema, 1);

    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;

    down(&sema);
    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);
    }
    up(&sema);

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

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

    down(&sema);
    list_for_each_entry(new_node, &my_list, list) {
        new_node->data++;
        pr_info("Executing timer function 2 : %d\n", new_node->data);
    }
    up(&sema);
    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#include <linux/semaphore.h>
 14
 15#define TIMEOUT 5000
 16
 17MODULE_LICENSE("GPL");
 18MODULE_AUTHOR("Linux_usr");
 19MODULE_DESCRIPTION("Two Timers with mutex");
 20
 21struct node {
 22	int data;
 23	struct list_head list;
 24};
 25
 26LIST_HEAD(my_list);
 27
 28static struct semaphore sema;
 29
 30static struct timer_list timer1;
 31static struct timer_list timer2;
 32
 33
 34static void timer1_callback(struct timer_list *timer)
 35{
 36	struct node *new_node;
 37
 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	down(&sema);
 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	up(&sema);
 49
 50	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 51}
 52
 53static void timer2_callback(struct timer_list *timer)
 54{
 55	struct node *new_node;
 56
 57	down(&sema);
 58	list_for_each_entry(new_node, &my_list, list) {
 59		new_node->data++;
 60		pr_info("Executing timer function 2 : %d\n", new_node->data);
 61	}
 62	up(&sema);
 63	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 64}
 65
 66static int __init timer_init(void)
 67{
 68	struct node *node1;
 69	struct node *node2;
 70
 71	pr_info("Driver Loaded...\n");
 72
 73	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 74	node1->data = 1;
 75	INIT_LIST_HEAD(&node1->list);
 76	list_add_tail(&node1->list, &my_list);
 77
 78	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 79	node2->data = 2;
 80	INIT_LIST_HEAD(&node2->list);
 81	list_add_tail(&node2->list, &my_list);
 82
 83	sema_init(&sema, 1);
 84
 85	timer_setup(&timer1, timer1_callback, 0);
 86	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
 87
 88	timer_setup(&timer2, timer2_callback, 0);
 89	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
 90
 91	return 0;
 92}
 93
 94static void __exit timer_exit(void)
 95{
 96	del_timer(&timer1);
 97	del_timer(&timer2);
 98	pr_info("Driver Unloaded...\n");
 99
100}
101
102module_init(timer_init);
103module_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
[95497.842813] Driver Loaded...
[95502.992948] Executing timer function 2 : 2
[95502.992977] Executing timer function 2 : 3
[95502.992984] Executing timer function 1 : 3
[95502.993003] Executing timer function 1 : 4
[95508.113030] Executing timer function 1 : 4
[95508.113034] Executing timer function 1 : 5
[95508.113039] Executing timer function 2 : 5
[95508.113041] Executing timer function 2 : 6
[95513.233147] Executing timer function 2 : 6
[95513.233150] Executing timer function 2 : 7
[95513.233155] Executing timer function 1 : 7
[95513.233156] Executing timer function 1 : 8
[95518.353206] Executing timer function 1 : 8
[95518.353208] Executing timer function 1 : 9
[95518.353211] Executing timer function 2 : 9
[95518.353212] 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
[95497.842813] Driver Loaded...
[95502.992948] Executing timer function 2 : 2
[95502.992977] Executing timer function 2 : 3
[95502.992984] Executing timer function 1 : 3
[95502.993003] Executing timer function 1 : 4
[95508.113030] Executing timer function 1 : 4
[95508.113034] Executing timer function 1 : 5
[95508.113039] Executing timer function 2 : 5
[95508.113041] Executing timer function 2 : 6
[95513.233147] Executing timer function 2 : 6
[95513.233150] Executing timer function 2 : 7
[95513.233155] Executing timer function 1 : 7
[95513.233156] Executing timer function 1 : 8
[95518.353206] Executing timer function 1 : 8
[95518.353208] Executing timer function 1 : 9
[95518.353211] Executing timer function 2 : 9
[95518.353212] Executing timer function 2 : 10
[95518.410551] Driver Unloaded...
  • Run modinfo to get information about the kernel module.

$ modinfo timer.ko
filename:       timer.ko
description:    Two timers with semaphore
author:         Linux_usr
license:        GPL
srcversion:     A15F713079F2266371FBAEA
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

sema_init

To declare a semaphore variable and initialize it in one line

LIST_HEAD

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

down

Acquires given lock

list_for_each_entry

iterate over list of given type

up

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