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