diff --git a/examples/completions.c b/examples/completions.c index 9fd229c..c02341f 100644 --- a/examples/completions.c +++ b/examples/completions.c @@ -9,34 +9,32 @@ #include #include -static struct { - struct completion crank_comp; - struct completion flywheel_comp; -} machine; +static struct completion crank_comp; +static struct completion flywheel_comp; static int machine_crank_thread(void *arg) { pr_info("Turn the crank\n"); - complete_all(&machine.crank_comp); + complete_all(&crank_comp); #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 - complete_and_exit(&machine.crank_comp, 0); + complete_and_exit(&crank_comp, 0); #endif } 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"); - complete_all(&machine.flywheel_comp); + complete_all(&flywheel_comp); #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 - complete_and_exit(&machine.flywheel_comp, 0); + complete_and_exit(&flywheel_comp, 0); #endif } @@ -47,8 +45,8 @@ static int __init completions_init(void) pr_info("completions example\n"); - init_completion(&machine.crank_comp); - init_completion(&machine.flywheel_comp); + init_completion(&crank_comp); + init_completion(&flywheel_comp); crank_thread = kthread_create(machine_crank_thread, NULL, "KThread Crank"); if (IS_ERR(crank_thread)) @@ -73,8 +71,8 @@ ERROR_THREAD_1: static void __exit completions_exit(void) { - wait_for_completion(&machine.crank_comp); - wait_for_completion(&machine.flywheel_comp); + wait_for_completion(&crank_comp); + wait_for_completion(&flywheel_comp); pr_info("completions exit\n"); } diff --git a/lkmpg.tex b/lkmpg.tex index 58ba1e1..d961183 100644 --- a/lkmpg.tex +++ b/lkmpg.tex @@ -1645,17 +1645,20 @@ $ 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. -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} -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} \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.