Adding waitqueue entries to waitqueue head =========================================== .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow Topics in this section, * :ref:`Version Info ` * :ref:`Learnings in this section ` * :ref:`Explanation of kthread APIs ` * :ref:`kthread_should_stop ` * :ref:`kthread_run ` * :ref:`kthread_stop ` * :ref:`Explanation of Waitqueue APIs ` * :ref:`DECLARE_WAIT_QUEUE_HEAD ` * :ref:`init_waitqueue_entry ` * :ref:`init_waitqueue_func_entry ` * :ref:`add_wait_queue ` * :ref:`add_wait_queue_exclusive ` * :ref:`add_wait_queue_priority ` * :ref:`remove_wait_queue ` * :ref:`waitqueue_active ` * :ref:`wq_has_sleeper ` * :ref:`wake_up ` * :ref:`wait_event_interruptible ` * :ref:`wake_up_interruptible ` * :ref:`Explanation of miscellaneous APIs ` * :ref:`Module parameter APIs ` * :ref:`IS_ERR ` * :ref:`pr_info ` * :ref:`wake_up_process ` * :ref:`msleep ` * :ref:`Driver entry point APIs ` * :ref:`Example 1: Create waitqueue entry using init_waitqueue_entry ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load the module ` * :ref:`Example 2: Adding waitqueue entries using add_wait_queue ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Waitqueue entry function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load the module ` * :ref:`Example 3: Adding waitqueue entries using add_wait_queue_exclusive ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Waitqueue entry function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load the module ` * :ref:`Example 4: Adding waitqueue entries using add_wait_queue_priority ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Waitqueue entry function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load the module ` * :ref:`Example 5: Check for wait entries in wait queue using waitqueue_active ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Waitqueue entry function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load ` * :ref:`Example 6: Checking for any waiting waitqueue entries using wq_has_sleeper ` * :ref:`List of headers ` * :ref:`Module Macros ` * :ref:`Initialize thread variables ` * :ref:`Module init function ` * :ref:`Thread start function ` * :ref:`Module exit function ` * :ref:`Thread stop function ` * :ref:`Thread function API ` * :ref:`Waitqueue entry function API ` * :ref:`Driver entry points ` * :ref:`See the full program below ` * :ref:`Makefile ` * :ref:`Compile and Load the module ` * :ref:`Summary of Kthread APIs ` * :ref:`Summary of Waitqueue APIs ` * :ref:`Summary of Miscellaneous APIs ` .. _p2_addwq_0: .. tab-set:: .. tab-item:: Version Info =============================== ======================================= # Version =============================== ======================================= Ubuntu Ubuntu 22.10 Kernel 6.7.9 =============================== ======================================= .. _p2_addwq_1: .. tab-set:: .. tab-item:: Learnings in this section * In this program, you are going to learn .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to create waitqueue? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * What are the different ways to add waitqueue entries to waitqueue head? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to wakeup the waiting threads once the task is completed? .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use Kthread APIs ? * `kthread_should_stop `_ * `kthread_run `_ * `kthread_stop `_ .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use Waitqueue APIs ? * `DECLARE_WAIT_QUEUE_HEAD `_ * `init_waitqueue_entry `_ * `init_waitqueue_func_entry `_ * `waitqueue_active `_ * `wq_has_sleeper `_ * `add_wait_queue `_ * `add_wait_queue_exclusive `_ * `add_wait_queue_priority `_ * `remove_wait_queue `_ * `wake_up `_ * `wait_event_interruptible `_ * `wake_up_interruptible `_ .. panels:: :container: container pb-4 :column: col-lg-12 p-2 :card: shadow * How to use below APIs ? * `MODULE_LICENSE `_ * `MODULE_DESCRIPTION `_ * `MODULE_AUTHOR `_ * `IS_ERR `_ * `pr_info `_ * `wake_up_process `_ * `msleep `_ * `module_init `_ * `module_exit `_ .. _p2_addwq_2: .. tab-set:: .. tab-item:: Explanation of Kthread APIs .. _p2_addwq_5: .. tab-set:: .. tab-item:: kthread_should_stop * Here is the function prototype of the API: `kthread_should_stop `_ .. code-block:: c #include 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, .. code-block:: c while (!kthread_should_stop()) { //add the instructions to be performed during thread execution. } .. _p2_addwq_6: .. tab-set:: .. tab-item:: kthread_run * Here is the function prototype of the API: `kthread_run `_ .. code-block:: c #include #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, .. code-block:: c kthread_run(mythread,NULL,"sample kthread"); .. _p2_addwq_7: .. tab-set:: .. tab-item:: kthread_stop * Here is the function prototype of the API: `kthread_stop `_ .. code-block:: c #include 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, .. code-block:: c kthread_stop(mythread); .. _p2_addwq_82: .. tab-set:: .. tab-item:: Explanation of Waitqueue APIs .. _p2_addwq_83: .. tab-set:: .. tab-item:: DECLARE_WAIT_QUEUE_HEAD * Here is the prototype of the API: `DECLARE_WAIT_QUEUE_HEAD `_ .. code-block:: c # include #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, .. code-block:: c DECLARE_WAIT_QUEUE_HEAD(mywaitqueue); .. _p2_addwq_91: .. tab-set:: .. tab-item:: init_waitqueue_entry * Here is the prototype of the API: `init_waitqueue_entry `_ .. code-block:: c # include static inline void init_waitqueue_entry(struct wait_queue_entry *wq_entry, struct task_struct *p); * where * `init_waitqueue_entry `_ : initialize a waitqueue entry * wq_entry : waitqueue entry which is going to be initialized * p : thread to which waitqueue entry is added * Here is an example of how to use the API .. code-block:: c init_waitqueue_entry(mywaitqueue,current); .. _p2_addwq_92: .. tab-set:: .. tab-item:: init_waitqueue_func_entry * Here is the prototype of the API: `init_waitqueue_func_entry `_ .. code-block:: c # include static inline void init_waitqueue_func_entry(struct wait_queue_entry *wq_entry, wait_queue_func_t func); * where * `init_waitqueue_func_entry `_ : initialize a waitqueue entry and map user defined function to it. * wq_entry : waitqueue entry which needs to be initialized * func: user defined function which is mapped to waitqueue entry. .. _p2_addwq_93: .. tab-set:: .. tab-item:: add_wait_queue * Here is the prototype of the API: `add_wait_queue `_ .. code-block:: c # include void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry); * where * `add_wait_queue `_ : adding a waitqueue entry to waitqueue head. * wq_head: waitqueue head to which entry needs to be added * wq_entry: waitqueue entry to be added. * Here is an example of how to use the API, .. code-block:: c add_wait_queue(mywq_head,mywq_entry); .. _p2_addwq_94: .. tab-set:: .. tab-item:: add_wait_queue_exclusive * Here is the prototype of the API: `add_wait_queue_exclusive `_ .. code-block:: c # include void add_wait_queue_exclusive(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry); * where * `add_wait_queue_exclusive `_ : used to add wait entries to wait queue head exclusively * wq_head: waitqueue head to which entry needs to be added * wq_entry: entry to be added * Here is an example of how to use the API, .. code-block:: c add_wait_queue_exclusive(mywq_head,mywq_entry); .. _p2_addwq_95: .. tab-set:: .. tab-item:: add_wait_queue_priority * Here is the prototype of the API: `add_wait_queue_priority `_ .. code-block:: c # include void add_wait_queue_priority(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry); * where * `add_wait_queue_priority `_ : adding the wait entry to wait queue head in a priority manner. * wq_head: waitqueue head to which wait entry should be added. * wq_entry: waitqueue entry to be added. * Here is an example of the API, .. code-block:: c add_wait_queue_priority (mywq_head,mywq_entry); .. _p2_addwq_96: .. tab-set:: .. tab-item:: remove_wait_queue * Here is the prototype of the API: `remove_wait_queue `_ .. code-block:: c # include void remove_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry); * where * `remove_wait_queue `_ : remove a waitqueue entry from waitqueue head. * wq_head: waitqueue head from which waitqueue entry should be removed. * wq_entry: entry to be removed * Here is an example of the API, .. code-block:: c remove_wait_queue(mywq_head,mywq_entry); .. _p2_addwq_97: .. tab-set:: .. tab-item:: waitqueue_active * Here is the prototype of the API: `waitqueue_active `_ .. code-block:: c # include int waitqueue_active(struct wait_queue_head *wq_head); * where * `waitqueue_active `_ : locklessly test for waiters on the queue. * wq_head: the waitqueue to test for waiters * return type: returns true if the wait list is not empty .. _p2_addwq_98: .. tab-set:: .. tab-item:: wq_has_sleeper * Here is the prototype of the API: `wq_has_sleeper `_ .. code-block:: c # include bool wq_has_sleeper(struct wait_queue_head *wq_head); * where * `wq_has_sleeper `_ : check if there are any waiting processes * wq_head: wait queue head * return type: Returns true if wq_head has waiting processes .. _p2_addwq_99: .. tab-set:: .. tab-item:: wake_up * Here is the prototype of the API: `wake_up `_ .. code-block:: c # include #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) * where * `wake_up `_ : wake up the waiting waitqueues under wait_event * x: waitqueue which needs to be woken up. .. _p2_addwq_86: .. tab-set:: .. tab-item:: wait_event_interruptible * Here is the prototype of the API : `wait_event_interruptible `_ .. code-block:: c #include #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, .. code-block :: c wait_event_interruptible(mywaitqueue,flag != 0); .. _p2_addwq_87: .. tab-set:: .. tab-item:: wake_up_interruptible * Here is the prototype of the API: `wake_up_interruptible `_ .. code-block:: c #include #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 .. code-block:: c wake_up_interruptible(mywaitqueue); .. _p2_addwq_8: .. tab-set:: .. tab-item:: Explanation of miscellaneous APIs .. _p2_addwq_9: .. tab-set:: .. tab-item:: Module parameter APIs * Here is the prototype of module paramter APIs .. code-block:: c #include #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, .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("Sample kernel module"); .. _p2_addwq_10: .. tab-set:: .. tab-item:: IS_ERR * Here is the prototype of the API: `IS_ERR `_ .. code-block:: c #include 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, .. code-block:: c 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 } .. _p2_addwq_11: .. tab-set:: .. tab-item:: pr_info * Here is the prototype of the API: `pr_info `_ .. code-block:: c #include #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, .. code-block:: c pr_info("//sample print statement"); .. _p2_addwq_12: .. tab-set:: .. tab-item:: wake_up_process * Here is the example of the API: `wake_up_process `_ .. code-block:: c #include 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, .. code-block:: c wake_up_process(mythread); .. _p2_addwq_13: .. tab-set:: .. tab-item:: msleep * Here is the example of the API: `msleep `_ .. code-block:: c #include 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, .. code-block:: c msleep(1000); .. _p2_addwq_14: .. tab-set:: .. tab-item:: Driver entry point API's * Here is the prototype of the Driver entry point API's .. code-block:: c #include #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 .. code-block:: c module_init(myinitmodule); module_exit(myexitmodule); .. _p2_addwq_15: .. tab-set:: .. tab-item:: Example 1: Create waitqueue entry using init_waitqueue_entry * In this example let's see how to create waitqueue entry using init_waitqueue_entry and use with kthread. .. _p2_addwq_16: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_17: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("Example of init_waitqueue_entry"); .. _p2_addwq_18: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread and waitqueue variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD(wqueue); wait_queue_entry_t wait; int wait_flag = 0; .. _p2_addwq_19: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init init_waitqueue_entry_init(void) { pr_info("Inside thread init function\n"); thread_start(); return 0; } .. _p2_addwq_20: .. tab-set:: .. tab-item:: Thread start function * Add the thread start function which is called from module init function, creates the thread and starts it's execution. .. code-block:: c void thread_start(void) { wait_thread = kthread_run(wait_thread_fn,NULL,"init_waitqueue_entry 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_entry wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_21: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit init_waitqueue_entry_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_22: .. tab-set:: .. tab-item:: Thread stop function * Add the thread stop function which is called from module exit function, destroys the thread created and stops it's execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_23: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_entry(&wait,current); add_wait_queue(&wqueue,&wait); while(!kthread_should_stop()) { wait_event_interruptible(wqueue,wait_flag != 0); pr_info("thread 1 execution\n"); msleep(1000); } remove_wait_queue(&wqueue,&wait); if (waitqueue_active(&wqueue)) { pr_info("pending waitqueues\n"); } else { pr_info("no more pending waitqueues\n"); } 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; } .. _p2_addwq_24: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(init_waitqueue_entry_init); module_exit(init_waitqueue_entry_exit); .. _p2_addwq_25: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/init_waitqueue_entry/kthread.c :language: c :linenos: .. _p2_addwq_26: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/init_waitqueue_entry/Makefile :language: c :linenos: .. _p2_addwq_27: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of init_waitqueue_entry 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep init_waitqueue_entry 7249 ? 00:00:00 init_waitqueue_entry wait_thread example 7250 ? 00:00:00 init_waitqueue_entry wait_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 580.934801] inside kthread init function [ 580.934856] kthread created successfully [ 580.934871] kthread created successfully [ 580.934873] thread 2 execution [ 581.941483] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep init_waitqueue_entry test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 590.988437] inside kthread exit function [ 591.147138] thread 1 execution [ 592.183215] no more pending waitqueues [ 592.183249] destroyed threads .. _p2_addwq_28: .. tab-set:: .. tab-item:: Example 2: Adding waitqueue entries using add_wait_queue * In this example let's see how to add waitqueue entries using add_wait_queue. .. _p2_addwq_29: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_30: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("Example of add_wait_queue"); .. _p2_addwq_31: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD(wqueue); wait_queue_entry_t wait; int wait_flag = 0; .. _p2_addwq_32: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init add_wait_queue_init(void) { pr_info("Inside kthread init function\n"); thread_start(); return 0; } .. _p2_addwq_33: .. tab-set:: .. tab-item:: Thread start function * Add the thread start function called from the module init function which is used to create the thread and execute it. .. code-block:: c void thread_start(void) { pr_info("initialized waitqueue head\n"); wait_thread = kthread_run(wait_thread_fn,NULL,"add_wait_queue 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,"add_wait_queue wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_34: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit add_wait_queue_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_35: .. tab-set:: .. tab-item:: Thread stop function * Add the thread stop function called from the module exit function which is used to destroy the thread and stop its execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); msleep(2000); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_36: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_func_entry(&wait,waitqueue_fn); add_wait_queue(&wqueue,&wait); while(!kthread_should_stop()) { wait_event_interruptible(wqueue,wait_flag != 0); pr_info("thread 1 execution\n"); msleep(1000); } remove_wait_queue(&wqueue,&wait); return 0; } int wake_up_thread_fn(void *data) { while(!kthread_should_stop()) { pr_info("thread 2 execution\n"); msleep(1000); } wait_flag = 1; wake_up(&wqueue); return 0; } .. _p2_addwq_54: .. tab-set:: .. tab-item:: Waitqueue entry function API * Add the waitqueue entry function which is mapped to the workqueue entry created. .. code-block:: c static int waitqueue_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 0; while ( i < 3) { pr_info("waitqueue fn i = %d\n",i); i++; } return 0; } .. _p2_addwq_37: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(add_wait_queue_init); module_exit(add_wait_queue_exit); .. _p2_addwq_38: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/add_wait_queue/kthread.c :language: c :linenos: .. _p2_addwq_39: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/add_wait_queue/Makefile :language: c :linenos: .. _p2_addwq_40: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of add_wait_queue 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue 15147 ? 00:00:00 add_wait_queue wait_thread example 15148 ? 00:00:00 add_wait_queue wake_up_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 4430.577931] inside kthread init function [ 4430.578033] kthread created successfully [ 4430.578067] kthread created successfully [ 4430.578072] thread 2 execution [ 4431.594784] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 4441.938004] inside kthread exit function [ 4442.858930] waitqueue fn i = 0 [ 4442.858937] waitqueue fn i = 1 [ 4442.858938] waitqueue fn i = 2 [ 4442.859038] thread 1 execution [ 4443.882938] thread 1 execution [ 4444.891076] destroyed threads .. _p2_addwq_41: .. tab-set:: .. tab-item:: Example 3: Adding waitqueue entries using add_wait_queue_exclusive * In this example let's see how to add waitqueue entries using add_wait_queue_exclusive. .. _p2_addwq_42: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_43: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("example of add_wait_queue_exclusive"); .. _p2_addwq_44: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread and completion variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue); wait_queue_entry_t wait; int wait_flag = 0; .. _p2_addwq_45: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init add_wait_queue_exclusive_init(void) { pr_info("Inside kthread init function\n"); thread_start(); return 0; } .. _p2_addwq_46: .. tab-set:: .. tab-item:: thread start function * Add the thread start function called from module init function, which creates the thread, wakes it up and starts its execution. .. code-block:: void thread_start(void) { wait_thread = kthread_run(wait_thread_fn,NULL,"add_wait_queue_exclusive 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,"add_wait_queue_exclusive wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_47: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit add_wait_queue_exclusive_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_48: .. tab-set:: .. tab-item:: thread stop function * Add the thread stop function which is called from module exit function, which destroys the thread and stops the execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_49: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_func_entry(&wait,waitqueue_fn); add_wait_queue_exclusive(&wqueue,&wait); while(!kthread_should_stop()) { wait_event_interruptible(wqueue,wait_flag != 0); pr_info("thread 1 execution\n"); msleep(1000); } remove_wait_queue(&wqueue,&wait); 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(&wqueue); return 0; } .. _p2_addwq_55: .. tab-set:: .. tab-item:: Waitqueue entry function API * Add the waitqueue entry function which is mapped to the waitqueue entry created. .. code-block:: c static int waitqueue_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 0; while ( i < 3) { pr_info("waitqueue fn i = %d\n",i); i++; } return 0; } .. _p2_addwq_50: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(add_wait_queue_exclusive_init); module_exit(add_wait_queue_exclusive_exit); .. _p2_addwq_51: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/add_wait_queue_exclusive/kthread.c :language: c :linenos: .. _p2_addwq_52: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/add_wait_queue_exclusive/Makefile :language: c :linenos: .. _p2_addwq_53: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of add_wait_queue_exclusive 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue_exclusive 5995 ? 00:00:00 add_wait_queue_exclusive wait_thread example 5996 ? 00:00:00 add_wait_queue_exclusive wake_up_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 860.339559] inside kthread init function [ 860.339678] kthread created successfully [ 860.339713] kthread created successfully [ 860.339718] thread 2 execution [ 861.360841] thread 2 execution [ 862.384770] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue_exclusive test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 870.211391] inside kthread exit function [ 870.576490] waitqueue fn i = 0 [ 870.576496] waitqueue fn i = 1 [ 870.576498] waitqueue fn i = 2 [ 870.576568] thread 1 execution [ 871.600551] destroyed threads .. _p2_addwq_56: .. tab-set:: .. tab-item:: Example 4: Adding waitqueue entries using add_wait_queue_priority * In this example let's see how to add waitqueue entries using add_wait_queue_priority. .. _p2_addwq_57: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_58: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("example of add_wait_queue_priority"); .. _p2_addwq_59: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread and completion variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue); wait_queue_entry_t wait_1; wait_queue_entry_t wait_2; wait_queue_entry_t wait_priority; int wait_flag = 0; .. _p2_addwq_60: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init add_wait_queue_priority_init(void) { pr_info("Inside kthread init function\n"); thread_start(); return 0; } .. _p2_addwq_61: .. tab-set:: .. tab-item:: thread start function * Add the thread start function called from module init function, which creates the thread, wakes it up and starts its execution. .. code-block:: void thread_start(void) { wait_thread = kthread_run(wait_thread_fn,NULL,"add_wait_queue_priority 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,"add_wait_queue_priority wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_62: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit add_wait_queue_priority_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_63: .. tab-set:: .. tab-item:: thread stop function * Add the thread stop function which is called from module exit function, which destroys the thread and stops the execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_64: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_func_entry(&wait_1,waitqueue_fn); init_waitqueue_func_entry(&wait_priority,waitqueue_priority_fn); init_waitqueue_func_entry(&wait_2,waitqueue_fn); add_wait_queue(&wqueue,&wait_1); add_wait_queue_priority(&wqueue,&wait_priority); add_wait_queue(&wqueue,&wait_2); while(!kthread_should_stop()) { wait_event_interruptible(wqueue,wait_flag != 0); pr_info("thread 1 execution\n"); msleep(1000); } remove_wait_queue(&wqueue,&wait_1); remove_wait_queue(&wqueue,&wait_priority); remove_wait_queue(&wqueue,&wait_2); 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(&wqueue); return 0; } .. _p2_addwq_65: .. tab-set:: .. tab-item:: Waitqueue entry function API * Add the waitqueue entry function which is mapped to the waitqueue entry created. .. code-block:: c static int waitqueue_priority_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 3; while (i > 0) { pr_info("waitqueue priority fn i = %d\n",i); i--; } return 0; } static int waitqueue_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 0; while ( i < 3) { pr_info("waitqueue fn i = %d\n",i); i++; } return 0; } .. _p2_addwq_66: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(add_wait_queue_priority_init); module_exit(add_wait_queue_priority_exit); .. _p2_addwq_67: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/add_wait_queue_priority/kthread.c :language: c :linenos: .. _p2_addwq_68: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/add_wait_queue_priority/Makefile :language: c :linenos: .. _p2_addwq_69: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of add_wait_queue_priority 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue_priority 7859 ? 00:00:00 add_wait_queue_priority wait_thread example 7860 ? 00:00:00 add_wait_queue_priority wake_up_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 4025.593901] inside kthread init function [ 4025.593970] kthread created successfully [ 4025.594009] kthread created successfully [ 4025.594014] thread 2 execution [ 4026.625693] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep add_wait_queue_priority test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [ 4037.183307] inside kthread exit function [ 4037.889741] waitqueue priority fn i = 3 [ 4037.889748] waitqueue priority fn i = 2 [ 4037.889750] waitqueue priority fn i = 1 [ 4037.889755] waitqueue fn i = 0 [ 4037.889757] waitqueue fn i = 1 [ 4037.889758] waitqueue fn i = 2 [ 4037.889759] waitqueue fn i = 0 [ 4037.889760] waitqueue fn i = 1 [ 4037.889762] waitqueue fn i = 2 [ 4037.889764] thread 1 execution [ 4038.913860] destroyed threads .. _p2_addwq_70: .. tab-set:: .. tab-item:: Example 5: Check for any waitqueue entries in waitqueue head using waitqueue_active * In this example let's see how to check for any waitqueue entries in waitqueue head using waitqueue_active. .. _p2_addwq_71: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_72: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("example of waitqueue_active"); .. _p2_addwq_73: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread and completion variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue); wait_queue_entry_t wait_entry_1; wait_queue_entry_t wait_entry_2; wait_queue_entry_t wait_entry_3; int wait_flag = 0; .. _p2_addwq_74: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init waitqueue_active_init(void) { pr_info("Inside kthread init function\n"); thread_start(); return 0; } .. _p2_addwq_75: .. tab-set:: .. tab-item:: thread start function * Add the thread start function called from module init function, which creates the thread, wakes it up and starts its execution. .. code-block:: void thread_start(void) { wait_thread = kthread_run(wait_thread_fn,NULL,"waitqueue_active 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,"waitqueue_active wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_76: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit waitqueue_active_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_77: .. tab-set:: .. tab-item:: thread stop function * Add the thread stop function which is called from module exit function, which destroys the thread and stops the execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_78: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_func_entry(&wait_entry_1,waitqueue_fn); init_waitqueue_func_entry(&wait_entry_2,waitqueue_fn); init_waitqueue_func_entry(&wait_entry_3,waitqueue_fn); add_wait_queue(&wqueue,&wait_entry_1); add_wait_queue(&wqueue,&wait_entry_2); add_wait_queue(&wqueue,&wait_entry_3); while(!kthread_should_stop()) { if (waitqueue_active(&wqueue)){ pr_info("waitqueues are pending\n"); wait_event_interruptible(wqueue,wait_flag != 0); msleep(1000); } } remove_wait_queue(&wqueue,&wait_entry_1); remove_wait_queue(&wqueue,&wait_entry_2); remove_wait_queue(&wqueue,&wait_entry_3); 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(&wqueue); return 0; } .. _p2_addwq_79: .. tab-set:: .. tab-item:: Waitqueue entry function API * Add the waitqueue entry function which is mapped to the waitqueue entry created. .. code-block:: c static int waitqueue_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 0; while ( i < 3) { pr_info("waitqueue fn i = %d\n",i); i++; } return 0; } .. _p2_addwq_100: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(waitqueue_active_init); module_exit(waitqueue_active_exit); .. _p2_addwq_101: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/waitqueue_active/kthread.c :language: c :linenos: .. _p2_addwq_102: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/waitqueue_active/Makefile :language: c :linenos: .. _p2_addwq_103: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of waitqueue_active 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep waitqueue_active 12444 ? 00:00:00 waitqueue_active wait_thread example 12445 ? 00:00:00 waitqueue_active wake_up_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [15037.653923] inside kthread init function [15037.653983] kthread created successfully [15037.653988] waitqueues are pending [15037.654015] kthread created successfully [15037.654019] thread 2 execution [15038.664157] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep waitqueue_active test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [15053.122433] inside kthread exit function [15054.024850] waitqueue fn i = 0 [15054.024859] waitqueue fn i = 1 [15054.024862] waitqueue fn i = 2 [15054.024864] waitqueue fn i = 0 [15054.024866] waitqueue fn i = 1 [15054.024868] waitqueue fn i = 2 [15054.024870] waitqueue fn i = 0 [15054.024872] waitqueue fn i = 1 [15054.024874] waitqueue fn i = 2 [15055.049009] destroyed threads .. _p2_addwq_104: .. tab-set:: .. tab-item:: Example 6: Check for any waiting waitqueue entries using wq_has_sleeper * In this example let's see how to check for any waiting waitqueue entries using wq_has_sleeper. .. _p2_addwq_105: .. tab-set:: .. tab-item:: List of headers * Include the follow header files(.h) to refer the API being used for the execution. .. code-block:: c #include #include #include #include #include #include .. _p2_addwq_106: .. tab-set:: .. tab-item:: Module macros * Add the following module macros to display information about the license, author and description about the module. .. code-block:: c MODULE_LICENSE("GPL"); MODULE_AUTHOR("Linux Usr"); MODULE_DESCRIPTION("example of wq_has_sleeper"); .. _p2_addwq_107: .. tab-set:: .. tab-item:: Initialize thread variables * Declare the thread and completion variables which we are going to create and use in this example .. code-block:: c static struct task_struct *wait_thread; static struct task_struct *wake_up_thread; static DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wqueue); wait_queue_entry_t wait_entry_1; wait_queue_entry_t wait_entry_2; wait_queue_entry_t wait_entry_3; int wait_flag = 0; .. _p2_addwq_108: .. tab-set:: .. tab-item:: Module init function * Add the module init function which will be executed once we load the kernel module using insmod command. .. code-block:: c static int __init wq_has_sleeper_init(void) { pr_info("Inside kthread init function\n"); thread_start(); return 0; } .. _p2_addwq_109: .. tab-set:: .. tab-item:: thread start function * Add the thread start function called from module init function, which creates the thread, wakes it up and starts its execution. .. code-block:: void thread_start(void) { wait_thread = kthread_run(wait_thread_fn,NULL,"wq_has_sleeper 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,"wq_has_sleeper wake_up_thread example"); if(IS_ERR(wake_up_thread)) pr_info("error creating thread\n"); else pr_info("kthread created successfully\n"); } .. _p2_addwq_110: .. tab-set:: .. tab-item:: Module exit function * Add the module exit function which will be executed once we unload the kernel module using rmmod command. .. code-block:: c static void __exit wq_has_sleeper_exit(void) { pr_info("Inside kthread exit function\n"); thread_stop(); } .. _p2_addwq_111: .. tab-set:: .. tab-item:: thread stop function * Add the thread stop function which is called from module exit function, which destroys the thread and stops the execution. .. code-block:: c void thread_stop(void) { kthread_stop(wake_up_thread); kthread_stop(wait_thread); pr_info("destroyed threads\n"); } .. _p2_addwq_112: .. tab-set:: .. tab-item:: Thread function API * Add the thread function API which will be called as soon as the kthread is created and is in running state. .. code-block:: c int wait_thread_fn(void *data) { init_waitqueue_func_entry(&wait_entry_1,waitqueue_fn); init_waitqueue_func_entry(&wait_entry_2,waitqueue_fn); init_waitqueue_func_entry(&wait_entry_3,waitqueue_fn); add_wait_queue(&wqueue,&wait_entry_1); add_wait_queue(&wqueue,&wait_entry_2); add_wait_queue(&wqueue,&wait_entry_3); while(!kthread_should_stop()) { if(wq_has_sleeper(&wqueue)) { pr_info("waitqueue entries are pending\n"); wait_event_interruptible(wqueue,wait_flag != 0); remove_wait_queue(&wqueue,&wait_entry_1); remove_wait_queue(&wqueue,&wait_entry_2); remove_wait_queue(&wqueue,&wait_entry_3); 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; } .. _p2_addwq_113: .. tab-set:: .. tab-item:: Waitqueue entry function API * Add the waitqueue entry function which is mapped to the waitqueue entry created. .. code-block:: c static int waitqueue_fn(wait_queue_entry_t *wait, unsigned mode, int flags,void *key) { int i = 0; while ( i < 3) { pr_info("waitqueue fn i = %d\n",i); i++; } return 0; } .. _p2_addwq_114: .. tab-set:: .. tab-item:: Driver entry points * Add the driver entry points which will be executed once the module is inserted or removed from the kernel. .. code-block:: c module_init(wq_has_sleeper_init); module_exit(wq_has_sleeper_exit); .. _p2_addwq_115: .. tab-set:: .. tab-item:: See the full program below .. tab-set:: .. tab-item:: kthread.c .. literalinclude:: p2_addwq/wq_has_sleeper/kthread.c :language: c :linenos: .. _p2_addwq_116: .. tab-set:: .. tab-item:: Makefile .. literalinclude:: p2_addwq/wq_has_sleeper/Makefile :language: c :linenos: .. _p2_addwq_117: .. tab-set:: .. tab-item:: Compile and load the module * Run make to compile the kernel source and generate the .ko image. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~/.../tc_1$ modinfo kthread.ko filename: $HOME/kthread_examples/kthread.ko description: Example of wq_has_sleeper 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. .. code-block:: shell test@test-V520-15IKL:~$ sudo insmod ./kthread.ko * check if the module is loaded or not using lsmod command. .. code-block:: shell test@test-V520-15IKL:~$ sudo lsmod | grep kthread kthread 16384 0 * check if the thread is created or not using ps command. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep wq_has_sleeper 16077 ? 00:00:00 wq_has_sleeper wait_thread example 16078 ? 00:00:00 wq_has_sleeper wake_up_thread example * check for the kernel messages from init function and thread function once the module is loaded and thread is created. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [18271.224100] inside kthread init function [18271.224171] kthread created successfully [18271.224179] waitqueue entries are pending [18271.224208] kthread created successfully [18271.224216] thread 2 execution [18272.255559] thread 2 execution * remove the module from kernel using rmmod command. .. code-block:: shell 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. .. code-block:: shell 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. .. code-block:: shell test@test-V520-15IKL:~$ ps -N | grep wq_has_sleeper test@test-V520-15IKL:~$ * Check for kernel messages from exit function using dmesg command. .. code-block:: shell test@test-V520-15IKL:~$ sudo dmesg [18280.647574] inside kthread exit function [18281.473143] waitqueue fn i = 0 [18281.473150] waitqueue fn i = 1 [18281.473151] waitqueue fn i = 2 [18281.473153] waitqueue fn i = 0 [18281.473154] waitqueue fn i = 1 [18281.473155] waitqueue fn i = 2 [18281.473156] waitqueue fn i = 0 [18281.473157] waitqueue fn i = 1 [18281.473158] waitqueue fn i = 2 [18282.497421] destroyed threads .. _p2_addwq_80: .. tab-set:: .. tab-item:: Summary of Kthread APIs =============================== ======================================= 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 =============================== ======================================= .. _p2_addwq_90: .. tab-set:: .. tab-item:: Summary of Waitqueue APIs ================================= ================================================================================== Waitqueue API Learning ================================= ================================================================================== DECLARE_WAIT_QUEUE_HEAD Declare waitqueue head init_waitqueue_entry Initialize a waitqueue entry init_waitqueue_func_entry Initialize a waitqueue entry and map a function to it add_wait_queue Add waitqueue entry to waitqueue head add_wait_queue_exclusive Add waitqueue entry to waitqueue head exclusively add_wait_queue_priority Add waitqueue entry to waitqueue head on priority manner remove_wait_queue Remove waitqueue entry from waitqueue head waitqueue_active locklessly test for waiters on the queue wq_has_sleeper check if there are any waiting processes wake_up wake up the waiting waitqueues under wait_event APIs wait_event_interruptible sleep until a condition gets true wake_up_event_interruptible wakes up the waiting queue ================================= ================================================================================== .. _p2_addwq_81: .. tab-set:: .. tab-item:: Summary of miscellaneous APIs =============================== =========================================================================================== 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 =============================== ===========================================================================================