Module with Two Kthread - mutex

  • 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>
#include <linux/mutex.h>
  • Initialize all the required thread and spinlock variables.

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

DEFINE_MUTEX(mutex);

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

LIST_HEAD(my_list);
  • DEFINE_MUTEX macro is used to declare a mutex.

  • LIST_HEAD is used to initialize a list head structure.

  • 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 - SLL - mutex");
  • 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)
{
    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,

    • node1, node2 is initialized, allocated memory dynamically and added to the list.

    • thread1, thread2 is created and the corresponding thread APIs is passed as an argument.

  • 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)
{
    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,

    • threads which are created in init function are being terminated.

    • each entry in the list are being freed.

  • 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()) {
            mutex_lock(&mutex);
            list_for_each_entry(new_node, &my_list, list) {
                    new_node->data++;
                    pr_info("Executing thread function 1 : %d\n", new_node->data);
                }
            mutex_unlock(&mutex);
            msleep(5000);
        }
    return 0;
}

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

    while (!kthread_should_stop()) {
            mutex_lock(&mutex);
            list_for_each_entry(new_node, &my_list, list) {
                    new_node->data++;
                    pr_info("Executing thread function 2 : %d\n", new_node->data);
                }
            mutex_unlock(&mutex);
            msleep(5000);
        }
    return 0;
}
  • In this thread fun1 and thread fun2,

    • A new node is being created.

    • whenever the mutex is locked the new node is being added to the list.

    • once the new node is added to the list the mutex is unlocked so that next set of operations can access the resources.

  • 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#include <linux/mutex.h>
 14
 15MODULE_LICENSE("GPL");
 16MODULE_AUTHOR("Linux_usr");
 17MODULE_DESCRIPTION("Two kthreads - SLL - mutex");
 18
 19static struct task_struct *thread1;
 20static struct task_struct *thread2;
 21
 22DEFINE_MUTEX(mutex);
 23
 24struct node {
 25	int data;
 26	struct list_head list;
 27};
 28
 29LIST_HEAD(my_list);
 30
 31static int thread_fun1(void *data)
 32{
 33	struct node *new_node;
 34
 35	while (!kthread_should_stop()) {
 36		mutex_lock(&mutex);
 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		mutex_unlock(&mutex);
 42		msleep(5000);
 43	}
 44	return 0;
 45}
 46
 47static int thread_fun2(void *data)
 48{
 49	struct node *new_node;
 50
 51	while (!kthread_should_stop()) {
 52		mutex_lock(&mutex);
 53		list_for_each_entry(new_node, &my_list, list) {
 54			new_node->data++;
 55			pr_info("Executing thread function 2 : %d\n", new_node->data);
 56		}
 57		mutex_unlock(&mutex);
 58		msleep(5000);
 59	}
 60	return 0;
 61}
 62
 63static int __init thread_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
 94static void __exit thread_exit(void)
 95{
 96	struct node *curr_node, *next_node;
 97
 98	kthread_stop(thread1);
 99	kthread_stop(thread2);
100	list_for_each_entry_safe(curr_node, next_node, &my_list, list) {
101		list_del(&curr_node->list);
102		kfree(curr_node);
103	}
104	pr_info("Driver Unloaded...\n");
105}
106
107module_init(thread_init);
108module_exit(thread_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
[30043.211194] Driver Loaded...
[30043.211240] Executing thread function 1 : 2
[30043.211241] Executing thread function 1 : 3
[30043.211258] Executing thread function 2 : 3
[30043.211259] Executing thread function 2 : 4
[30048.366455] Executing thread function 1 : 4
[30048.366458] Executing thread function 1 : 5
[30048.366462] Executing thread function 2 : 5
[30048.366464] Executing thread function 2 : 6
  • 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
[30043.211194] Driver Loaded...
[30043.211240] Executing thread function 1 : 2
[30043.211241] Executing thread function 1 : 3
[30043.211258] Executing thread function 2 : 3
[30043.211259] Executing thread function 2 : 4
[30048.366455] Executing thread function 1 : 4
[30048.366458] Executing thread function 1 : 5
[30048.366462] Executing thread function 2 : 5
[30048.366464] Executing thread function 2 : 6
[30053.390584] Driver Unloaded...
  • We can also get the information about the module using modinfo command.

$ modinfo threads.ko
filename:       threads.ko
description:    Two kthreads - SLL - mutex
author:         Linux_usr
license:        GPL
srcversion:     CCE53406D9BB7F8DAD8150C
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_MUTEX

To declare a mutex variable

LIST_HEAD

A list is headed by a structure defined by the LIST_HEAD macro

mutex_lock

Acquires given lock

list_for_each_entry

iterate over list of given type

mutex_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