Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/thread.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <linux/err.h>
#include <linux/kthread.h>
#include <linux/mutex.h>
#include <linux/sched/task.h>

#include "thread.h"

Expand All @@ -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)
{
Expand All @@ -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)
Expand All @@ -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;
Expand Down