Module with Two Kthread - spinlock

  • 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/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>
  • 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");
  • 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.

  • 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("Device 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(&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(&sp);
                msleep(5000);
        }
        return 0;
}

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

    while (!kthread_should_stop()) {
            spin_lock(&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(&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");
 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(&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(&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(&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(&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	node1 = kmalloc(sizeof(struct node), GFP_KERNEL);
 71	node1->data = 1;
 72	INIT_LIST_HEAD(&node1->list);
 73	list_add_tail(&node1->list, &my_list);
 74	
 75	node2 = kmalloc(sizeof(struct node), GFP_KERNEL);
 76	node2->data = 2;
 77	INIT_LIST_HEAD(&node2->list);
 78	list_add_tail(&node2->list, &my_list);
 79	
 80	thread1 = kthread_run(thread_fun1, NULL, "thread1");
 81	if (IS_ERR(thread1)) {
 82		pr_alert("Failed to create thraed1");
 83		return PTR_ERR(thread1);
 84	}
 85	
 86	thread2 = kthread_run(thread_fun2, NULL, "thread2");
 87	if (IS_ERR(thread1)) {
 88		pr_alert("Failed to create thraed2");
 89		return PTR_ERR(thread2);
 90	}
 91	return 0;
 92}
 93
 94
 95static void __exit kthread_exit(void)
 96{
 97	struct node *curr_node, *next_node;
 98
 99	kthread_stop(thread1);
100	kthread_stop(thread2);
101	list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
102		list_del(&curr_node->list);
103		kfree(curr_node);
104	}
105	pr_info("Device Driver Unloaded...\n");
106}
107
108module_init(kthread_init);
109module_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
[30470.237516] Driver Loaded...
[30470.237552] Executing thread function 1 : 2
[30470.237553] Executing thread function 1 : 3
[30470.237584] Executing thread function 2 : 3
[30470.237585] Executing thread function 2 : 4
[30475.383116] Executing thread function 2 : 4
[30475.383119] Executing thread function 2 : 5
[30475.383125] Executing thread function 1 : 5
[30475.383133] Executing thread function 1 : 6
[30480.503222] Executing thread function 2 : 6
[30480.503225] Executing thread function 2 : 7
[30480.503229] Executing thread function 1 : 7
[30480.503231] Executing thread function 1 : 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 from linux kernel.

$ sudo rmmod threads
  • Check the kernel messages to see if the module is unloaded or not.

$ dmesg
[30470.237516] Driver Loaded...
[30470.237552] Executing thread function 1 : 2
[30470.237553] Executing thread function 1 : 3
[30470.237584] Executing thread function 2 : 3
[30470.237585] Executing thread function 2 : 4
[30475.383116] Executing thread function 2 : 4
[30475.383119] Executing thread function 2 : 5
[30475.383125] Executing thread function 1 : 5
[30475.383133] Executing thread function 1 : 6
[30480.503222] Executing thread function 2 : 6
[30480.503225] Executing thread function 2 : 7
[30480.503229] Executing thread function 1 : 7
[30480.503231] Executing thread function 1 : 8
[30485.511443] Device 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
author:         Linux_usr
license:        GPL
srcversion:     DAE1A9A3D4570B634E91349
depends:
retpoline:      Y
name:           threads
vermagic:       6.2.0-32-generic SMP preempt 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

Acquires given lock

list_for_each_entry

iterate over list of given type

spin_unlock

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