1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (C) 2021 Oracle Corporation
4 */
5#include <linux/slab.h>
6#include <linux/completion.h>
7#include <linux/sched/task.h>
8#include <linux/sched/vhost_task.h>
9#include <linux/sched/signal.h>
10
11enum vhost_task_flags {
12 VHOST_TASK_FLAGS_STOP,
13};
14
15struct vhost_task {
16 bool (*fn)(void *data);
17 void *data;
18 struct completion exited;
19 unsigned long flags;
20 struct task_struct *task;
21};
22
23static int vhost_task_fn(void *data)
24{
25 struct vhost_task *vtsk = data;
26 bool dead = false;
27
28 for (;;) {
29 bool did_work;
30
31 if (!dead && signal_pending(current)) {
32 struct ksignal ksig;
33 /*
34 * Calling get_signal will block in SIGSTOP,
35 * or clear fatal_signal_pending, but remember
36 * what was set.
37 *
38 * This thread won't actually exit until all
39 * of the file descriptors are closed, and
40 * the release function is called.
41 */
42 dead = get_signal(ksig: &ksig);
43 if (dead)
44 clear_thread_flag(TIF_SIGPENDING);
45 }
46
47 /* mb paired w/ vhost_task_stop */
48 set_current_state(TASK_INTERRUPTIBLE);
49
50 if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
51 __set_current_state(TASK_RUNNING);
52 break;
53 }
54
55 did_work = vtsk->fn(vtsk->data);
56 if (!did_work)
57 schedule();
58 }
59
60 complete(&vtsk->exited);
61 do_exit(error_code: 0);
62}
63
64/**
65 * vhost_task_wake - wakeup the vhost_task
66 * @vtsk: vhost_task to wake
67 *
68 * wake up the vhost_task worker thread
69 */
70void vhost_task_wake(struct vhost_task *vtsk)
71{
72 wake_up_process(tsk: vtsk->task);
73}
74EXPORT_SYMBOL_GPL(vhost_task_wake);
75
76/**
77 * vhost_task_stop - stop a vhost_task
78 * @vtsk: vhost_task to stop
79 *
80 * vhost_task_fn ensures the worker thread exits after
81 * VHOST_TASK_FLAGS_SOP becomes true.
82 */
83void vhost_task_stop(struct vhost_task *vtsk)
84{
85 set_bit(nr: VHOST_TASK_FLAGS_STOP, addr: &vtsk->flags);
86 vhost_task_wake(vtsk);
87 /*
88 * Make sure vhost_task_fn is no longer accessing the vhost_task before
89 * freeing it below.
90 */
91 wait_for_completion(&vtsk->exited);
92 kfree(objp: vtsk);
93}
94EXPORT_SYMBOL_GPL(vhost_task_stop);
95
96/**
97 * vhost_task_create - create a copy of a task to be used by the kernel
98 * @fn: vhost worker function
99 * @arg: data to be passed to fn
100 * @name: the thread's name
101 *
102 * This returns a specialized task for use by the vhost layer or NULL on
103 * failure. The returned task is inactive, and the caller must fire it up
104 * through vhost_task_start().
105 */
106struct vhost_task *vhost_task_create(bool (*fn)(void *), void *arg,
107 const char *name)
108{
109 struct kernel_clone_args args = {
110 .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM |
111 CLONE_THREAD | CLONE_SIGHAND,
112 .exit_signal = 0,
113 .fn = vhost_task_fn,
114 .name = name,
115 .user_worker = 1,
116 .no_files = 1,
117 };
118 struct vhost_task *vtsk;
119 struct task_struct *tsk;
120
121 vtsk = kzalloc(size: sizeof(*vtsk), GFP_KERNEL);
122 if (!vtsk)
123 return NULL;
124 init_completion(x: &vtsk->exited);
125 vtsk->data = arg;
126 vtsk->fn = fn;
127
128 args.fn_arg = vtsk;
129
130 tsk = copy_process(NULL, trace: 0, NUMA_NO_NODE, args: &args);
131 if (IS_ERR(ptr: tsk)) {
132 kfree(objp: vtsk);
133 return NULL;
134 }
135
136 vtsk->task = tsk;
137 return vtsk;
138}
139EXPORT_SYMBOL_GPL(vhost_task_create);
140
141/**
142 * vhost_task_start - start a vhost_task created with vhost_task_create
143 * @vtsk: vhost_task to wake up
144 */
145void vhost_task_start(struct vhost_task *vtsk)
146{
147 wake_up_new_task(tsk: vtsk->task);
148}
149EXPORT_SYMBOL_GPL(vhost_task_start);
150

source code of linux/kernel/vhost_task.c