Module with Two Kthread - spinlock_bh

  • 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/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/slab.h>
  • Initialize all the required thread and spinlock variables.

static struct task_struct *thread1;
static struct task_struct *thread2;

DEFINE_SPINLOCK(sp);

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

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

  • DEFINE_SPINLOCK is used to declare a spinlock variable.

  • 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 kthread_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);

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


        thread1 = kthread_run(thread_fun1, NULL, "thread1");
        if (IS_ERR(thread1)) {
                pr_alert("Failed to create thraed1");
                return PTR_ERR(thread1);
        }


        thread2 = kthread_run(thread_fun2, NULL, "thread2");
        if (IS_ERR(thread1)) {
                pr_alert("Failed to create thraed2");
                return PTR_ERR(thread2);
        }

        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.

    • two nodes node1 and node2 is created.

    • INIT_LIST_HEAD is used to initialize a list head for which memory is dynamically allocated.

    • list_add_tail is used to add a new entry before a specified head.

  • 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 kthread_exit(void)
{
    struct node *curr_node, *next_node;

    kthread_stop(thread1);
    kthread_stop(thread2);
    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");
}
  • In this exit function,

    • list_for_each_entry_safe is used to iterate over the list.

    • list_del is used to delete the current node in the list.

  • thread function will execute a print statement until the thread is stopped and until that the thread will execute.

static int thread_fun1(void *data)
{
    struct node *new_node;

    while (!kthread_should_stop()) {
            spin_lock_bh(&sp);
            list_for_each_entry(new_node, &my_list, list) {
                    new_node->data++;
                    pr_info("Executing thread function 1 : %d\n", new_node->data);
            }
            spin_unlock_bh(&sp);
            msleep(5000);
        }
    return 0;
}

static int thread_fun2(void *data)
{
    struct node *new_node;

    while (!kthread_should_stop()) {
            spin_lock_bh(&sp);
            list_for_each_entry(new_node, &my_list, list) {
                    new_node->data++;
                    pr_info("Executing thread function 2 : %d\n", new_node->data);
                }
            spin_unlock_bh(&sp);
            msleep(5000);
        }
    return 0;
}
  • 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/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("Two kthreads with Spinlock_irqsave");
 17
 18static struct task_struct *thread1;
 19static struct task_struct *thread2;
 20
 21DEFINE_SPINLOCK(sp);
 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	struct node *new_node;
 33
 34	while (!kthread_should_stop()) {
 35		spin_lock_bh(&sp);
 36		list_for_each_entry(new_node, &my_list, list) {
 37			new_node->data++;
 38			pr_info("Executing thread function 1 : %d\n", new_node->data);
 39		}
 40		spin_unlock_bh(&sp);
 41		msleep(5000);
 42	}
 43	return 0;
 44}
 45
 46static int thread_fun2(void *data)
 47{
 48	struct node *new_node;
 49
 50	while (!kthread_should_stop()) {
 51		spin_lock_bh(&sp);
 52		list_for_each_entry(new_node, &my_list, list) {
 53			new_node->data++;
 54			pr_info("Executing thread function 2 : %d\n", new_node->data);
 55		}
 56		spin_unlock_bh(&sp);
 57		msleep(5000);
 58	}
 59	return 0;
 60}
 61
 62
 63static int __init kthread_init(void)
 64{
 65	struct node *node1;
 66	struct node *node2;
 67
 68	pr_info("Driver Loaded...\n");
 69
 70
 71	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 72	node1->data = 1;
 73	INIT_LIST_HEAD(&node1->list);
 74	list_add_tail(&node1->list, &my_list);
 75
 76	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 77	node2->data = 2;
 78	INIT_LIST_HEAD(&node2->list);
 79	list_add_tail(&node2->list, &my_list);
 80
 81
 82	thread1 = kthread_run(thread_fun1, NULL, "thread1");
 83	if (IS_ERR(thread1)) {
 84		pr_alert("Failed to create thraed1");
 85		return PTR_ERR(thread1);
 86	}
 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("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 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
[31047.420026] Driver Loaded...
[31047.420106] Executing thread function 1 : 2
[31047.420109] Executing thread function 1 : 3
[31047.420141] Executing thread function 2 : 3
[31047.420142] Executing thread function 2 : 4
[31052.674684] Executing thread function 2 : 4
[31052.674687] Executing thread function 2 : 5
[31052.674692] Executing thread function 1 : 5
[31052.674694] Executing thread function 1 : 6
[31057.794771] Executing thread function 1 : 6
[31057.794774] Executing thread function 1 : 7
[31057.794778] Executing thread function 2 : 7
[31057.794781] Executing thread function 2 : 8
  • 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 in linux module.

sudo insmod ./threads.ko
  • Run dmesg to check the kernel message and verify if the module is unloaded or not.

$ dmesg
[31047.420026] Driver Loaded...
[31047.420106] Executing thread function 1 : 2
[31047.420109] Executing thread function 1 : 3
[31047.420141] Executing thread function 2 : 3
[31047.420142] Executing thread function 2 : 4
[31052.674684] Executing thread function 2 : 4
[31052.674687] Executing thread function 2 : 5
[31052.674692] Executing thread function 1 : 5
[31052.674694] Executing thread function 1 : 6
[31057.794771] Executing thread function 1 : 6
[31057.794774] Executing thread function 1 : 7
[31057.794778] Executing thread function 2 : 7
[31057.794781] Executing thread function 2 : 8
[31062.819070] Driver Unloaded...
  • We can also get the information about the module using modinfo command.

$ modinfo threads.ko
filename:       threads.ko
description:    Two kthreads with Spinlock_irqsave
author:         Linux_usr
license:        GPL
srcversion:     33A16D633CA13297F690405
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_bh

Acquires given lock

list_for_each_entry

iterate over list of given type

spin_unlock_bh

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