Module with Two Timers - spinlock bh

  • 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/kfifo.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("Timers - kfifo -spinlock");
  • Add the modules macro which gives information about the license, author and description of the modules.

static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);

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)
{
    pr_info("Driver Loaded...\n");

    INIT_KFIFO(my_fifo);

    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)
{
    char value = 'A';

    spin_lock_bh(&sp);
    if (!kfifo_is_full(&my_fifo)) {
        kfifo_in(&my_fifo, &value, sizeof(value));
        pr_info("Timer function 1 : Enqueue : %c\n",  value);
    }
    spin_unlock_bh(&sp);

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

static void timer2_callback(struct timer_list *timer)
{
    char value;

    spin_lock_bh(&sp);
    if (!kfifo_is_empty(&my_fifo)) {
        kfifo_out(&my_fifo, &value, sizeof(value));
        pr_info("Timer function 2 : Dequeue : %c\n", value);
    }
    spin_unlock_bh(&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/kfifo.h>
10#include <linux/slab.h>
11#include <linux/timer.h>
12#include <linux/jiffies.h>
13#include <linux/spinlock.h>
14
15#define TIMEOUT 5000
16#define FIFO_SIZE 2
17
18MODULE_LICENSE("GPL");
19MODULE_AUTHOR("Linux_usr");
20MODULE_DESCRIPTION("Timers - kfifo - spinlock");
21
22static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
23
24static DEFINE_SPINLOCK(sp);
25
26static struct timer_list timer1;
27static struct timer_list timer2;
28
29
30static void timer1_callback(struct timer_list *timer)
31{
32	char value = 'A';
33
34	spin_lock_bh(&sp);
35	if (!kfifo_is_full(&my_fifo)) {
36		kfifo_in(&my_fifo, &value, sizeof(value));
37		pr_info("Timer function 1 : Enqueue : %c\n",  value);
38	}
39	spin_unlock_bh(&sp);
40
41	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
42}
43
44static void timer2_callback(struct timer_list *timer)
45{
46	char value;
47
48	spin_lock_bh(&sp);
49	if (!kfifo_is_empty(&my_fifo)) {
50		kfifo_out(&my_fifo, &value, sizeof(value));
51		pr_info("Timer function 2 : Dequeue : %c\n", value);
52	}
53	spin_unlock_bh(&sp);
54
55	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
56}
57
58
59static int __init timer_init(void)
60{
61	pr_info("Driver Loaded...\n");
62
63	INIT_KFIFO(my_fifo);
64
65	timer_setup(&timer1, timer1_callback, 0);
66	mod_timer(&timer1, jiffies + msecs_to_jiffies(TIMEOUT));
67
68	timer_setup(&timer2, timer2_callback, 0);
69	mod_timer(&timer2, jiffies + msecs_to_jiffies(TIMEOUT));
70
71	return 0;
72}
73
74static void __exit timer_exit(void)
75{
76	del_timer_sync(&timer1);
77	del_timer_sync(&timer2);
78	
79	pr_info("Driver Unloaded...\n");
80
81}
82
83module_init(timer_init);
84module_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
[ 9415.330913] Driver Loaded...
[ 9420.370837] Timer function 1 : Enqueue : A
[ 9425.490929] Timer function 1 : Enqueue : A
[ 9425.490952] Timer function 2 : Dequeue : A
[ 9430.615043] Timer function 2 : Dequeue : A
[ 9430.615047] Timer function 1 : Enqueue : A
[ 9435.731173] Timer function 1 : Enqueue : A
[ 9435.731221] Timer function 2 : Dequeue : A
[ 9440.851305] Timer function 2 : Dequeue : A
[ 9440.851342] Timer function 1 : Enqueue : A
  • 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
[ 9415.330913] Driver Loaded...
[ 9420.370837] Timer function 1 : Enqueue : A
[ 9425.490929] Timer function 1 : Enqueue : A
[ 9425.490952] Timer function 2 : Dequeue : A
[ 9430.615043] Timer function 2 : Dequeue : A
[ 9430.615047] Timer function 1 : Enqueue : A
[ 9435.731173] Timer function 1 : Enqueue : A
[ 9435.731221] Timer function 2 : Dequeue : A
[ 9440.851305] Timer function 2 : Dequeue : A
[ 9440.851342] Timer function 1 : Enqueue : A
[ 9444.759962] Driver Unloaded...
  • Run modinfo to get information about the kernel module.

$ modinfo timer.ko
filename:       timer.ko
description:    Timers - kfifo - spinlock
author:         Linux_usr
license:        GPL
srcversion:     F23BAAF85708729C01BA612
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_bh

Acquires given lock

list_for_each_entry

iterate over list of given type

spin_unlock_bh

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

DECLARE_KFIFO

macro to declare a kfifo and the associated buffer

INIT_KFIFO

to initialize kfifo

kfifo_in

enqueue elements to kfifo

kfifo_out

dequeue elemenst to kfifo

kfifo_is_empty

used to check if kfifo is empty, returns 1 if true otherwise 0

kfifo_is_full

used to check if kfifo is full, returns 1 if true otherwise 0