summaryrefslogtreecommitdiff
path: root/linux/kthread.c
blob: 17830e5fd7b4b30250c49abc8ac15b2bde67742a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include <pthread.h>
#include <stdlib.h>
#include <string.h>

#include <linux/bitops.h>
#include <linux/kthread.h>
#include <linux/rcupdate.h>
#include <linux/sched.h>

#include "tools-util.h"

enum KTHREAD_BITS {
	KTHREAD_IS_PER_CPU = 0,
	KTHREAD_SHOULD_STOP,
	KTHREAD_SHOULD_PARK,
	KTHREAD_IS_PARKED,
};

static void *kthread_start_fn(void *data)
{
	rcu_register_thread();

	current = data;
	schedule();
	current->thread_fn(current->thread_data);

	complete(&current->exited);
	put_task_struct(current);
	rcu_unregister_thread();
	return NULL;
}

/**
 * kthread_create_on_node - create a kthread.
 * @threadfn: the function to run until signal_pending(current).
 * @data: data ptr for @threadfn.
 * @node: task and thread structures for the thread are allocated on this node
 * @namefmt: printf-style name for the thread.
 *
 * Description: This helper function creates and names a kernel
 * thread.  The thread will be stopped: use wake_up_process() to start
 * it.  See also kthread_run().  The new thread has SCHED_NORMAL policy and
 * is affine to all CPUs.
 *
 * If thread is going to be bound on a particular cpu, give its node
 * in @node, to get NUMA affinity for kthread stack, or else give NUMA_NO_NODE.
 * When woken, the thread will run @threadfn() with @data as its
 * argument. @threadfn() can either call do_exit() directly if it is a
 * standalone thread for which no one will call kthread_stop(), or
 * return when 'kthread_should_stop()' is true (which means
 * kthread_stop() has been called).  The return value should be zero
 * or a negative error number; it will be passed to kthread_stop().
 *
 * Returns a task_struct or ERR_PTR(-ENOMEM) or ERR_PTR(-EINTR).
 */
struct task_struct *kthread_create(int (*thread_fn)(void *data),
				   void *thread_data,
				   const char namefmt[], ...)
{
	va_list args;
	struct task_struct *p = malloc(sizeof(*p));
	int ret;

	memset(p, 0, sizeof(*p));

	va_start(args, namefmt);
	vsnprintf(p->comm, sizeof(p->comm), namefmt, args);
	va_end(args);

	p->flags	|= PF_KTHREAD;
	p->thread_fn	= thread_fn;
	p->thread_data	= thread_data;
	p->state	= TASK_UNINTERRUPTIBLE;
	p->signal	= &p->_signal;
	atomic_set(&p->usage, 1);
	init_completion(&p->exited);
	init_rwsem(&p->_signal.exec_update_lock);

	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setstacksize(&attr, 32 << 10);

	for (unsigned i = 0; i < 10; i++) {
		ret = pthread_create(&p->thread, &attr, kthread_start_fn, p);
		if (!ret)
			break;

		run_shrinkers(GFP_KERNEL, true);
	}
	if (ret)
		return ERR_PTR(-ret);
	pthread_setname_np(p->thread, p->comm);
	return p;
}

/**
 * kthread_should_stop - should this kthread return now?
 *
 * When someone calls kthread_stop() on your kthread, it will be woken
 * and this will return true.  You should then return, and your return
 * value will be passed through to kthread_stop().
 */
bool kthread_should_stop(void)
{
	return test_bit(KTHREAD_SHOULD_STOP, &current->kthread_flags);
}

bool kthread_freezable_should_stop(bool *was_frozen)
{
	return test_bit(KTHREAD_SHOULD_STOP, &current->kthread_flags);
}

/**
 * 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 *p)
{
	get_task_struct(p);

	set_bit(KTHREAD_SHOULD_STOP, &p->kthread_flags);
	wake_up_process(p);
	wait_for_completion(&p->exited);

	put_task_struct(p);

	return 0;
}