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