If I create a kernel thread with kthread_run then kthread_stop it immediately, the kernel thread might be stopped without running. I checked the source code of kthread_run and kthread_stop in Linux-5.4.73
/**
* kthread_run - create and wake a thread.
* @threadfn: the function to run until signal_pending(current).
* @data: data ptr for @threadfn.
* @namefmt: printf-style name for the thread.
*
* Description: Convenient wrapper for kthread_create() followed by
* wake_up_process(). Returns the kthread or ERR_PTR(-ENOMEM).
*/
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
/**
* kthread_stop - stop a thread created by kthread_create().
* @k: thread created by kthread_create().
*
* Sets kthread_should_stop() for @k to return true, wakes it, and
* waits for it to exit. This can also be called after kthread_create()
* instead of calling wake_up_process(): the thread will exit without
* calling threadfn().
*
* If threadfn() may call do_exit() itself, the caller must ensure
* task_struct can't go away.
*
* Returns the result of threadfn(), or %-EINTR if wake_up_process()
* was never called.
*/
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_kthread(k);
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
kthread_unpark(k);
wake_up_process(k);
wait_for_completion(&kthread->exited);
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
It seems that the kernel thread should have been waken up before kthread_stop return, but it might not. I am really confused, could anybody help me?
My test code is as follows.
test1.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
#include <linux/spinlock.h>
MODULE_LICENSE("GPL");
static struct task_struct *t1;
static struct task_struct *t2;
static struct task_struct *t3;
static int func(void *__para)
{
const char *msg = (const char *)__para;
printk("%s %s\n", __func__, msg);
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
set_current_state(TASK_RUNNING);
printk("%s %s return\n", __func__, msg);
return 0;
}
static int __init start_init(void)
{
printk(KERN_INFO "Thread Creating...\n");
t1 = kthread_run(func, "t1", "t1");
if (IS_ERR(t1)) {
WARN_ON(1);
return 0;
}
t2 = kthread_run(func, "t2", "t2");
if (IS_ERR(t2)) {
WARN_ON(1);
return 0;
}
printk("Stopping t2\n");
kthread_stop(t2);
printk("t2 stopped\n");
t3 = kthread_run(func, "t3", "t3");
if (IS_ERR(t3)) {
WARN_ON(1);
return 0;
}
return 0;
}
static void __exit end_exit(void)
{
printk(KERN_INFO "Cleaning Up...\n");
if (IS_ERR(t1))
return;
printk("Stopping t1\n");
kthread_stop(t1);
printk("t1 stopped\n");
printk("Stopping t3\n");
kthread_stop(t3);
printk("t3 stopped\n");
}
module_init(start_init)
module_exit(end_exit)
Makefile
obj-m += test1.o
all:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd`
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=`pwd` clean
The command to run
sudo insmod test1.ko
sudo rmmod test1
The dmesg of the first run
[10914.046211] Thread Creating...
[10914.046515] func t1
[10914.046530] Stopping t2
[10914.046531] func t2
[10914.046533] func t2 return
[10914.046538] t2 stopped
[10914.046555] func t3
[10938.895544] Cleaning Up...
[10938.895545] Stopping t1
[10938.895552] func t1 return
[10938.895561] t1 stopped
[10938.895562] Stopping t3
[10938.895566] func t3 return
[10938.895587] t3 stopped
t2 has executed before stopped this time.
The dmesg of the second run
[10940.775771] Thread Creating...
[10940.776109] func t1
[10940.776126] Stopping t2
[10940.776138] t2 stopped
[10940.776162] func t3
[10956.375606] Cleaning Up...
[10956.375607] Stopping t1
[10956.375613] func t1 return
[10956.375674] t1 stopped
[10956.375674] Stopping t3
[10956.375678] func t3 return
[10956.375697] t3 stopped
t2 stopped without running. But t1 and t3 which are not stopped immediately run before being stopped.