Module with Two Kthread - spinlock

  • In this program, you are going to learn

How to create two threads ?

  • 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_KFIFO is 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 sp is initialized using spin_lock_init

    • kfifo variable myfifo is 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(&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(&sp);
        msleep(3000);
    }
    return 0;
}

int thread_func2(void *data)
{
    char value = 'A';
    char val = 'H';

    while (val > 'A' && !kthread_should_stop()) {
        spin_lock(&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(&sp);
        msleep(3000);
    }
    return 0;
}
  • In function thread_func1,

    • kfifo elements are removed from the kfifo with the help of spinlock.

    • kfifo_is_empty is used to check if the kfifo is empty or not.

    • kfifo_out is 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_full is used to check if the kfifo is full or not.

    • kfifo_in is 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(&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(&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(&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(&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
[100471.716573] Driver Loaded...
[100471.716602] Kthread1 Created Successfully....
[100471.716617] Kthread2 Created Successfully...
[100471.716619] Thread 2 : Enqueue : A
[100474.878719] Thread 2 : Enqueue : B
[100474.878746] Thread 1 : Dequeue : A
[100477.950851] Thread 2 : Enqueue : C
[100477.950864] Thread 1 : Dequeue : B
[100481.022940] Thread 2 : Enqueue : D
[100481.026932] Thread 1 : Dequeue : C
[100484.095038] Thread 2 : Enqueue : E
[100484.095043] Thread 1 : Dequeue : D
[100487.167100] Thread 2 : Enqueue : F
[100487.167106] Thread 1 : Dequeue : E
[100490.239225] Thread 2 : Enqueue : G
[100490.239230] Thread 1 : Dequeue : F
  • 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
[100471.716573] Driver Loaded...
[100471.716602] Kthread1 Created Successfully....
[100471.716617] Kthread2 Created Successfully...
[100471.716619] Thread 2 : Enqueue : A
[100474.878719] Thread 2 : Enqueue : B
[100474.878746] Thread 1 : Dequeue : A
[100477.950851] Thread 2 : Enqueue : C
[100477.950864] Thread 1 : Dequeue : B
[100481.022940] Thread 2 : Enqueue : D
[100481.026932] Thread 1 : Dequeue : C
[100484.095038] Thread 2 : Enqueue : E
[100484.095043] Thread 1 : Dequeue : D
[100487.167100] Thread 2 : Enqueue : F
[100487.167106] Thread 1 : Dequeue : E
[100490.239225] Thread 2 : Enqueue : G
[100490.239230] Thread 1 : Dequeue : F
[100493.247365] 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:     27EADBF8D0C8EB7775C1559
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

Acquires given lock

spin_unlock

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