blob: 8800f5acc00717b956ea0915c976e647a010aed7 [file] [log] [blame]
Mike Christiee297cd52023-03-10 16:03:30 -06001// 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,
Mike Christiedb5247d2024-03-15 19:47:06 -050013 VHOST_TASK_FLAGS_KILLED,
Mike Christiee297cd52023-03-10 16:03:30 -060014};
15
Mike Christief9010db2023-06-01 13:32:32 -050016struct vhost_task {
17 bool (*fn)(void *data);
Mike Christiedb5247d2024-03-15 19:47:06 -050018 void (*handle_sigkill)(void *data);
Mike Christief9010db2023-06-01 13:32:32 -050019 void *data;
20 struct completion exited;
21 unsigned long flags;
22 struct task_struct *task;
Mike Christiedb5247d2024-03-15 19:47:06 -050023 /* serialize SIGKILL and vhost_task_stop calls */
24 struct mutex exit_mutex;
Mike Christief9010db2023-06-01 13:32:32 -050025};
26
Mike Christiee297cd52023-03-10 16:03:30 -060027static int vhost_task_fn(void *data)
28{
29 struct vhost_task *vtsk = data;
Mike Christiee297cd52023-03-10 16:03:30 -060030
Mike Christief9010db2023-06-01 13:32:32 -050031 for (;;) {
32 bool did_work;
33
Mike Christiedb5247d2024-03-15 19:47:06 -050034 if (signal_pending(current)) {
Mike Christief9010db2023-06-01 13:32:32 -050035 struct ksignal ksig;
Mike Christiedb5247d2024-03-15 19:47:06 -050036
37 if (get_signal(&ksig))
38 break;
Mike Christief9010db2023-06-01 13:32:32 -050039 }
40
Mike Christie4b13cbe2023-06-07 14:23:38 -050041 /* mb paired w/ vhost_task_stop */
42 set_current_state(TASK_INTERRUPTIBLE);
43
44 if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
45 __set_current_state(TASK_RUNNING);
46 break;
Mike Christief9010db2023-06-01 13:32:32 -050047 }
Mike Christie4b13cbe2023-06-07 14:23:38 -050048
49 did_work = vtsk->fn(vtsk->data);
50 if (!did_work)
51 schedule();
Mike Christief9010db2023-06-01 13:32:32 -050052 }
53
Mike Christiedb5247d2024-03-15 19:47:06 -050054 mutex_lock(&vtsk->exit_mutex);
55 /*
56 * If a vhost_task_stop and SIGKILL race, we can ignore the SIGKILL.
57 * When the vhost layer has called vhost_task_stop it's already stopped
58 * new work and flushed.
59 */
60 if (!test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
61 set_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags);
62 vtsk->handle_sigkill(vtsk->data);
63 }
64 mutex_unlock(&vtsk->exit_mutex);
Mike Christiee297cd52023-03-10 16:03:30 -060065 complete(&vtsk->exited);
Mike Christiedb5247d2024-03-15 19:47:06 -050066
Mike Christief9010db2023-06-01 13:32:32 -050067 do_exit(0);
Mike Christiee297cd52023-03-10 16:03:30 -060068}
69
70/**
Mike Christief9010db2023-06-01 13:32:32 -050071 * vhost_task_wake - wakeup the vhost_task
72 * @vtsk: vhost_task to wake
73 *
74 * wake up the vhost_task worker thread
75 */
76void vhost_task_wake(struct vhost_task *vtsk)
77{
78 wake_up_process(vtsk->task);
79}
80EXPORT_SYMBOL_GPL(vhost_task_wake);
81
82/**
Mike Christiee297cd52023-03-10 16:03:30 -060083 * vhost_task_stop - stop a vhost_task
84 * @vtsk: vhost_task to stop
85 *
Mike Christief9010db2023-06-01 13:32:32 -050086 * vhost_task_fn ensures the worker thread exits after
Mike Christiedb5247d2024-03-15 19:47:06 -050087 * VHOST_TASK_FLAGS_STOP becomes true.
Mike Christiee297cd52023-03-10 16:03:30 -060088 */
89void vhost_task_stop(struct vhost_task *vtsk)
90{
Mike Christiedb5247d2024-03-15 19:47:06 -050091 mutex_lock(&vtsk->exit_mutex);
92 if (!test_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags)) {
93 set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
94 vhost_task_wake(vtsk);
95 }
96 mutex_unlock(&vtsk->exit_mutex);
97
Mike Christiee297cd52023-03-10 16:03:30 -060098 /*
99 * Make sure vhost_task_fn is no longer accessing the vhost_task before
Mike Christief9010db2023-06-01 13:32:32 -0500100 * freeing it below.
Mike Christiee297cd52023-03-10 16:03:30 -0600101 */
102 wait_for_completion(&vtsk->exited);
Mike Christiee297cd52023-03-10 16:03:30 -0600103 kfree(vtsk);
104}
105EXPORT_SYMBOL_GPL(vhost_task_stop);
106
107/**
Mike Christief9010db2023-06-01 13:32:32 -0500108 * vhost_task_create - create a copy of a task to be used by the kernel
109 * @fn: vhost worker function
Mike Christiedb5247d2024-03-15 19:47:06 -0500110 * @handle_sigkill: vhost function to handle when we are killed
111 * @arg: data to be passed to fn and handled_kill
Mike Christiee297cd52023-03-10 16:03:30 -0600112 * @name: the thread's name
113 *
114 * This returns a specialized task for use by the vhost layer or NULL on
115 * failure. The returned task is inactive, and the caller must fire it up
116 * through vhost_task_start().
117 */
Mike Christiedb5247d2024-03-15 19:47:06 -0500118struct vhost_task *vhost_task_create(bool (*fn)(void *),
119 void (*handle_sigkill)(void *), void *arg,
Mike Christiee297cd52023-03-10 16:03:30 -0600120 const char *name)
121{
122 struct kernel_clone_args args = {
Mike Christief9010db2023-06-01 13:32:32 -0500123 .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM |
124 CLONE_THREAD | CLONE_SIGHAND,
Mike Christiee297cd52023-03-10 16:03:30 -0600125 .exit_signal = 0,
126 .fn = vhost_task_fn,
127 .name = name,
128 .user_worker = 1,
129 .no_files = 1,
Mike Christiee297cd52023-03-10 16:03:30 -0600130 };
131 struct vhost_task *vtsk;
132 struct task_struct *tsk;
133
134 vtsk = kzalloc(sizeof(*vtsk), GFP_KERNEL);
135 if (!vtsk)
136 return NULL;
137 init_completion(&vtsk->exited);
Mike Christiedb5247d2024-03-15 19:47:06 -0500138 mutex_init(&vtsk->exit_mutex);
Mike Christiee297cd52023-03-10 16:03:30 -0600139 vtsk->data = arg;
140 vtsk->fn = fn;
Mike Christiedb5247d2024-03-15 19:47:06 -0500141 vtsk->handle_sigkill = handle_sigkill;
Mike Christiee297cd52023-03-10 16:03:30 -0600142
143 args.fn_arg = vtsk;
144
145 tsk = copy_process(NULL, 0, NUMA_NO_NODE, &args);
146 if (IS_ERR(tsk)) {
147 kfree(vtsk);
148 return NULL;
149 }
150
151 vtsk->task = tsk;
152 return vtsk;
153}
154EXPORT_SYMBOL_GPL(vhost_task_create);
155
156/**
157 * vhost_task_start - start a vhost_task created with vhost_task_create
158 * @vtsk: vhost_task to wake up
159 */
160void vhost_task_start(struct vhost_task *vtsk)
161{
162 wake_up_new_task(vtsk->task);
163}
164EXPORT_SYMBOL_GPL(vhost_task_start);