Merge tag 'backlight-next-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/lee...
[linux-block.git] / kernel / vhost_task.c
CommitLineData
e297cd54
MC
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
15static int vhost_task_fn(void *data)
16{
17 struct vhost_task *vtsk = data;
18 int ret;
19
20 ret = vtsk->fn(vtsk->data);
21 complete(&vtsk->exited);
22 do_exit(ret);
23}
24
25/**
26 * vhost_task_stop - stop a vhost_task
27 * @vtsk: vhost_task to stop
28 *
29 * Callers must call vhost_task_should_stop and return from their worker
30 * function when it returns true;
31 */
32void vhost_task_stop(struct vhost_task *vtsk)
33{
34 pid_t pid = vtsk->task->pid;
35
36 set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
37 wake_up_process(vtsk->task);
38 /*
39 * Make sure vhost_task_fn is no longer accessing the vhost_task before
40 * freeing it below. If userspace crashed or exited without closing,
41 * then the vhost_task->task could already be marked dead so
42 * kernel_wait will return early.
43 */
44 wait_for_completion(&vtsk->exited);
45 /*
46 * If we are just closing/removing a device and the parent process is
47 * not exiting then reap the task.
48 */
49 kernel_wait4(pid, NULL, __WCLONE, NULL);
50 kfree(vtsk);
51}
52EXPORT_SYMBOL_GPL(vhost_task_stop);
53
54/**
55 * vhost_task_should_stop - should the vhost task return from the work function
56 * @vtsk: vhost_task to stop
57 */
58bool vhost_task_should_stop(struct vhost_task *vtsk)
59{
60 return test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
61}
62EXPORT_SYMBOL_GPL(vhost_task_should_stop);
63
64/**
65 * vhost_task_create - create a copy of a process to be used by the kernel
66 * @fn: thread stack
67 * @arg: data to be passed to fn
68 * @name: the thread's name
69 *
70 * This returns a specialized task for use by the vhost layer or NULL on
71 * failure. The returned task is inactive, and the caller must fire it up
72 * through vhost_task_start().
73 */
74struct vhost_task *vhost_task_create(int (*fn)(void *), void *arg,
75 const char *name)
76{
77 struct kernel_clone_args args = {
78 .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM,
79 .exit_signal = 0,
80 .fn = vhost_task_fn,
81 .name = name,
82 .user_worker = 1,
83 .no_files = 1,
84 .ignore_signals = 1,
85 };
86 struct vhost_task *vtsk;
87 struct task_struct *tsk;
88
89 vtsk = kzalloc(sizeof(*vtsk), GFP_KERNEL);
90 if (!vtsk)
91 return NULL;
92 init_completion(&vtsk->exited);
93 vtsk->data = arg;
94 vtsk->fn = fn;
95
96 args.fn_arg = vtsk;
97
98 tsk = copy_process(NULL, 0, NUMA_NO_NODE, &args);
99 if (IS_ERR(tsk)) {
100 kfree(vtsk);
101 return NULL;
102 }
103
104 vtsk->task = tsk;
105 return vtsk;
106}
107EXPORT_SYMBOL_GPL(vhost_task_create);
108
109/**
110 * vhost_task_start - start a vhost_task created with vhost_task_create
111 * @vtsk: vhost_task to wake up
112 */
113void vhost_task_start(struct vhost_task *vtsk)
114{
115 wake_up_new_task(vtsk->task);
116}
117EXPORT_SYMBOL_GPL(vhost_task_start);