Check Workqueue

#

Version

Ubuntu

Ubuntu 22.10

Kernel

6.8.0

  • In this program, you are going to learn

  • How to create workqueue ?

  • How to allocate the work to the queue ?

#include <linux/workqueue.h>

#define create_workqueue(name)
  • Here is an example of how to use the API,

my_workqueue = create_workqueue(WQ_NAME);
  • Here is the function prototype of the API: INIT_WORK

#include <linux/workqueue.h>

#define INIT_WORK(_work, _func)
  • Here is an example of how to use the API,

INIT_WORK(&my_work_1,workqueue_func_1);
  • Here is the function prototype of the API: queue_work

#include <linux/workqueue.h>

static inline bool queue_work(struct workqueue_struct *wq, struct work_struct *work);
  • Here is an example of how to use the API,

queue_work(my_workqueue,&my_work_1);
  • Here is the function prototype of the API: queue_work

#include <linux/workqueue.h>

extern void destroy_workqueue(struct workqueue_struct *wq);
  • Here is an example of how to use the API,

destroy_workqueue(my_workqueue);
#include <linux/workqueue.h>

#define work_pending(work)
  • Here is an example of how to use the API,

work_pending(&my_work_1);
#include <linux/workqueue.h>

#define INIT_DELAYED_WORK(_work, _func)
  • Here is an example of how to use the API,

INIT_DELAYED_WORK(&my_work_1,workqueue_func_1);
INIT_DELAYED_WORK(&my_work_2,workqueue_func_2);
#include <linux/workqueue.h>

#define delayed_work_pending(w)
  • Here is an example of how to use the API,

delayed_work_pending(&my_work_1);
#include <linux/workqueue.h>

extern bool cancel_delayed_work_sync(struct delayed_work *dwork);
  • Here is an example of how to use the API,

cancel_delayed_work_sync(&my_work_1);
cancel_delayed_work_sync(&my_work_2);
#include <linux/workqueue.h>

static inline bool schedule_work(struct work_struct *work);
  • Here is an example of how to use the API,

schedule_work(&my_work_1);
schedule_work(&my_work_2);
  • Here is the function prototype of the API: cancel_work

#include <linux/workqueue.h>

extern bool cancel_work(struct work_struct *work);
  • Here is an example of how to use the API,

cancel_work(&my_work_1);
cancel_work(&my_work_2);
#include <linux/workqueue.h>

static inline bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
  • Here is an example of how to use the API,

schedule_delayed_work(&my_work,msecs_to_jiffies(100));
#include <linux/workqueue.h>

extern bool cancel_delayed_work(struct delayed_work *dwork);
  • Here is an example of how to use the API,

cancel_delayed_work(&my_work);
  • In this example let’s see how to check the workqueue and execute it.

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

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/jiffies.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 work_pending");
  • Initialize the work which we are going to use in this example

INIT_WORK(&my_work_1,workqueue_func_1);
INIT_WORK(&my_work_2,workqueue_func_2);
  • This workqueue_func_1 function executes when work is queued to workqueue.

void workqueue_func_1(struct work_struct *work)
{
        int i = 0;
        while (i < 10) {
                pr_info("Inside workqueue_func_1\n");
                i++;
                msleep(1000);
        }
}
  • This workqueue_func_2 function executes when work is queued to workqueue.

void workqueue_func_2(struct work_struct *work)
{
        int i = 0;
        while (i < 10) {
                pr_info("Inside workqueue_func_1\n");
                i++;
                msleep(1000);
        }
}
  • This function creates workqueue, intiializes work and queues to the workqueue.

void queue_start(void)
{
    my_workqueue = create_workqueue(WQ_NAME);
    INIT_WORK(&my_work_1,workqueue_func_1);
    INIT_WORK(&my_work_2,workqueue_func_2);
    if (!work_pending(&my_work_1)) {
        schedule_work(&my_work_1);
        queue_work(my_workqueue,&my_work_1);
    }
    if (!work_pending(&my_work_2)) {
        schedule_work(&my_work_2);
        queue_work(my_workqueue,&my_work_2);
    }
}
  • This function executes when module is loaded.

static int __init queue_init(void)
{
        pr_info("Inside queue_init function\n");
        queue_start();
        return 0;
}
  • This function executes during execution of queue_exit, waits until the completion and destroys workqueue.

void queue_stop(void)
{
        pr_info("Destroying workqueue\n");
        cancel_work(&my_work_1);
        cancel_work(&my_work_2);
}
  • This function executes when module is unloaded.

static void __exit queue_exit(void)
{
        pr_info("Inside queue_exit function\n");
        queue_stop();
        return;
}
  • Add the driver entry points which will be executed once the module is inserted or removed from the kernel.

module_init(queue_init);
module_exit(queue_exit);
  1#include <linux/init.h>
  2#include <linux/kernel.h>
  3#include <linux/module.h>
  4#include <linux/workqueue.h>
  5#include <linux/delay.h>
  6#include <linux/jiffies.h>
  7
  8#define WQ_NAME "tc8_workqueue"
  9
 10MODULE_LICENSE("GPL");
 11MODULE_AUTHOR("Linux_usr");
 12MODULE_DESCRIPTION("Example of work_pending");
 13
 14struct workqueue_struct *my_workqueue;
 15struct work_struct my_work_1;
 16struct work_struct my_work_2;
 17void workqueue_func_1(struct work_struct *work);
 18void workqueue_func_2(struct work_struct *work);
 19void queue_start(void);
 20void queue_stop(void);
 21
 22/* workqueue_func_1 - executes when work is queued to workqueue,
 23 * prints a statement and sleeps for 1000ms,
 24 * stops when i value is greater than 10 */
 25
 26void workqueue_func_1(struct work_struct *work)
 27{
 28    int i = 0;
 29    while (i < 10) {
 30        pr_info("Inside workqueue_func_1\n");
 31        i++;
 32        msleep(500);
 33    }
 34}
 35
 36/* workqueue_func_2 - executes when work is queued to workqueue,
 37 * prints a statement and sleeps for 1000ms,
 38 * stops when i value is greater than 10 */
 39
 40void workqueue_func_2(struct work_struct *work)
 41{
 42    int i = 0;
 43    while (i < 10) {
 44        pr_info("Inside workqueue_func_2\n");
 45        i++;
 46        msleep(500);
 47    }
 48}
 49
 50/* queue_start - executes during execution of queue_start,
 51 * creates workqueue,
 52 * intiializes work and queues to the workqueue */
 53
 54void queue_start(void)
 55{
 56    my_workqueue = create_workqueue(WQ_NAME);
 57    INIT_WORK(&my_work_1,workqueue_func_1);
 58    INIT_WORK(&my_work_2,workqueue_func_2);
 59    if (!work_pending(&my_work_1)) {
 60        schedule_work(&my_work_1);
 61        queue_work(my_workqueue,&my_work_1);
 62    }
 63    if (!work_pending(&my_work_2)) {
 64        schedule_work(&my_work_2);
 65        queue_work(my_workqueue,&my_work_2);
 66    }
 67}
 68
 69/* queue_init - executes when module is loaded,
 70 * calls queue_start */
 71
 72static int __init queue_init(void)
 73{
 74    pr_info("Inside queue_init function\n");
 75    queue_start();
 76    return 0;
 77}
 78
 79/* queue_stop - executes during execution of queue_exit,
 80 * waits until the completion and destroys workqueue */
 81
 82void queue_stop(void)
 83{
 84    pr_info("Destroying workqueue\n");
 85    cancel_work(&my_work_1);
 86    cancel_work(&my_work_2);
 87}
 88
 89/* queue_exit - executes when module is unloaded,
 90 * calls queue_stop */
 91
 92static void __exit queue_exit(void)
 93{
 94    pr_info("Inside queue_exit function\n");
 95    queue_stop();
 96    return;
 97}
 98
 99module_init(queue_init);
100module_exit(queue_exit);
1obj-m += tc1.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to load the kernel module.

$ make
  • Check if work_queue.ko is generated or not using ls command.

$ ls -l
total 384
-rw-rw-r-- 1 test test    151 Aug  2 11:47 Makefile
-rw-rw-r-- 1 test test     76 Aug  2 12:48 modules.order
-rw-rw-r-- 1 test test      0 Aug  2 12:48 Module.symvers
-rw-rw-r-- 1 test test   2040 Aug  2 11:47 tc1.c
-rw-rw-r-- 1 test test 180944 Aug  2 12:48 tc1.ko
-rw-rw-r-- 1 test test     76 Aug  2 12:48 tc1.mod
-rw-rw-r-- 1 test test   1103 Aug  2 12:48 tc1.mod.c
-rw-rw-r-- 1 test test 149280 Aug  2 12:48 tc1.mod.o
-rw-rw-r-- 1 test test  33112 Aug  2 12:48 tc1.o
  • Load the module to kernel using insmod command.

$ sudo insmod ./tc1.ko
  • Check kernel messages to verify if the module is loaded or not.

$ dmesg
[ 1812.726131] Inside queue_init function
[ 1812.726612] Inside workqueue_func_2
[ 1812.726979] Inside workqueue_func_1
[ 1813.754717] Inside workqueue_func_1
[ 1814.778711] Inside workqueue_func_1
[ 1815.802723] Inside workqueue_func_1
[ 1816.826704] Inside workqueue_func_1
[ 1817.850705] Inside workqueue_func_1
[ 1818.874707] Inside workqueue_func_1
[ 1819.898722] Inside workqueue_func_1
[ 1820.922729] Inside workqueue_func_1
[ 1821.946714] Inside workqueue_func_1
  • Unload the module using rmmod command.

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

$ dmesg
[ 1835.094347] Inside queue_exit function
  • In this example let’s see how to check the workqueue and execute it.

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

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/jiffies.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 delayed_work_pending");
  • Initialize the work which we are going to use in this example

INIT_WORK_ONSTACK(&my_work_1,workqueue_func_1);
INIT_WORK_ONSTACK(&my_work_2,workqueue_func_2);
  • This workqueue_func_1 function executes when work is queued to workqueue.

void workqueue_func_1(struct work_struct *work)
{
        int i = 0;
        while (i < 10) {
                pr_info("Inside workqueue_func_1\n");
                i++;
                msleep(1000);
        }
}
  • This workqueue_func_2 function executes when work is queued to workqueue.

void workqueue_func_2(struct work_struct *work)
{
        int i = 0;
        while (i < 10) {
                pr_info("Inside workqueue_func_2\n");
                i++;
                msleep(1000);
        }
}
  • This function creates workqueue, intiializes work and queues to the workqueue.

void queue_start(void)
{
    INIT_DELAYED_WORK(&my_work_1,workqueue_func_1);
    INIT_DELAYED_WORK(&my_work_2,workqueue_func_2);
    if (!delayed_work_pending(&my_work_1)) {
        pr_info("Delayed work is not scheduled yet\n");
        schedule_delayed_work(&my_work_1,msecs_to_jiffies(500));
    }
    schedule_delayed_work(&my_work_2,msecs_to_jiffies(500));
}
  • This function executes when module is loaded.

static int __init queue_init(void)
{
        pr_info("Inside queue_init function\n");
        queue_start();
        return 0;
}
  • This function executes during execution of queue_exit, waits until the completion and destroys workqueue.

void queue_stop(void)
{
    pr_info("Destroying workqueue\n");
    cancel_delayed_work_sync(&my_work_1);
    cancel_delayed_work(&my_work_2);
}
  • This function executes when module is unloaded.

static void __exit queue_exit(void)
{
        pr_info("Inside queue_exit function\n");
        queue_stop();
        return;
}
  • Add the driver entry points which will be executed once the module is inserted or removed from the kernel.

module_init(queue_init);
module_exit(queue_exit);
 1#include <linux/init.h>
 2#include <linux/kernel.h>
 3#include <linux/module.h>
 4#include <linux/workqueue.h>
 5#include <linux/delay.h>
 6#include <linux/jiffies.h>
 7
 8#define WQ_NAME "tc7_workqueue"
 9
10MODULE_LICENSE("GPL");
11MODULE_AUTHOR("Linux_usr");
12MODULE_DESCRIPTION("Example of delayed_work_pending");
13
14struct delayed_work my_work_1;
15struct delayed_work my_work_2;
16void workqueue_func_1(struct work_struct *work);
17void workqueue_func_2(struct work_struct *work);
18void queue_start(void);
19void queue_stop(void);
20
21/* workqueue_func_1 - executes when work is queued to workqueue,
22 * prints a statement and sleeps for 1000ms,
23 * stops when i value is greater than 10 */
24
25void workqueue_func_1(struct work_struct *work)
26{
27    int i = 0;
28    while (i < 10) {
29        pr_info("Inside workqueue_func_1\n");
30        i++;
31        msleep(500);
32    }
33}
34
35/* workqueue_func_2 - executes when work is queued to workqueue,
36 * prints a statement and sleeps for 1000ms,
37 * stops when i value is greater than 10 */
38
39void workqueue_func_2(struct work_struct *work)
40{
41    int i = 0;
42    while (i < 10) {
43        pr_info("Inside workqueue_func_2\n");
44        i++;
45        msleep(500);
46    }
47}
48
49/* queue_start - executes during execution of queue_start,
50 * creates workqueue,
51 * intiializes work and queues to the workqueue */
52
53void queue_start(void)
54{
55    INIT_DELAYED_WORK(&my_work_1,workqueue_func_1);
56    INIT_DELAYED_WORK(&my_work_2,workqueue_func_2);
57    if (!delayed_work_pending(&my_work_1)) {
58        pr_info("Delayed work is not scheduled yet\n");
59        schedule_delayed_work(&my_work_1,msecs_to_jiffies(500));
60    }
61    schedule_delayed_work(&my_work_2,msecs_to_jiffies(500));
62}
63
64/* queue_init - executes when module is loaded,
65 * calls queue_start */
66
67static int __init queue_init(void)
68{
69    pr_info("Inside queue_init function\n");
70    queue_start();
71    return 0;
72}
73
74/* queue_stop - executes during execution of queue_exit,
75 * waits until the completion and destroys workqueue */
76
77void queue_stop(void)
78{
79    pr_info("Destroying workqueue\n");
80    cancel_delayed_work_sync(&my_work_1);
81    cancel_delayed_work(&my_work_2);
82}
83
84/* queue_exit - executes when module is unloaded,
85 * calls queue_stop */
86
87static void __exit queue_exit(void)
88{
89    pr_info("Inside queue_exit function\n");
90    queue_stop();
91    return;
92}
93
94module_init(queue_init);
95module_exit(queue_exit);
1obj-m += tc2.o
2all:
3	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
4clean:
5	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
  • Run make to load the kernel module.

$ make
  • Check if work_queue.ko is generated or not using ls command.

$ ls -l
total 384
-rw-rw-r-- 1 test test    151 Aug  2 11:47 Makefile
-rw-rw-r-- 1 test test     84 Aug  2 12:51 modules.order
-rw-rw-r-- 1 test test      0 Aug  2 12:51 Module.symvers
-rw-rw-r-- 1 test test   2356 Aug  2 11:47 tc2.c
-rw-rw-r-- 1 test test 181992 Aug  2 12:51 tc2.ko
-rw-rw-r-- 1 test test     84 Aug  2 12:51 tc2.mod
-rw-rw-r-- 1 test test   1103 Aug  2 12:51 tc2.mod.c
-rw-rw-r-- 1 test test 149296 Aug  2 12:51 tc2.mod.o
-rw-rw-r-- 1 test test  34160 Aug  2 12:51 tc2.o
  • Load the module to kernel using insmod command.

$ sudo insmod ./tc2.ko
  • Check kernel messages to verify if the module is loaded or not.

$ dmesg
[ 1927.976881] Inside queue_init function
[ 1927.977802] Inside workqueue_func_1
[ 1927.977886] Inside workqueue_func_2
[ 1928.986617] Inside workqueue_func_2
[ 1928.986634] Inside workqueue_func_1
[ 1930.010693] Inside workqueue_func_2
[ 1930.010731] Inside workqueue_func_1
[ 1931.034722] Inside workqueue_func_2
[ 1931.034739] Inside workqueue_func_1
[ 1932.058735] Inside workqueue_func_2
[ 1932.058751] Inside workqueue_func_1
[ 1933.082732] Inside workqueue_func_2
[ 1933.082760] Inside workqueue_func_1
[ 1934.106692] Inside workqueue_func_1
[ 1934.106709] Inside workqueue_func_2
[ 1935.130737] Inside workqueue_func_2
[ 1935.130753] Inside workqueue_func_1
[ 1936.154741] Inside workqueue_func_1
[ 1936.154758] Inside workqueue_func_2
[ 1937.178738] Inside workqueue_func_2
[ 1937.178755] Inside workqueue_func_1
  • Unload the module using rmmod command.

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

$ dmesg
[ 1955.215382] Inside queue_exit function