mirror of
https://github.com/sysprog21/lkmpg.git
synced 2024-11-24 18:11:53 +08:00
Simplify code by removal of outer struct
Two struct completion(s) are encapsulated within another 'struct machine'. Simplify the code by removing the outer struct and let the struct completion(s) be self-standing. Update description in tex to match code.
This commit is contained in:
parent
c6b02adc98
commit
bf523b2332
|
@ -9,34 +9,32 @@
|
||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
|
||||||
static struct {
|
static struct completion crank_comp;
|
||||||
struct completion crank_comp;
|
static struct completion flywheel_comp;
|
||||||
struct completion flywheel_comp;
|
|
||||||
} machine;
|
|
||||||
|
|
||||||
static int machine_crank_thread(void *arg)
|
static int machine_crank_thread(void *arg)
|
||||||
{
|
{
|
||||||
pr_info("Turn the crank\n");
|
pr_info("Turn the crank\n");
|
||||||
|
|
||||||
complete_all(&machine.crank_comp);
|
complete_all(&crank_comp);
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
||||||
kthread_complete_and_exit(&machine.crank_comp, 0);
|
kthread_complete_and_exit(&crank_comp, 0);
|
||||||
#else
|
#else
|
||||||
complete_and_exit(&machine.crank_comp, 0);
|
complete_and_exit(&crank_comp, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int machine_flywheel_spinup_thread(void *arg)
|
static int machine_flywheel_spinup_thread(void *arg)
|
||||||
{
|
{
|
||||||
wait_for_completion(&machine.crank_comp);
|
wait_for_completion(&crank_comp);
|
||||||
|
|
||||||
pr_info("Flywheel spins up\n");
|
pr_info("Flywheel spins up\n");
|
||||||
|
|
||||||
complete_all(&machine.flywheel_comp);
|
complete_all(&flywheel_comp);
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 17, 0)
|
||||||
kthread_complete_and_exit(&machine.flywheel_comp, 0);
|
kthread_complete_and_exit(&flywheel_comp, 0);
|
||||||
#else
|
#else
|
||||||
complete_and_exit(&machine.flywheel_comp, 0);
|
complete_and_exit(&flywheel_comp, 0);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,8 +45,8 @@ static int __init completions_init(void)
|
||||||
|
|
||||||
pr_info("completions example\n");
|
pr_info("completions example\n");
|
||||||
|
|
||||||
init_completion(&machine.crank_comp);
|
init_completion(&crank_comp);
|
||||||
init_completion(&machine.flywheel_comp);
|
init_completion(&flywheel_comp);
|
||||||
|
|
||||||
crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank");
|
crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank");
|
||||||
if (IS_ERR(crank_thread))
|
if (IS_ERR(crank_thread))
|
||||||
|
@ -73,8 +71,8 @@ ERROR_THREAD_1:
|
||||||
|
|
||||||
static void __exit completions_exit(void)
|
static void __exit completions_exit(void)
|
||||||
{
|
{
|
||||||
wait_for_completion(&machine.crank_comp);
|
wait_for_completion(&crank_comp);
|
||||||
wait_for_completion(&machine.flywheel_comp);
|
wait_for_completion(&flywheel_comp);
|
||||||
|
|
||||||
pr_info("completions exit\n");
|
pr_info("completions exit\n");
|
||||||
}
|
}
|
||||||
|
|
19
lkmpg.tex
19
lkmpg.tex
|
@ -1645,17 +1645,20 @@ $
|
||||||
Sometimes one thing should happen before another within a module having multiple threads.
|
Sometimes one thing should happen before another within a module having multiple threads.
|
||||||
Rather than using \sh|/bin/sleep| commands, the kernel has another way to do this which allows timeouts or interrupts to also happen.
|
Rather than using \sh|/bin/sleep| commands, the kernel has another way to do this which allows timeouts or interrupts to also happen.
|
||||||
|
|
||||||
In the following example two threads are started, but one needs to start before another.
|
Completions as code synchronization mechanism have three main parts, initialization of struct completion synchronization object, the waiting or barrier part through \cpp|wait_for_completion()|, and the signalling side through a call to \cpp|complete()|.
|
||||||
|
|
||||||
|
In the subsequent example, two threads are initiated: crank and flywheel.
|
||||||
|
It is imperative that the crank thread starts before the flywheel thread.
|
||||||
|
A completion state is established for each of these threads, with a distinct completion defined for both the crank and flywheel threads.
|
||||||
|
At the exit point of each thread the respective completion state is updated, and \cpp|wait_for_completion| is used by the flywheel thread to ensure that it does not begin prematurely.
|
||||||
|
The crank thread uses the \cpp|complete_all()| function to update the completion, which lets the flywheel thread continue.
|
||||||
|
|
||||||
|
So even though \cpp|flywheel_thread| is started first you should notice when you load this module and run \sh|dmesg|, that turning the crank always happens first because the flywheel thread waits for the crank thread to complete.
|
||||||
|
|
||||||
|
There are other variations of the \cpp|wait_for_completion| function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
|
||||||
|
|
||||||
\samplec{examples/completions.c}
|
\samplec{examples/completions.c}
|
||||||
|
|
||||||
The \cpp|machine| structure stores the completion states for the two threads.
|
|
||||||
At the exit point of each thread the respective completion state is updated, and \cpp|wait_for_completion| is used by the flywheel thread to ensure that it does not begin prematurely.
|
|
||||||
|
|
||||||
So even though \cpp|flywheel_thread| is started first you should notice if you load this module and run \sh|dmesg| that turning the crank always happens first because the flywheel thread waits for it to complete.
|
|
||||||
|
|
||||||
There are other variations upon the \cpp|wait_for_completion| function, which include timeouts or being interrupted, but this basic mechanism is enough for many common situations without adding a lot of complexity.
|
|
||||||
|
|
||||||
\section{Avoiding Collisions and Deadlocks}
|
\section{Avoiding Collisions and Deadlocks}
|
||||||
\label{sec:synchronization}
|
\label{sec:synchronization}
|
||||||
If processes running on different CPUs or in different threads try to access the same memory, then it is possible that strange things can happen or your system can lock up.
|
If processes running on different CPUs or in different threads try to access the same memory, then it is possible that strange things can happen or your system can lock up.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user