Module with Two Kthread - spinlock_irqsave
In this program, you are going to learn
How to create two threads ?
How to use below APIs ?
Here is the explanation of thread program part by part
This sections contains all the headers which contains the definition of the APIs used in the kernel thread program.
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/kfifo.h>
Initialize all the required thread and spinlock variables.
spinlock_t sp;
static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
static struct task_struct *thread1;
static struct task_struct *thread2;
static int thread_func1(void *data);
static int thread_func2(void *data);
DECLARE_KFIFO
macro is used to declare a kfifo and the associated buffer
The module marco is initialized which lists out the license of the module, author of the module and description of the module.
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux_usr");
MODULE_DESCRIPTION("Two kthreads with Spinlock_irqsave");
module-init function contains the instructions to create the thread and execute the thread via the corresponding thread function.
static int __init thread_init(void)
{
pr_info("Driver Loaded...\n");
INIT_KFIFO(my_fifo);
spin_lock_init(&sp);
thread1 = kthread_run(thread_func1, NULL, "thread1");
if (IS_ERR(thread1)) {
pr_alert("Failed to create thread1\n");
return PTR_ERR(thread1);
}
pr_info("Kthread1 Created Successfully....\n");
thread2 = kthread_run(thread_func2, NULL, "thread2");
if (IS_ERR(thread2)) {
pr_alert("Failed to create device\n");
return PTR_ERR(thread2);
}
pr_info("Kthread2 Created Successfully...\n");
return 0;
}
module-exit function is used to stop and clean up all the thread process going on from the start of module-init function.
static void __exit thread_exit(void)
{
if (thread1) {
kthread_stop(thread1);
thread1 = NULL;
}
if (thread2) {
kthread_stop(thread2);
thread2 = NULL;
}
pr_info("Driver Unloaded...\n");
}
thread function will execute a print statement until the thread is stopped and until that the thread will execute.
int thread_func1(void *data)
{
char val = 'H';
char value;
unsigned long flags;
while (!kthread_should_stop()) {
spin_lock_irqsave(&sp, flags);
if (!kfifo_is_empty(&my_fifo)) {
kfifo_out(&my_fifo, &value, sizeof(value));
pr_info("Thread 1 : Dequeue : %c\n", value);
val--;
}
spin_unlock_irqrestore(&sp, flags);
msleep(3000);
}
return 0;
}
int thread_func2(void *data)
{
char value = 'A';
char val = 'H';
unsigned long flags;
while (val > 'A' && !kthread_should_stop()) {
spin_lock_irqsave(&sp, flags);
if (!kfifo_is_full(&my_fifo)) {
kfifo_in(&my_fifo, &value, sizeof(value));
pr_info("Thread 2 : Enqueue : %c\n", value);
value++;
val--;
}
spin_unlock_irqrestore(&sp, flags);
msleep(3000);
}
return 0;
}
init and exit function name should be added in the module_init and module_exit parameter.
module_init(thread_init);
module_exit(thread_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/kthread.h>
9#include <linux/delay.h>
10#include <linux/interrupt.h>
11#include <linux/list.h>
12#include <linux/slab.h>
13
14MODULE_LICENSE("GPL");
15MODULE_AUTHOR("Linux_usr");
16MODULE_DESCRIPTION("Spinlock");
17
18DEFINE_SPINLOCK(sp);
19
20static struct task_struct *thread1;
21static struct task_struct *thread2;
22
23struct node {
24 int data;
25 struct list_head list;
26};
27
28LIST_HEAD(my_list);
29
30static int thread_fun1(void *data)
31{
32 unsigned long flags;
33 struct node *new_node;
34
35 while (!kthread_should_stop()) {
36 spin_lock_irqsave(&sp, flags);
37 list_for_each_entry(new_node, &my_list, list) {
38 new_node->data++;
39 pr_info("Executing thread function 1 : %d\n", new_node->data);
40 }
41 spin_unlock_irqrestore(&sp, flags);
42 msleep(5000);
43 }
44
45 return 0;
46}
47
48static int thread_fun2(void *data)
49{
50 struct node *new_node;
51 unsigned long flags;
52
53 while (!kthread_should_stop()) {
54 spin_lock_irqsave(&sp, flags);
55 list_for_each_entry(new_node, &my_list, list) {
56 new_node->data++;
57 pr_info("Executing thread function 2 : %d\n", new_node->data);
58 }
59 spin_unlock_irqrestore(&sp, flags);
60 msleep(5000);
61 }
62
63 return 0;
64}
65
66static int __init kthread_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 thread1 = kthread_run(thread_fun1, NULL, "thread1");
84 if (IS_ERR(thread1)) {
85 pr_alert("Failed to create thraed1");
86 return PTR_ERR(thread1);
87 }
88
89 thread2 = kthread_run(thread_fun2, NULL, "thread2");
90 if (IS_ERR(thread1)) {
91 pr_alert("Failed to create thraed2");
92 return PTR_ERR(thread2);
93 }
94
95 return 0;
96}
97
98static void __exit kthread_exit(void)
99{
100 struct node *curr_node, *next_node;
101
102 kthread_stop(thread1);
103 kthread_stop(thread2);
104 list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
105 list_del(&curr_node->list);
106 kfree(curr_node);
107 }
108 pr_info("Device Driver Unloaded...\n");
109}
110
111module_init(kthread_init);
112module_exit(kthread_exit);
1obj-m += threads.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_thread modules
make[1]: Entering directory '/usr/src/linux-headers-5.4.0-150-generic'
CC [M] $HOME/kernel_thread/thread.o
Building modules, stage 2.
MODPOST 1 modules
CC [M] $HOME/kernel_thread/thread.mod.o
LD [M] $HOME/kernel_thread/thread.ko
make[1]: Leaving directory '/usr/src/linux-headers-5.4.0-150-generic'
Check if thread.ko is generated or not.
$ ls -l
total 408
-rwxrwxrwx 1 test test 1388 Sep 12 16:13 threads.c
-rw-rw-r-- 1 test test 194592 Sep 12 16:14 threads.ko
-rw-rw-r-- 1 test test 142 Sep 12 16:14 threads.mod
-rw-rw-r-- 1 test test 1066 Sep 12 16:14 threads.mod.c
-rw-rw-r-- 1 test test 93872 Sep 12 16:14 threads.mod.o
-rw-rw-r-- 1 test test 102136 Sep 12 16:14 threads.o
-rwxrwxrwx 1 test test 182 Sep 12 16:08 Makefile
-rw-rw-r-- 1 test test 142 Sep 12 16:14 modules.order
-rw-rw-r-- 1 test test 0 Sep 12 16:14 Module.symvers
-rwxrwxrwx 1 test test 2144 Sep 12 16:12 README
Run insmod to load the kernel module to linux kernel.
$ sudo insmod ./threads.ko
Check for kernel messages to verify if the module is loaded or not.
$ dmesg
[ 1188.223137] Driver Loaded...
[ 1188.223179] Kthread1 Created Successfully....
[ 1188.223196] Kthread2 Created Successfully...
[ 1188.223198] Thread 2 : Enqueue : A
[ 1191.231771] Thread 1 : Dequeue : A
[ 1191.231777] Thread 2 : Enqueue : B
[ 1194.303938] Thread 2 : Enqueue : C
[ 1194.303943] Thread 1 : Dequeue : B
[ 1197.376027] Thread 1 : Dequeue : C
[ 1197.376032] Thread 2 : Enqueue : D
[ 1200.448106] Thread 2 : Enqueue : E
[ 1200.448111] Thread 1 : Dequeue : D
[ 1203.520151] Thread 2 : Enqueue : F
[ 1203.520153] Thread 1 : Dequeue : E
[ 1206.592284] Thread 2 : Enqueue : G
[ 1206.592289] Thread 1 : Dequeue : F
Run lsmod to check for the kernel module.
$ lsmod | grep threads
threads 16384 0
Unload the module using rmmod command.
$ sudo rmmod threads
Check kernel messages to see if the module is unloaded or not.
$ dmesg
[ 1188.223137] Driver Loaded...
[ 1188.223179] Kthread1 Created Successfully....
[ 1188.223196] Kthread2 Created Successfully...
[ 1188.223198] Thread 2 : Enqueue : A
[ 1191.231771] Thread 1 : Dequeue : A
[ 1191.231777] Thread 2 : Enqueue : B
[ 1194.303938] Thread 2 : Enqueue : C
[ 1194.303943] Thread 1 : Dequeue : B
[ 1197.376027] Thread 1 : Dequeue : C
[ 1197.376032] Thread 2 : Enqueue : D
[ 1200.448106] Thread 2 : Enqueue : E
[ 1200.448111] Thread 1 : Dequeue : D
[ 1203.520151] Thread 2 : Enqueue : F
[ 1203.520153] Thread 1 : Dequeue : E
[ 1206.592284] Thread 2 : Enqueue : G
[ 1206.592289] Thread 1 : Dequeue : F
[ 1209.664431] Driver Unloaded...
We can also get the information about the module using
modinfo
command.
$ modinfo threads.ko
filename: threads.ko
description: KFIFO - Spinlock
author: Linux_usr
license: GPL
srcversion: 06B5EDF90D5A03419B7784B
depends:
retpoline: Y
name: threads
vermagic: 5.4.0-150-generic SMP mod_unload modversions
# |
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 |
kthread_run |
create and wake a thread |
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 |
kthread_stop |
stop a thread created by kthread_create. |
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 |