From 7d16a5c024983579a367ceaca2bc46264ad355f6 Mon Sep 17 00:00:00 2001 From: Ciro Santilli Date: Sat, 7 Jul 2018 19:04:43 +0100 Subject: [PATCH] Move sleep and workqueue module doc to README sleep was broken because the workqueue was declared locally inside init, further evidence that no one has ever run the examples :-( --- README.adoc | 63 ++++++++++++++++++++++++++++++++- kernel_module/README.adoc | 3 -- kernel_module/sleep.c | 42 +++++----------------- kernel_module/work_from_work.c | 8 ++--- kernel_module/workqueue_cheat.c | 35 ++++++++---------- 5 files changed, 89 insertions(+), 62 deletions(-) diff --git a/README.adoc b/README.adoc index d7df9a2..6ee1778 100644 --- a/README.adoc +++ b/README.adoc @@ -1440,7 +1440,7 @@ does not give any interesting hits at `cc`, no symbol was placed that far. ==== GDB module_init -TODO find a convenient method. We have working methods, but they are not convenient. +TODO find a more convenient method. We have working methods, but they are not ideal. This is not very easy, since by the time the module finishes loading, and `lx-symbols` can work properly, `module_init` has already finished running! @@ -3993,6 +3993,8 @@ The count stops when we `rmmod`: rmmod kthread .... +The sleep is done with `usleep_range`, see: <>. + Bibliography: * http://stackoverflow.com/questions/10177641/proper-way-of-handling-threads-in-kernel @@ -4032,6 +4034,65 @@ Possible very likely outcome: The threads almost always interleaved nicely, thus confirming that they are actually running in parallel. +===== sleep + +Count to dmesg every one second from `0` up to `n - 1`: + +.... +insmod /sleep.ko n=5 +.... + +Source: link:kernel_module/sleep.c[] + +The sleep is done with a call to `usleep_range` directly inside `module_init` for simplicity. + +Bibliography: + +* https://stackoverflow.com/questions/15994603/how-to-sleep-in-the-linux-kernel/44153288#44153288 +* https://github.com/torvalds/linux/blob/v4.17/Documentation/timers/timers-howto.txt + +===== Workqueue + +A more convenient front-end for <>: + +.... +insmod /workqueue_cheat.ko +.... + +Outcome: count from `0` to `9` infinitely many times + +Stop counting: + +.... +rmmod workqueue_cheat +.... + +Source: link:kernel_module/workqueue_cheat.c[] + +The workqueue thread is killed after the worker function returns. + +We can't call the module just `workqueue.c` because there is already a built-in with that name: https://unix.stackexchange.com/questions/364956/how-can-insmod-fail-with-kernel-module-is-already-loaded-even-is-lsmod-does-not + +Bibliography: https://github.com/torvalds/linux/blob/v4.17/Documentation/core-api/workqueue.rst + +===== Workqueue from workqueue + +Count from `0` to `9` every second infinitely many times by scheduling a new work item from a work item: + +.... +insmod /work_from_work.ko +.... + +Stop: + +.... +rmmod work_from_work +.... + +The sleep is done indirectly through: `queue_delayed_work`, which waits the specified time before scheduling the work. + +Source: link:kernel_module/work_from_work.c[] + ===== schedule Let's block the entire kernel! Yay: diff --git a/kernel_module/README.adoc b/kernel_module/README.adoc index 69549cc..69c424b 100644 --- a/kernel_module/README.adoc +++ b/kernel_module/README.adoc @@ -1,7 +1,4 @@ https://github.com/cirosantilli/linux-kernel-module-cheat#directory-structure . Asynchronous -.. link:sleep.c[] .. link:timer.c[] -.. link:work_from_work.c[] -.. link:workqueue_cheat.c[] diff --git a/kernel_module/sleep.c b/kernel_module/sleep.c index 7f2ec21..4acd3c0 100644 --- a/kernel_module/sleep.c +++ b/kernel_module/sleep.c @@ -1,48 +1,24 @@ -/* -Usage: - - insmod /sleep.ko - rmmod sleep - -dmesg prints an integer every second until rmmod. - -Since insmod returns, this also illustrates how the work queues are asynchronous. -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#sleep */ #include /* usleep_range */ #include #include #include /* atomic_t */ -#include -static struct workqueue_struct *queue; -static atomic_t run = ATOMIC_INIT(1); - -static void work_func(struct work_struct *work) -{ - int i = 0; - while (atomic_read(&run)) { - pr_info("%d\n", i); - usleep_range(1000000, 1000001); - i++; - if (i == 10) - i = 0; - } -} +static u32 n = 5; +module_param(n, int, S_IRUSR | S_IWUSR); static int myinit(void) { - DECLARE_WORK(work, work_func); - queue = create_workqueue("myworkqueue"); - queue_work(queue, &work); + u32 i; + for (i = 0; i < n; ++i) { + pr_info("%d\n", i); + usleep_range(1000000, 1000001); + } return 0; } -static void myexit(void) -{ - atomic_set(&run, 0); - destroy_workqueue(queue); -} +static void myexit(void) {} module_init(myinit) module_exit(myexit) diff --git a/kernel_module/work_from_work.c b/kernel_module/work_from_work.c index f7c8048..ca05f8f 100644 --- a/kernel_module/work_from_work.c +++ b/kernel_module/work_from_work.c @@ -1,12 +1,10 @@ -/* -Declare more work from a workqueue. -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#workqueue-from-workqueue */ #include #include #include -static int i = 0; +static int i; static struct workqueue_struct *queue; static void work_func(struct work_struct *work); @@ -18,6 +16,8 @@ static void work_func(struct work_struct *work) { pr_info("%d\n", i); i++; + if (i == 10) + i = 0; queue_delayed_work(queue, &next_work, HZ); } diff --git a/kernel_module/workqueue_cheat.c b/kernel_module/workqueue_cheat.c index 4f98e18..8be493f 100644 --- a/kernel_module/workqueue_cheat.c +++ b/kernel_module/workqueue_cheat.c @@ -1,45 +1,38 @@ -/* -Usage: - - insmod /workqueue_cheat.ko - # dmesg => worker - rmmod workqueue_cheat - -Creates a separate thread. So init_module can return, but some work will still get done. - -Can't call this just workqueue.c because there is already a built-in with that name: -https://unix.stackexchange.com/questions/364956/how-can-insmod-fail-with-kernel-module-is-already-loaded-even-is-lsmod-does-not - -Workqueues are a convenience frontend for kthreads. - -Bibliography: - -- https://www.ibm.com/developerworks/library/l-tasklets/ -*/ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#workqueue */ +#include /* usleep_range */ #include #include +#include /* atomic_t */ #include static struct workqueue_struct *queue; +static atomic_t run = ATOMIC_INIT(1); static void work_func(struct work_struct *work) { - pr_info("worker\n"); + int i = 0; + while (atomic_read(&run)) { + pr_info("%d\n", i); + usleep_range(1000000, 1000001); + i++; + if (i == 10) + i = 0; + } } DECLARE_WORK(work, work_func); static int myinit(void) { - queue = create_singlethread_workqueue("myworkqueue"); + queue = create_workqueue("myworkqueue"); queue_work(queue, &work); return 0; } static void myexit(void) { - /* Waits for jobs to finish. */ + atomic_set(&run, 0); destroy_workqueue(queue); }