Module with Two Kthread - spinlock_irqsave

  • 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