diff --git a/src/thread.c b/src/thread.c index e6dc919..c54c5df 100644 --- a/src/thread.c +++ b/src/thread.c @@ -9,6 +9,7 @@ #include #include #include +#include #include "thread.h" @@ -19,7 +20,9 @@ * will get the actual thread function from there and call it. * * Once the actual thread function has exited, this function will trigger the completion object - * so that the thread can be cleaned up using kthread_stop(). + * so that the thread can be cleaned up using kthread_stop(). Because the thread function + * returns on its own, the task_struct is only guaranteed to still be there because + * ipts_thread_start() holds a reference to it. */ static int ipts_thread_runner(void *data) { @@ -40,14 +43,36 @@ bool ipts_thread_should_stop(struct ipts_thread *thread) int ipts_thread_start(struct ipts_thread *thread, int (*threadfn)(struct ipts_thread *thread), void *data, const char *name) { + struct task_struct *task = NULL; + init_completion(&thread->done); thread->data = data; thread->should_stop = false; thread->threadfn = threadfn; - thread->thread = kthread_run(ipts_thread_runner, thread, name); - return PTR_ERR_OR_ZERO(thread->thread); + task = kthread_create(ipts_thread_runner, thread, name); + if (IS_ERR(task)) { + /* + * Do not leave an error pointer behind: ipts_thread_stop() only checks for + * NULL, and would pass it on to kthread_stop(). + */ + thread->thread = NULL; + return PTR_ERR(task); + } + + /* + * ipts_thread_runner() completes &thread->done and then returns, so the kthread can + * exit and have its task_struct freed before ipts_thread_stop() gets to call + * kthread_stop() on it. Hold a reference so that the task stays valid until then, as + * kthread_stop() requires of a thread function that may exit on its own. + */ + get_task_struct(task); + thread->thread = task; + + wake_up_process(task); + + return 0; } int ipts_thread_stop(struct ipts_thread *thread) @@ -65,7 +90,7 @@ int ipts_thread_stop(struct ipts_thread *thread) wmb(); wait_for_completion(&thread->done); - ret = kthread_stop(thread->thread); + ret = kthread_stop_put(thread->thread); thread->thread = NULL; thread->data = NULL;