Module with Two Kthread - spinlock bh
- In this program, you are going to learn 
How to create two threads ?
- How to use below APIs ? 
- Here is the part by part explanation of the above kernel thread program. 
- 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>
- 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("KFIFO - Spinlock");
- 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_KFIFOis a macro used to declare a kfifo and the associated buffer.
- 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;
}
- In this init function, - two threads thread1,thread2 is created and the corresponding thread functions for both the threads are passed as arguments. 
- spinlock variable - spis initialized using- spin_lock_init
- kfifo variable - myfifois initialized using- INIT_KFIFO
 
- 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;
    while (!kthread_should_stop()) {
        spin_lock_bh(&sp);
        if (!kfifo_is_empty(&my_fifo)) {
            kfifo_out(&my_fifo, &value, sizeof(value));
            pr_info("Thread 1 : Dequeue : %c\n", value);
            val--;
        }
        spin_unlock_bh(&sp);
        msleep(3000);
    }
    return 0;
}
int thread_func2(void *data)
{
    char value = 'A';
    char val = 'H';
    while (val > 'A' && !kthread_should_stop()) {
        spin_lock_bh(&sp);
        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_bh(&sp);
        msleep(3000);
    }
    return 0;
}
- In function thread_func1, - kfifo elements are removed from the kfifo with the help of spinlock. 
- kfifo_is_emptyis used to check if the kfifo is empty or not.
- kfifo_outis used to remove the element (dequeue) from kfifo.
 
- In function thread_func2, - kfifo elements are inserted to kfifo with the help of spinlock. 
- kfifo_is_fullis used to check if the kfifo is full or not.
- kfifo_inis used to insert the element (enqueue) to kfifo.
 
- init and exit function name should be added in the module_init and module_exit parameter. 
module_init(kthread_init);
module_exit(kthread_exit);
  1#include <linux/init.h>
  2#include <linux/kernel.h>
  3#include <linux/module.h>
  4#include <linux/fs.h>
  5#include <linux/uaccess.h>
  6#include <linux/cdev.h>
  7#include <linux/device.h>
  8#include <linux/kthread.h>
  9#include <linux/delay.h>
 10#include <linux/kfifo.h>
 11
 12#define FIFO_SIZE 32
 13
 14MODULE_LICENSE("GPL");
 15MODULE_AUTHOR("Linux_usr");
 16MODULE_DESCRIPTION("KFIFO - Spinlock");
 17
 18spinlock_t sp;
 19
 20static DECLARE_KFIFO(my_fifo, char, FIFO_SIZE);
 21
 22static struct task_struct *thread1;
 23static struct task_struct *thread2;
 24
 25static int thread_func1(void *data);
 26static int thread_func2(void *data);
 27
 28int thread_func1(void *data)
 29{
 30	char val = 'H';
 31	char value;
 32
 33	while (!kthread_should_stop()) {
 34		spin_lock_bh(&sp);
 35		if (!kfifo_is_empty(&my_fifo)) {
 36			kfifo_out(&my_fifo, &value, sizeof(value));
 37			pr_info("Thread 1 : Dequeue : %c\n", value);
 38			val--;
 39		}
 40		spin_unlock_bh(&sp);
 41		msleep(3000);
 42	}
 43	return 0;
 44}
 45
 46int thread_func2(void *data)
 47{
 48	char value = 'A';
 49	char val = 'H';
 50
 51	while (val > 'A' && !kthread_should_stop()) {
 52		spin_lock_bh(&sp);
 53		if (!kfifo_is_full(&my_fifo)) {
 54			kfifo_in(&my_fifo, &value, sizeof(value));
 55			pr_info("Thread 2 : Enqueue : %c\n", value);
 56			value++;
 57			val--;
 58		}
 59		spin_unlock_bh(&sp);
 60		msleep(3000);
 61	}
 62	return 0;
 63}
 64
 65static int __init thread_init(void)
 66{
 67	pr_info("Driver Loaded...\n");
 68
 69	INIT_KFIFO(my_fifo);
 70	spin_lock_init(&sp);
 71
 72	thread1 = kthread_run(thread_func1, NULL, "thread1");
 73	if (IS_ERR(thread1)) {
 74		pr_alert("Failed to create thread1\n");
 75		return PTR_ERR(thread1);
 76	}
 77	pr_info("Kthread1 Created Successfully....\n");
 78
 79	thread2 = kthread_run(thread_func2, NULL, "thread2");
 80	if (IS_ERR(thread2)) {
 81		pr_alert("Failed to create device\n");
 82		return PTR_ERR(thread2);
 83	}
 84	pr_info("Kthread2 Created Successfully...\n");
 85	return 0;
 86}
 87
 88static void __exit thread_exit(void)
 89{
 90	if (thread1) {
 91		kthread_stop(thread1);
 92		thread1 = NULL;
 93	}
 94
 95	if (thread2) {
 96		kthread_stop(thread2);
 97		thread2 = NULL;
 98	}
 99
100	pr_info("Driver Unloaded...\n");
101}
102
103module_init(thread_init);
104module_exit(thread_exit);
 1obj-m += threads.o
 2 
 3KDIR = /lib/modules/$(shell uname -r)/build
 4 
 5 
 6all:
 7	make -C $(KDIR)  M=$(shell pwd) modules
 8 
 9clean:
10	make -C $(KDIR)  M=$(shell 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 threads.ko is generated or not using ls command. 
$ 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 command to load the kernel module in linux module. 
sudo insmod ./threads.ko
- Run dmesg to check the kernel message and verify if the module is loaded or not. 
$ dmesg
[  472.940667] Driver Loaded...
[  472.940697] Kthread1 Created Successfully....
[  472.940714] Kthread2 Created Successfully...
[  472.940716] Thread 2 : Enqueue : A
[  475.955143] Thread 1 : Dequeue : A
[  475.955148] Thread 2 : Enqueue : B
[  479.027210] Thread 1 : Dequeue : B
[  479.027216] Thread 2 : Enqueue : C
[  482.099291] Thread 2 : Enqueue : D
[  482.099298] Thread 1 : Dequeue : C
[  485.171337] Thread 1 : Dequeue : D
[  485.171345] Thread 2 : Enqueue : E
[  488.243423] Thread 1 : Dequeue : E
[  488.243429] Thread 2 : Enqueue : F
[  491.315460] Thread 1 : Dequeue : F
[  491.315462] Thread 2 : Enqueue : G
- Check lsmod output to find whether the threads kernel module is loaded or not. 
$ lsmod | grep threads
threads                16384  0
- Run rmmod command to unload the kernel module from linux kernel. 
$ sudo rmmod threads
- Check the kernel messages to see if the module is unloaded or not. 
$ dmesg
[  472.940667] Driver Loaded...
[  472.940697] Kthread1 Created Successfully....
[  472.940714] Kthread2 Created Successfully...
[  472.940716] Thread 2 : Enqueue : A
[  475.955143] Thread 1 : Dequeue : A
[  475.955148] Thread 2 : Enqueue : B
[  479.027210] Thread 1 : Dequeue : B
[  479.027216] Thread 2 : Enqueue : C
[  482.099291] Thread 2 : Enqueue : D
[  482.099298] Thread 1 : Dequeue : C
[  485.171337] Thread 1 : Dequeue : D
[  485.171345] Thread 2 : Enqueue : E
[  488.243423] Thread 1 : Dequeue : E
[  488.243429] Thread 2 : Enqueue : F
[  491.315460] Thread 1 : Dequeue : F
[  491.315462] Thread 2 : Enqueue : G
[  494.323692] Driver Unloaded...
- We can also get the information about the module using - modinfocommand.
$ modinfo threads.ko
filename:       threads.ko
description:    KFIFO - Spinlock
author:         Linux_usr
license:        GPL
srcversion:     71580D1D74B1EEEB57712DE
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 | 
| spin_lock_bh | Acquires given lock | 
| spin_unlock_bh | Releases given lock | 
| 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. | 
| kfree | free previously allocated memory by kmalloc | 
| DECLARE_KFIFO | To declare a kfifo variable | 
| kfifo_is_empty | used to check if the kfifo is empty or not, Returns 1 if empty otherwise 0 | 
| kfifo_out | to dequeue an element from kfifo | 
| kfifo_is_full | used to check if it is full, returns 1 if full otherwise 0 | 
| kfifo_in | to enqueue an element to kfifo | 
| INIT_KFIFO | To initialize kfifo |