Module with Two Tasklet - semaphore

  • In this program, you are going to learn

  • How to schedule the tasklet ?

  • Here is the explanation of the program part by part

  • Include all the required header files to refer to the APIs used in the program.

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/slab.h>
#include <linux/semaphore.h>
  • Add the macro modules to list out the information about the module such as license, description and author.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Tasklets - semaphore");
  • Intialize all the required variables and functions.

static struct semaphore sema;

void tasklet_fun1(unsigned long data);
void tasklet_fun2(unsigned long data);

static DECLARE_TASKLET(tasklet1, (void *)tasklet_fun1, 0);
static DECLARE_TASKLET(tasklet2, (void *)tasklet_fun2, 0);

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

LIST_HEAD(my_list);
  • LIST_HEAD is used to initialize a list head structure.

  • Add the module init function which contains instructions executed once when the module is loaded.

static int __init tasklets_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);

    sema_init(&sema, 1);

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

    tasklet_schedule(&tasklet1);
    tasklet_schedule(&tasklet2);

    return 0;
}
  • In this module init function,

    • initialize two nodes node1, node2 and allocate memory dynamically.

    • add the nodes to the list.

    • initiate tasklet with tasklet_schedule

  • Add the instructions for module exit function which will be executed once the module is unloaded from kernel.

static void __exit tasklets_exit(void)
{
    struct node *curr_node, *next_node;

    list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
        list_del(&curr_node->list);
        kfree(curr_node);
    }
    pr_info("Driver Unloaded...\n");
}
  • Delete and free all the entries from the list.

  • Add the task handler APIs for both tasklet1, tasklet2 which will be executed once the tasklet is scheduled.

void tasklet_fun1(unsigned long data)
{
    struct node *new_node;

    down(&sema);
    list_for_each_entry(new_node, &my_list, list) {
        new_node->data++;
        pr_info("Executing tasklet function 1 : %d\n", new_node->data);
    }
    up(&sema);
}

void tasklet_fun2(unsigned long data)
{
    struct node *new_node;

    down(&sema);
    list_for_each_entry(new_node, &my_list, list) {
        new_node->data++;
        pr_info("Executing tasklet function 2 : %d\n", new_node->data);
    }
    up(&sema);
}
  • In this tasklet handler APIs,

    • Add the node for list by locking and unlocking with the help of semaphore.

    • locking and unlocking can be done with the help of down up respectively.

  • Add the init and exit functions in module_init and module_exit respectively.

module_init(tasklets_init);
module_exit(tasklets_exit);
 1#include <linux/init.h>
 2#include <linux/kernel.h>
 3#include <linux/module.h>
 4#include <linux/cdev.h>
 5#include <linux/device.h>
 6#include <linux/fs.h>
 7#include <linux/uaccess.h>
 8#include <linux/delay.h>
 9#include <linux/interrupt.h>
10#include <linux/list.h>
11#include <linux/slab.h>
12#include <linux/semaphore.h>
13
14MODULE_LICENSE("GPL");
15MODULE_AUTHOR("Linux_usr");
16MODULE_DESCRIPTION("Tasklets - Semaphore");
17
18static struct semaphore sema;
19
20void tasklet_fun1(unsigned long data);
21void tasklet_fun2(unsigned long data);
22
23static DECLARE_TASKLET(tasklet1, (void *)tasklet_fun1, 0);
24static DECLARE_TASKLET(tasklet2, (void *)tasklet_fun2, 0);
25
26struct node {
27	int data;
28	struct list_head list;
29};
30
31LIST_HEAD(my_list);
32
33void tasklet_fun1(unsigned long data)
34{
35	struct node *new_node;
36	
37	down(&sema);
38	list_for_each_entry(new_node, &my_list, list) {
39		new_node->data++;
40		pr_info("Executing tasklet function 1 : %d\n", new_node->data);
41	}
42	up(&sema);
43}
44
45void tasklet_fun2(unsigned long data)
46{
47	struct node *new_node;
48
49	down(&sema);
50	list_for_each_entry(new_node, &my_list, list) {
51		new_node->data++;
52		pr_info("Executing tasklet function 2 : %d\n", new_node->data);
53	}
54	up(&sema);
55}
56
57static int __init tasklets_init(void)
58{
59	struct node *node1;
60	struct node *node2;
61
62	pr_info("Driver Loaded...\n");
63
64	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
65	node1->data = 1;
66	INIT_LIST_HEAD(&node1->list);
67	list_add_tail(&node1->list, &my_list);
68
69	sema_init(&sema, 1);
70
71	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
72	node2->data = 2;
73	INIT_LIST_HEAD(&node2->list);
74	list_add_tail(&node2->list, &my_list);
75
76	tasklet_schedule(&tasklet1);
77	tasklet_schedule(&tasklet2);
78
79	return 0;
80}
81
82static void __exit tasklets_exit(void)
83{
84	struct node *curr_node, *next_node;
85
86	list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
87		list_del(&curr_node->list);
88		kfree(curr_node);
89	}
90	pr_info("Driver Unloaded...\n");
91}
92
93module_init(tasklets_init);
94module_exit(tasklets_exit);
1obj-m += tasklets.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 kernel module.

$ make
make -C /lib/modules/5.4.0-150-generic/build M=$HOME/kernel_tasklets modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
  CC [M]  $HOME/kernel_tasklets/tasklets.o
  Building modules, stage 2.
  MODPOST 1 modules
  CC [M]  $HOME/kernel_tasklets/tasklets.mod.o
  LD [M]  $HOME/kernel_tasklets/tasklets.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
  • Check if tasklets.ko is generated or not using ls command.

$ 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 tasklets.c
-rw-rw-r-- 1 test test 5880 Feb 26 13:18 tasklets.ko
-rw-rw-r-- 1 test test   47 Feb 26 13:18 tasklets.mod
-rw-rw-r-- 1 test test  919 Feb 26 13:18 tasklets.mod.c
-rw-rw-r-- 1 test test 3448 Feb 26 13:18 tasklets.mod.o
-rw-rw-r-- 1 test test 3320 Feb 26 13:18 tasklets.o
  • Run insmod command to load the module in kernel.

$ sudo insmod ./tasklets.ko
  • Check if the module is loaded or not by seeing the kernel messages.

$ dmesg
[34802.992048] Driver Loaded...
[34802.992060] Executing tasklet function 1 : 2
[34802.992061] Executing tasklet function 1 : 3
[34802.992061] Executing tasklet function 2 : 3
[34802.992062] Executing tasklet function 2 : 4
  • This can also be verified by using the lsmod command.

$ lsmod | grep tasklets
tasklets               16384  0
  • Unload the module by using rmmod command.

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

$ dmesg
[34802.992048] Driver Loaded...
[34802.992060] Executing tasklet function 1 : 2
[34802.992061] Executing tasklet function 1 : 3
[34802.992061] Executing tasklet function 2 : 3
[34802.992062] Executing tasklet function 2 : 4
[34812.872064] Driver Unloaded...
  • List out the information about the kernel module using modinfo

$ modinfo tasklets.ko
filename:       tasklets.ko
description:    Tasklets - semaphore
author:         Linux_usr
license:        GPL
srcversion:     17465E688EC46A276B8B348
depends:
retpoline:      Y
name:           tasklets
vermagic:       5.4.0-150-generic SMP mod_unload modversions

API

Learning

MODULE_DESCRIPTION

used to describe what the module does

DECLARE_TASKLET

To declare tasklet variable

sema_init

To initialize semaphore variable

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

tasklet_schedule

to schedule the tasklet