Creating Waitqueue

#

Version

Ubuntu

Ubuntu 22.10

Kernel

6.7.9

  • In this program, you are going to learn

  • How to create waitqueue?

  • What are the different ways to create waitqueue?

  • How to wakeup the waiting threads once the task is completed?

#include <linux/kthread.h>

bool kthread_should_stop(void);
  • where

    • kthread_should_stop: this is used to determine whether the thread should return now. Whenever the kthread_stop() is called it will be woken and returns true.

    • return type: returns true when the thread is stopped, false when the thread is still in execution

  • Here is an example of how to use the API,

while (!kthread_should_stop())
{
    //add the instructions to be performed during thread execution.
}
  • Here is the function prototype of the API: kthread_run

#include <linux/kthread.h>

#define kthread_run(threadfn, data, namefmt, ...)
  • where

    • kthread_run : is used to create and wake a kthread

    • return type: struct task_struct* (i.e) address of the kthread

    • threadfn: function to executed by kthread

    • data: data pointer for threadfn which can be used to send any possible arguments required for threadfn.

    • namefmt: printf-style format for the thread name which will be displayed on ps output when the thread is in execution.

  • Here is an example of how to use the API,

kthread_run(mythread,NULL,"sample kthread");
#include <linux/kthread.h>

int kthread_stop(struct task_struct *k);
  • where

    • kthread_stop: stops a kthread.

    • return type: returns the return value of threadfn() which is passed as an argument during kthread creation.

    • k: kthread created by one of the API used to created kthread.

  • Here is the example of how to use the API,

kthread_stop(mythread);
# include <linux/wait.h>

#define DECLARE_WAIT_QUEUE_HEAD(name) \
        struct wait_queue_head name = __WAIT_QUEUE_HEAD_INITIALIZER(name)
  • where

    • DECLARE_WAIT_QUEUE_HEAD : To declare and initialize wait queue head.

    • name : name which is given to the wait queue head on declaration.

  • Here is the example of the API,

DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);
# include <linux/wait.h>

#define init_waitqueue_head(wq_head)                        \
    do {                                                    \
            static struct lock_class_key __key;                     \
                                                            \
            __init_waitqueue_head((wq_head), #wq_head, &__key); \
    } while (0)
  • where

    • init_waitqueue_head : initialize wait queue head.

    • wq_head : name of the waitqueue head which is initialized

  • Here is the example of how to use the API,

init_waitqueue_head(mywaitqueue);
# include <linux/wait.h>

# define DECLARE_WAIT_QUEUE_HEAD_ONSTACK(name) \
    struct wait_queue_head name = __WAIT_QUEUE_HEAD_INIT_ONSTACK(name)
  • where

  • Here is the example of how to use the API

DECLARE_WAIT_QUEUE_HEAD_ONSTACK(mywaitqueue);
#include <linux/wait.h>

#define wait_event_interruptible(wq_head, condition)            \
({                                                              \
    int __ret = 0;                                              \
    might_sleep();                                              \
    if (!(condition))                                           \
        __ret = __wait_event_interruptible(wq_head, condition); \
    __ret;                                                      \
})
  • where

    • wait_event_interruptible : The process is put to sleep (TASK_INTERRUPTIBLE) until the condition evaluates to true or a signal is received. The condition is checked each time the waitqueue wq_head is woken up.

    • wq_head : the waitqueue to wait on

    • condition : a C expression for the event to wait for

    • return type: The function will return -ERESTARTSYS if it was interrupted by a signal and 0 if condition evaluated to true.

  • Here is an example of how to use the API,

wait_event_interruptible(mywaitqueue,flag != 0);
#include <linux/wait.h>

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
  • where

    • wake_up_interruptible : wakes up the waitqueue head which is in waiting state.

    • x : waitqueue which is in waiting state.

  • Here is the example of how to use the API

wake_up_interruptible(mywaitqueue);
  • Here is the prototype of module paramter APIs

#include <linux/module.h>

#define MODULE_LICENSE(_license) MODULE_FILE MODULE_INFO(license, _license)
#define MODULE_AUTHOR(_author) MODULE_INFO(author, _author)
#define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description)
  • where

    • MODULE_LICENSE: tells the kernel what license is used by our module.

    • MODULE_AUTHOR: denotes the author of this kernel module.

    • MODULE_DESCRIPTION: gives a basic idea about what the kernel module does.

    • These information can be found when modinfo command is used which lists out all these above mentioned information.

  • Here is the example of how to use the Module parameter APIs,

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Usr");
MODULE_DESCRIPTION("Sample kernel module");
  • Here is the prototype of the API: IS_ERR

#include <linux/err.h>

static inline bool __must_check IS_ERR(__force const void *ptr);
  • where

    • IS_ERR: Detects an error pointer

    • return type: return true if it’s an error pointer else return false.

    • ptr: pointer which needs to detected.

  • Here is an example of how to use the API,

if(IS_ERR(sample_ptr)) {
    //instructions to be executed when error ptr is detected
} else {
    //instructions to be executed when error ptr is not detected
}
  • Here is the prototype of the API: pr_info

#include <linux/printk.h>

#define pr_info(fmt, ...) \
    printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
  • where

    • pr_info: Prints an info-level messages

    • fmt: format string

  • Here is an example of how to use the API,

pr_info("//sample print statement");
#include <linux/sched.h>

extern int wake_up_process(struct task_struct *tsk);
  • where

    • wake_up_process: wake up a specific process

    • return type: returns 1 if the process is woken up, 0 if the process is in running state.

    • tsk: process to be woken up.

  • Here is the example of how to use the API,

wake_up_process(mythread);
  • Here is the example of the API: msleep

#include <linux/delay.h>

void msleep(unsigned int msecs);
  • where

    • msleep: will put it in sleep for a certain amount of msecs time.

    • msecs: time in milliseconds to sleep for

  • Here is the example of how to use the API,

msleep(1000);
  • Here is the prototype of the Driver entry point API’s

#include <linux/module.h>

#define module_init(x)      __initcall(x);
#define module_exit(x)      __exitcall(x);
  • where

    • module_init: driver initialization entry point which will be called at module insertion time.

    • module_exit: driver exit entry point which will be called during the removal of module.

    • x:
      • function to be run at module insertion for module_init function.

      • function to be run when driver is removed for module_exit function.

  • Here is an example of how to use the driver entry point API’s

module_init(myinitmodule);
module_exit(myexitmodule);
  • In this example let’s see how to create waitqueue and use with kthread.

  • Include the follow header files(.h) to refer the API being used for the execution.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/delay.h>
  • Add the following module macros to display information about the license, author and description about the module.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Usr");
MODULE_DESCRIPTION("Example of DECLARE_WAIT_QUEUE_HEAD");
  • Declare the thread and waitqueue variables which we are going to create and use in this example

static struct task_struct *wait_thread;
static struct task_struct *wake_up_thread;
static DECLARE_WAIT_QUEUE_HEAD(wqueue);
int wait_flag = 0;
  • Add the module init function which will be executed once we load the kernel module using insmod command.

static int __init declare_wait_queue_head_init(void)
{
    pr_info("Inside thread init function\n");
    thread_start();
    return 0;
}
  • Add the thread start function which is called from module init function, creates the thread and starts it’s execution.

void thread_start(void)
{
    wait_thread = kthread_run(wait_thread_fn,NULL,"DECLARE_WAIT_QUEUE_HEAD wait_thread example");
    if (IS_ERR(wait_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"DECLARE_WAIT_QUEUE_HEAD wake_up_thread example");
    if(IS_ERR(wake_up_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
}
  • Add the module exit function which will be executed once we unload the kernel module using rmmod command.

static void __exit declare_wait_queue_head_exit(void)
{
    pr_info("Inside kthread exit function\n");
    thread_stop();
}
  • Add the thread stop function which is called from module exit function, destroys the thread created and stops it’s execution.

void thread_stop(void)
{
    kthread_stop(wake_up_thread);
    kthread_stop(wait_thread);
    pr_info("destroyed threads\n");
}
  • Add the thread function API which will be called as soon as the kthread is created and is in running state.

int wait_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("waiting for event \n");
        wait_event_interruptible(wqueue,wait_flag != 0);
        pr_info("thread 1 execution\n");
        msleep(1000);
    }
    return 0;
}

int wake_up_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("thread 2 execution\n");
        wait_flag = 1;
        msleep(1000);
    }
    wake_up_interruptible(&wqueue);
    return 0;
}
  • Add the driver entry points which will be executed once the module is inserted or removed from the kernel.

module_init(declare_wait_queue_head_init);
module_exit(declare_wait_queue_head_exit);
 1#include <linux/init.h>
 2#include <linux/module.h>
 3#include <linux/kernel.h>
 4#include <linux/kthread.h>
 5#include <linux/wait.h>
 6#include <linux/delay.h>
 7
 8MODULE_LICENSE("GPL");
 9MODULE_AUTHOR("linux usr");
10MODULE_DESCRIPTION("Example of DECLARE_WAIT_QUEUE_HEAD");
11
12static struct task_struct *wait_thread;
13static struct task_struct *wake_up_thread;
14static DECLARE_WAIT_QUEUE_HEAD(wqueue);
15int wait_flag = 0;
16
17/* wait_thread_fn - executes when wait_thread is created,
18 * waits for the event to be completed by wake_up_thread,
19 * prints a message and sleeps for 1000ms,
20 * stops when kthread_stop is called */
21
22int wait_thread_fn(void *data)
23{
24	while(!kthread_should_stop()) {
25		pr_info("waiting for event \n");
26		wait_event_interruptible(wqueue,wait_flag != 0);
27		pr_info("thread 1 execution\n");
28		msleep(1000);
29	}
30	return 0;
31}
32
33/* wake_up_thread_fn - executes when wake_up_thread is created,
34 * prints a message and sleeps for 1000ms,
35 * wakes up wait_thread to check if the event is completed,
36 * stops when kthread_stop is called */
37
38int wake_up_thread_fn(void *data)
39{
40	while(!kthread_should_stop()) {
41		pr_info("thread 2 execution\n");
42		wait_flag = 1;
43		msleep(1000);
44	}
45	wake_up_interruptible(&wqueue);
46	return 0;
47}
48
49/* thread_start - creates kthread and starts execution */
50
51void thread_start(void)
52{
53    wait_thread = kthread_run(wait_thread_fn,NULL,"DECLARE_WAIT_QUEUE_HEAD wait_thread example");
54    if (IS_ERR(wait_thread))
55        pr_info("error creating thread\n");
56    else
57        pr_info("kthread created successfully\n");
58    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"DECLARE_WAIT_QUEUE_HEAD wake_up_thread example");
59    if(IS_ERR(wake_up_thread))
60        pr_info("error creating thread\n");
61    else
62        pr_info("kthread created successfully\n");
63}
64
65/* declare_wait_queue_head_init - calls thread_start,
66 * executes when the module is loaded */
67
68static int __init declare_wait_queue_head_init(void)
69{
70	pr_info("inside kthread init function\n");
71	thread_start();
72    return 0;
73}
74
75/* thread_stop - destroys thread and stops execution */
76
77void thread_stop(void)
78{
79    kthread_stop(wake_up_thread);
80    kthread_stop(wait_thread);
81    pr_info("destroyed threads\n");
82}
83
84/* declare_wait_queue_head - calls thread_stop,
85 * stops when module is unloaded */
86
87static void __exit declare_wait_queue_head_exit(void)
88{
89    pr_info("inside kthread exit function\n");
90    thread_stop();
91}
92
93module_init(declare_wait_queue_head_init);
94module_exit(declare_wait_queue_head_exit);
1obj-m += kthread.o
2
3all:
4	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
5
6clean:
7	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to compile the kernel source and generate the .ko image.

make -C /lib/modules/6.7.9/build M=$HOME/kthread_examples/
make[1]: Entering directory '/usr/src/linux-headers-6.7.9'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
You are using:           gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
CC [M]  $HOME/kthread_examples/kthread.o
MODPOST $HOME/kthread_examples/Module.symvers
CC [M]  $HOME/kthread_examples/kthread.mod.o
LD [M]  $HOME/kthread_examples/kthread.ko
BTF [M] $HOME/kthread_examples/kthread.ko
Skipping BTF generation for $HOME/kthread_examples/kthread.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.7.9'
  • Check if the .ko is generated or not using ls command.

test@test-V520-15IKL:~$ ls -l
total 360
-rw-rw-r-- 1 test test    713 Mar 18 16:06 kthread.c
-rw-rw-r-- 1 test test 169784 Mar 18 16:08 kthread.ko
-rw-rw-r-- 1 test test     58 Mar 18 16:08 kthread.mod
-rw-rw-r-- 1 test test   1047 Mar 18 16:08 kthread.mod.c
-rw-rw-r-- 1 test test  96512 Mar 18 16:08 kthread.mod.o
-rw-rw-r-- 1 test test  74696 Mar 18 16:08 kthread.o
-rw-rw-r-- 1 test test    161 Mar 18 16:00 Makefile
-rw-rw-r-- 1 test test     58 Mar 18 16:08 modules.order
-rw-rw-r-- 1 test test      0 Mar 18 16:08 Module.symvers
  • Run modinfo command to get the information about the kernel module.

test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko
filename:       $HOME/kthread_examples/kthread.ko
description:    Example of DECLARE_WAIT_QUEUE_HEAD
author:         Linux Usr
license:        GPL
srcversion:     8D2147F67AB01CF0E482DAC
depends:
retpoline:      Y
name:           kthread
vermagic:       6.7.9 SMP preempt mod_unload modversions
  • insert the module using insmod command.

test@test-V520-15IKL:~$ sudo insmod ./kthread.ko
  • check if the module is loaded or not using lsmod command.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
kthread                16384  0
  • check if the thread is created or not using ps command.

test@test-V520-15IKL:~$ ps -N | grep DECLARE_WAIT_QUEUE_HEAD
18619 ?        00:00:00 DECLARE_WAIT_QUEUE_HEAD wait_thread example
18620 ?        00:00:00 DECLARE_WAIT_QUEUE_HEAD wake_up_thread example
  • check for the kernel messages from init function and thread function once the module is loaded and thread is created.

test@test-V520-15IKL:~$ sudo dmesg
[ 8790.177139] inside kthread init function
[ 8790.177162] kthread created successfully
[ 8790.177164] waiting for event
[ 8790.177177] kthread created successfully
[ 8790.177179] thread 2 execution
[ 8791.185701] thread 2 execution
[ 8792.209741] thread 2 execution
  • remove the module from kernel using rmmod command.

test@test-V520-15IKL:~$ sudo rmmod kthread
  • check if the module is still loaded after removing the kernel module using lsmod if it is not displayed in lsmod output it is verified that the module is removed successfully.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
test@test-V520-15IKL:~$
  • check if the thread is destroyed using ps command if it is not displayed in ps output we can confirm that the thread is destroyed successfully.

test@test-V520-15IKL:~$ ps -N | grep DECLARE_WAIT_QUEUE_HEAD
test@test-V520-15IKL:~$
  • Check for kernel messages from exit function using dmesg command.

test@test-V520-15IKL:~$ sudo dmesg
[ 8803.961494] inside kthread exit function
[ 8804.496609] thread 1 execution
[ 8805.520555] destroyed threads
  • In this example let’s see how to create waitqueue head with init_waitqueue_head and use kthread with it.

  • Include the follow header files(.h) to refer the API being used for the execution.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/delay.h>
  • Add the following module macros to display information about the license, author and description about the module.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Usr");
MODULE_DESCRIPTION("Example of init_waitqueue_head");
  • Declare the thread variables which we are going to create and use in this example

static struct task_struct *wait_thread;
static struct task_struct *wake_up_thread;
wait_queue_head_t wqueue;
int wait_flag = 0;
  • Add the module init function which will be executed once we load the kernel module using insmod command.

static int __init init_waitqueue_head_init(void)
{
    pr_info("Inside kthread init function\n");
    thread_start();
    return 0;
}
  • Add the thread start function called from the module init function which is used to create the thread and execute it.

void thread_start(void)
{
    init_waitqueue_head(&wqueue);
    pr_info("initialized waitqueue head\n");
    wait_thread = kthread_run(wait_thread_fn,NULL,"init_waitqueue_head wait_thread example");
    if (IS_ERR(wait_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"init_waitqueue_head wake_up_thread example");
    if(IS_ERR(wake_up_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
}
  • Add the module exit function which will be executed once we unload the kernel module using rmmod command.

static void __exit init_waitqueue_head_exit(void)
{
    pr_info("Inside kthread exit function\n");
    thread_stop();
}
  • Add the thread stop function called from the module exit function which is used to destroy the thread and stop its execution.

void thread_stop(void)
{
    kthread_stop(wake_up_thread);
    kthread_stop(wait_thread);
    pr_info("destroyed threads\n");
}
  • Add the thread function API which will be called as soon as the kthread is created and is in running state.

int wait_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("waiting for event \n");
        wait_event_interruptible(wqueue,wait_flag != 0);
        pr_info("thread 1 execution\n");
        msleep(1000);
    }
    return 0;
}

int wake_up_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("thread 2 execution\n");
        wait_flag = 1;
        msleep(1000);
    }
    wake_up_interruptible(&wqueue);
    return 0;
}
  • Add the driver entry points which will be executed once the module is inserted or removed from the kernel.

module_init(init_waitqueue_head_init);
module_exit(init_waitqueue_head_exit);
 1#include <linux/init.h>
 2#include <linux/module.h>
 3#include <linux/kernel.h>
 4#include <linux/kthread.h>
 5#include <linux/wait.h>
 6#include <linux/delay.h>
 7
 8MODULE_LICENSE("GPL");
 9MODULE_AUTHOR("linux usr");
10MODULE_DESCRIPTION("Example of init_waitqueue_head");
11
12static struct task_struct *wait_thread;
13static struct task_struct *wake_up_thread;
14wait_queue_head_t wqueue;
15int wait_flag = 0;
16
17/* wait_thread_fn - executes when wait_thread is created,
18 * waits for an event from wake_up_thread,
19 * prints a message and sleeps for 1000ms,
20 * stops when kthread_stop is called */
21
22int wait_thread_fn(void *data)
23{
24	while(!kthread_should_stop()) {
25		pr_info("waiting for event \n");
26		wait_event_interruptible(wqueue,wait_flag != 0);
27		pr_info("thread 1 execution\n");
28		msleep(1000);
29	}	
30	return 0;
31}
32
33/* wake_up_thread_fn - executes when wake_up_thread is created,
34 * prints a message and sleeps for 1000ms,
35 * sends wake up signal to wait_thread,
36 * stops when kthread_stop is called */
37
38int wake_up_thread_fn(void *data)
39{
40	while(!kthread_should_stop()) {
41		pr_info("thread 2 execution\n");
42		wait_flag = 1;
43		msleep(1000);
44	}
45	wake_up_interruptible(&wqueue);
46	return 0;
47}
48
49/* thread_start - creates thread and starts execution */
50
51void thread_start(void)
52{
53    init_waitqueue_head(&wqueue);
54    pr_info("initialized waitqueue head\n");
55    wait_thread = kthread_run(wait_thread_fn,NULL,"init_waitqueue_head wait_thread example");
56    if (IS_ERR(wait_thread))
57        pr_info("error creating thread\n");
58    else
59        pr_info("kthread created successfully\n");
60    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"init_waitqueue_head wake_up_thread example");
61    if(IS_ERR(wake_up_thread))
62        pr_info("error creating thread\n");
63    else
64        pr_info("kthread created successfully\n");
65}
66
67/* init_waitqueue_head_init - calls thread_start,
68 * executes when module is loaded */
69
70static int __init init_waitqueue_head_init(void)
71{
72	pr_info("inside kthread init function\n");
73	thread_start();
74    return 0;
75}
76
77/* thread_stop - destroys thread and stops execution */
78
79void thread_stop(void)
80{
81    kthread_stop(wake_up_thread);
82    kthread_stop(wait_thread);
83    pr_info("destroyed threads\n");
84}
85
86/* init_waitqueue_head_exit - calls thread_stop,
87 * executes when module is unloaded */
88
89static void __exit init_waitqueue_head_exit(void)
90{
91    pr_info("inside kthread exit function\n");
92    thread_stop();
93}
94
95module_init(init_waitqueue_head_init);
96module_exit(init_waitqueue_head_exit);
1obj-m += kthread.o
2
3all:
4	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
5
6clean:
7	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to compile the kernel source and generate the .ko image.

make -C /lib/modules/6.7.9/build M=$HOME/kthread_examples/
make[1]: Entering directory '/usr/src/linux-headers-6.7.9'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
You are using:           gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
CC [M]  $HOME/kthread_examples/kthread.o
MODPOST $HOME/kthread_examples/Module.symvers
CC [M]  $HOME/kthread_examples/kthread.mod.o
LD [M]  $HOME/kthread_examples/kthread.ko
BTF [M] $HOME/kthread_examples/kthread.ko
Skipping BTF generation for $HOME/kthread_examples/kthread.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.7.9'
  • Check if the .ko is generated or not using ls command.

test@test-V520-15IKL:~$ ls -l
total 360
-rw-rw-r-- 1 test test    713 Mar 18 16:06 kthread.c
-rw-rw-r-- 1 test test 169784 Mar 18 16:08 kthread.ko
-rw-rw-r-- 1 test test     58 Mar 18 16:08 kthread.mod
-rw-rw-r-- 1 test test   1047 Mar 18 16:08 kthread.mod.c
-rw-rw-r-- 1 test test  96512 Mar 18 16:08 kthread.mod.o
-rw-rw-r-- 1 test test  74696 Mar 18 16:08 kthread.o
-rw-rw-r-- 1 test test    161 Mar 18 16:00 Makefile
-rw-rw-r-- 1 test test     58 Mar 18 16:08 modules.order
-rw-rw-r-- 1 test test      0 Mar 18 16:08 Module.symvers
  • Run modinfo command to get the information about the kernel module.

test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko
filename:       $HOME/kthread_examples/kthread.ko
description:    Example of init_waitqueue_head
author:         Linux Usr
license:        GPL
srcversion:     8D2147F67AB01CF0E482DAC
depends:
retpoline:      Y
name:           kthread
vermagic:       6.7.9 SMP preempt mod_unload modversions
  • insert the module using insmod command.

test@test-V520-15IKL:~$ sudo insmod ./kthread.ko
  • check if the module is loaded or not using lsmod command.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
kthread                16384  0
  • check if the thread is created or not using ps command.

test@test-V520-15IKL:~$ ps -N | grep init_waitqueue_head
20385 ?        00:00:00 init_waitqueue_head wait_thread example
20386 ?        00:00:00 init_waitqueue_head wake_up_thread example
  • check for the kernel messages from init function and thread function once the module is loaded and thread is created.

test@test-V520-15IKL:~$ sudo dmesg
[10705.908722] inside kthread init function
[10705.908726] initialized waitqueue head
[10705.908817] kthread created successfully
[10705.908819] waiting for event
[10705.908884] kthread created successfully
[10705.908885] thread 2 execution
[10706.936411] thread 2 execution
  • remove the module from kernel using rmmod command.

test@test-V520-15IKL:~$ sudo rmmod kthread
  • check if the module is still loaded after removing the kernel module using lsmod if it is not displayed in lsmod output it is verified that the module is removed successfully.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
test@test-V520-15IKL:~$
  • check if the thread is destroyed using ps command if it is not displayed in ps output we can confirm that the thread is destroyed successfully.

test@test-V520-15IKL:~$ ps -N | grep init_waitqueue_head
test@test-V520-15IKL:~$
  • Check for kernel messages from exit function using dmesg command.

test@test-V520-15IKL:~$ sudo dmesg
[10715.213892] inside kthread exit function
[10716.154353] thread 1 execution
[10717.178534] destroyed threads
  • In this example let’s see how to create waitqueue using DECLARE_WAIT_QUEUE_HEAD_ONSTACK and use kthread with it.

  • Include the follow header files(.h) to refer the API being used for the execution.

#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kthread.h>
#include <linux/wait.h>
#include <linux/delay.h>
  • Add the following module macros to display information about the license, author and description about the module.

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Usr");
MODULE_DESCRIPTION("example of declare_wait_queue_head_onstack");
  • Declare the thread and completion variables which we are going to create and use in this example

static struct task_struct *wait_thread;
static struct task_struct *wake_up_thread;
static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue);
int wait_flag = 0;
  • Add the module init function which will be executed once we load the kernel module using insmod command.

static int __init declare_wait_queue_head_onstack_init(void)
{
    pr_info("Inside kthread init function\n");
    thread_start();
    return 0;
}
  • Add the thread start function called from module init function, which creates the thread, wakes it up and starts its execution.

void thread_start(void)
{
    wait_thread = kthread_run(wait_thread_fn,NULL,"declare_wait_queue_head_onstack wait_thread example");
    if (IS_ERR(wait_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"declare_wait_queue_head_onstack wake_up_thread example");
    if(IS_ERR(wake_up_thread))
        pr_info("error creating thread\n");
    else
        pr_info("kthread created successfully\n");
}
  • Add the module exit function which will be executed once we unload the kernel module using rmmod command.

static void __exit declare_wait_queue_head_onstack_exit(void)
{
    pr_info("Inside kthread exit function\n");
    thread_stop();
}
  • Add the thread stop function which is called from module exit function, which destroys the thread and stops the execution.

void thread_stop(void)
{
    kthread_stop(wake_up_thread);
    kthread_stop(wait_thread);
    pr_info("destroyed threads\n");
}
  • Add the thread function API which will be called as soon as the kthread is created and is in running state.

int wait_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("waiting for event \n");
        wait_event_interruptible(wqueue,wait_flag != 0);
        pr_info("thread 1 execution\n");
        msleep(1000);
    }
    return 0;
}

int wake_up_thread_fn(void *data)
{
    while(!kthread_should_stop()) {
        pr_info("thread 2 execution\n");
        wait_flag = 1;
        msleep(1000);
    }
    wake_up_interruptible(&wqueue);
    return 0;
}
  • Add the driver entry points which will be executed once the module is inserted or removed from the kernel.

module_init(declare_wait_queue_head_onstack_init);
module_exit(declare_wait_queue_head_onstack_exit);
 1#include <linux/init.h>
 2#include <linux/module.h>
 3#include <linux/kernel.h>
 4#include <linux/kthread.h>
 5#include <linux/wait.h>
 6#include <linux/delay.h>
 7
 8MODULE_LICENSE("GPL");
 9MODULE_AUTHOR("linux usr");
10MODULE_DESCRIPTION("Example of declare_wait_queue_head_onstack");
11
12static struct task_struct *wait_thread;
13static struct task_struct *wake_up_thread;
14static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue);
15int wait_flag = 0;
16
17/* wait_thread_fn - executes when wait_thread is created,
18 * prints a message and sleeps for 1000ms,
19 * waits for the event completion from wake_up_thread,
20 * stops when kthread_stop is called */
21
22int wait_thread_fn(void *data)
23{
24	while(!kthread_should_stop()) {
25		pr_info("waiting for event \n");
26		wait_event_interruptible(wqueue,wait_flag != 0);
27		pr_info("thread 1 execution\n");
28		msleep(1000);
29	}
30	return 0;
31}
32
33/* wake_up_thread_fn - executes when wake_up_thread is created,
34 * prints a message and sleeps for 1000ms,
35 * sends wake_up signal to wait_thread,
36 * stops when kthread_stop is called */
37
38int wake_up_thread_fn(void *data)
39{
40	while(!kthread_should_stop()) {
41		pr_info("thread 2 execution\n");
42		wait_flag = 1;
43		msleep(1000);
44	}
45	wake_up_interruptible(&wqueue);
46	return 0;
47}
48
49/* thread_start - creates thread and starts execution */
50
51void thread_start(void)
52{
53    wait_thread = kthread_run(wait_thread_fn,NULL,"declare_wait_queue_head_onstack wait_thread example");
54    if (IS_ERR(wait_thread))
55        pr_info("error creating thread\n");
56    else
57        pr_info("kthread created successfully\n");
58    wake_up_thread = kthread_run(wake_up_thread_fn,NULL,"declare_wait_queue_head_onstack wake_up_thread example");
59    if(IS_ERR(wake_up_thread))
60        pr_info("error creating thread\n");
61    else
62        pr_info("kthread created successfully\n");
63}
64
65/* declare_wait_event_head_onstack_init - calls thread_start,
66 * executes when module is loaded */
67
68static int __init declare_wait_queue_head_onstack_init(void)
69{
70	pr_info("inside kthread init function\n");
71	thread_start();
72    return 0;
73}
74
75/* thread_stop - destroys thread and stops execution */
76
77void thread_stop(void)
78{
79    kthread_stop(wake_up_thread);
80    kthread_stop(wait_thread);
81    pr_info("destroyed threads\n");
82}
83
84/* declare_wait_queue_head_onstack_exit - calls thread_stop,
85 * executes when module is unloaded */
86
87static void __exit declare_wait_queue_head_onstack_exit(void)
88{
89    pr_info("inside kthread exit function\n");
90    thread_stop();
91}
92
93module_init(declare_wait_queue_head_onstack_init);
94module_exit(declare_wait_queue_head_onstack_exit);
1obj-m += kthread.o
2
3all:
4	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
5
6clean:
7	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to compile the kernel source and generate the .ko image.

make -C /lib/modules/6.7.9/build M=$HOME/kthread_examples/
make[1]: Entering directory '/usr/src/linux-headers-6.7.9'
warning: the compiler differs from the one used to build the kernel
The kernel was built by: x86_64-linux-gnu-gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
You are using:           gcc (Ubuntu 12.2.0-3ubuntu1) 12.2.0
CC [M]  $HOME/kthread_examples/kthread.o
MODPOST $HOME/kthread_examples/Module.symvers
CC [M]  $HOME/kthread_examples/kthread.mod.o
LD [M]  $HOME/kthread_examples/kthread.ko
BTF [M] $HOME/kthread_examples/kthread.ko
Skipping BTF generation for $HOME/kthread_examples/kthread.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.7.9'
  • Check if the .ko is generated or not using ls command.

test@test-V520-15IKL:~$ ls -l
total 360
-rw-rw-r-- 1 test test    713 Mar 18 16:06 kthread.c
-rw-rw-r-- 1 test test 169784 Mar 18 16:08 kthread.ko
-rw-rw-r-- 1 test test     58 Mar 18 16:08 kthread.mod
-rw-rw-r-- 1 test test   1047 Mar 18 16:08 kthread.mod.c
-rw-rw-r-- 1 test test  96512 Mar 18 16:08 kthread.mod.o
-rw-rw-r-- 1 test test  74696 Mar 18 16:08 kthread.o
-rw-rw-r-- 1 test test    161 Mar 18 16:00 Makefile
-rw-rw-r-- 1 test test     58 Mar 18 16:08 modules.order
-rw-rw-r-- 1 test test      0 Mar 18 16:08 Module.symvers
  • Run modinfo command to get the information about the kernel module.

test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko
filename:       $HOME/kthread_examples/kthread.ko
description:    Example of declare_wait_queue_head_onstack
author:         Linux Usr
license:        GPL
srcversion:     8D2147F67AB01CF0E482DAC
depends:
retpoline:      Y
name:           kthread
vermagic:       6.7.9 SMP preempt mod_unload modversions
  • insert the module using insmod command.

test@test-V520-15IKL:~$ sudo insmod ./kthread.ko
  • check if the module is loaded or not using lsmod command.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
kthread                16384  0
  • check if the thread is created or not using ps command.

test@test-V520-15IKL:~$ ps -N | grep declare_wait_queue_head_onstack
23624 ?        00:00:00 declare_wait_queue_head_onstack wait_thread example
23625 ?        00:00:00 declare_wait_queue_head_onstack wake_up_thread example
  • check for the kernel messages from init function and thread function once the module is loaded and thread is created.

test@test-V520-15IKL:~$ sudo dmesg
[14839.760745] inside kthread init function
[14839.760800] kthread created successfully
[14839.760804] waiting for event
[14839.760841] kthread created successfully
[14839.760845] thread 2 execution
[14840.769055] thread 2 execution
[14841.793082] thread 2 execution
  • remove the module from kernel using rmmod command.

test@test-V520-15IKL:~$ sudo rmmod kthread
  • check if the module is still loaded after removing the kernel module using lsmod if it is not displayed in lsmod output it is verified that the module is removed successfully.

test@test-V520-15IKL:~$ sudo lsmod | grep kthread
test@test-V520-15IKL:~$
  • check if the thread is destroyed using ps command if it is not displayed in ps output we can confirm that the thread is destroyed successfully.

test@test-V520-15IKL:~$ ps -N | grep declare_wait_queue_head_onstack
test@test-V520-15IKL:~$
  • Check for kernel messages from exit function using dmesg command.

test@test-V520-15IKL:~$ sudo dmesg
[14862.693612] inside kthread exit function
[14863.297411] thread 1 execution
[14864.321428] destroyed threads

kthread API

Learning

kthread_run

Create and wake a thread

kthread_should_stop

To determine when thread should exit

kthread_stop

Stop a thread created by kthread_create

Waitqueue API

Learning

DECLARE_WAIT_QUEUE_HEAD

Declare waitqueue head

init_waitqueue_head

Initialize waitqueue head

DECLARE_WAIT_QUEUEU_HEAD_ONSTACK

Declare waitqueue head on current stack

wait_event_interruptible

sleep until a condition gets true

wake_up_event_interruptible

wakes up the waiting queue

API

Learning

MODULE_LICENSE

Used to denote the license used in the kernel module

MODULE_AUTHOR

Used to mention the author of the kernel module

MODULE_DESCRIPTION

Used to describe what the module does

IS_ERR

Detects an error pointer

wake_up_process

wake up a specific process

msleep

will put in sleep for a certain amount of msecs time.

module_init

Driver initialization entry point

module_exit

Driver exit entry point

pr_info

Print an info-level message