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