xref: /openbmc/linux/kernel/vhost_task.c (revision 9ad685db)
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 
11 enum vhost_task_flags {
12 	VHOST_TASK_FLAGS_STOP,
13 };
14 
15 static 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  */
32 void 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 }
52 EXPORT_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  */
58 bool vhost_task_should_stop(struct vhost_task *vtsk)
59 {
60 	return test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
61 }
62 EXPORT_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  */
74 struct 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 }
107 EXPORT_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  */
113 void vhost_task_start(struct vhost_task *vtsk)
114 {
115 	wake_up_new_task(vtsk->task);
116 }
117 EXPORT_SYMBOL_GPL(vhost_task_start);
118