1e297cd54SMike Christie // SPDX-License-Identifier: GPL-2.0-only 2e297cd54SMike Christie /* 3e297cd54SMike Christie * Copyright (C) 2021 Oracle Corporation 4e297cd54SMike Christie */ 5e297cd54SMike Christie #include <linux/slab.h> 6e297cd54SMike Christie #include <linux/completion.h> 7e297cd54SMike Christie #include <linux/sched/task.h> 8e297cd54SMike Christie #include <linux/sched/vhost_task.h> 9e297cd54SMike Christie #include <linux/sched/signal.h> 10e297cd54SMike Christie 11e297cd54SMike Christie enum vhost_task_flags { 12e297cd54SMike Christie VHOST_TASK_FLAGS_STOP, 13*db5247d9SMike Christie VHOST_TASK_FLAGS_KILLED, 14e297cd54SMike Christie }; 15e297cd54SMike Christie 16f9010dbdSMike Christie struct vhost_task { 17f9010dbdSMike Christie bool (*fn)(void *data); 18*db5247d9SMike Christie void (*handle_sigkill)(void *data); 19f9010dbdSMike Christie void *data; 20f9010dbdSMike Christie struct completion exited; 21f9010dbdSMike Christie unsigned long flags; 22f9010dbdSMike Christie struct task_struct *task; 23*db5247d9SMike Christie /* serialize SIGKILL and vhost_task_stop calls */ 24*db5247d9SMike Christie struct mutex exit_mutex; 25f9010dbdSMike Christie }; 26f9010dbdSMike Christie 27e297cd54SMike Christie static int vhost_task_fn(void *data) 28e297cd54SMike Christie { 29e297cd54SMike Christie struct vhost_task *vtsk = data; 30e297cd54SMike Christie 31f9010dbdSMike Christie for (;;) { 32f9010dbdSMike Christie bool did_work; 33f9010dbdSMike Christie 34*db5247d9SMike Christie if (signal_pending(current)) { 35f9010dbdSMike Christie struct ksignal ksig; 36*db5247d9SMike Christie 37*db5247d9SMike Christie if (get_signal(&ksig)) 38*db5247d9SMike Christie break; 39e297cd54SMike Christie } 40e297cd54SMike Christie 414b13cbefSMike Christie /* mb paired w/ vhost_task_stop */ 42f9010dbdSMike Christie set_current_state(TASK_INTERRUPTIBLE); 434b13cbefSMike Christie 444b13cbefSMike Christie if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) { 454b13cbefSMike Christie __set_current_state(TASK_RUNNING); 464b13cbefSMike Christie break; 47f9010dbdSMike Christie } 484b13cbefSMike Christie 494b13cbefSMike Christie did_work = vtsk->fn(vtsk->data); 504b13cbefSMike Christie if (!did_work) 514b13cbefSMike Christie schedule(); 52f9010dbdSMike Christie } 53f9010dbdSMike Christie 54*db5247d9SMike Christie mutex_lock(&vtsk->exit_mutex); 55*db5247d9SMike Christie /* 56*db5247d9SMike Christie * If a vhost_task_stop and SIGKILL race, we can ignore the SIGKILL. 57*db5247d9SMike Christie * When the vhost layer has called vhost_task_stop it's already stopped 58*db5247d9SMike Christie * new work and flushed. 59*db5247d9SMike Christie */ 60*db5247d9SMike Christie if (!test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) { 61*db5247d9SMike Christie set_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags); 62*db5247d9SMike Christie vtsk->handle_sigkill(vtsk->data); 63*db5247d9SMike Christie } 64*db5247d9SMike Christie mutex_unlock(&vtsk->exit_mutex); 65f9010dbdSMike Christie complete(&vtsk->exited); 66*db5247d9SMike Christie 67f9010dbdSMike Christie do_exit(0); 68f9010dbdSMike Christie } 69f9010dbdSMike Christie 70f9010dbdSMike Christie /** 71f9010dbdSMike Christie * vhost_task_wake - wakeup the vhost_task 72f9010dbdSMike Christie * @vtsk: vhost_task to wake 73f9010dbdSMike Christie * 74f9010dbdSMike Christie * wake up the vhost_task worker thread 75f9010dbdSMike Christie */ 76f9010dbdSMike Christie void vhost_task_wake(struct vhost_task *vtsk) 77f9010dbdSMike Christie { 78f9010dbdSMike Christie wake_up_process(vtsk->task); 79f9010dbdSMike Christie } 80f9010dbdSMike Christie EXPORT_SYMBOL_GPL(vhost_task_wake); 81f9010dbdSMike Christie 82e297cd54SMike Christie /** 83e297cd54SMike Christie * vhost_task_stop - stop a vhost_task 84e297cd54SMike Christie * @vtsk: vhost_task to stop 85e297cd54SMike Christie * 86f9010dbdSMike Christie * vhost_task_fn ensures the worker thread exits after 87*db5247d9SMike Christie * VHOST_TASK_FLAGS_STOP becomes true. 88e297cd54SMike Christie */ 89e297cd54SMike Christie void vhost_task_stop(struct vhost_task *vtsk) 90e297cd54SMike Christie { 91*db5247d9SMike Christie mutex_lock(&vtsk->exit_mutex); 92*db5247d9SMike Christie if (!test_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags)) { 93e297cd54SMike Christie set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags); 94f9010dbdSMike Christie vhost_task_wake(vtsk); 95*db5247d9SMike Christie } 96*db5247d9SMike Christie mutex_unlock(&vtsk->exit_mutex); 97*db5247d9SMike Christie 98e297cd54SMike Christie /* 99e297cd54SMike Christie * Make sure vhost_task_fn is no longer accessing the vhost_task before 100f9010dbdSMike Christie * freeing it below. 101e297cd54SMike Christie */ 102e297cd54SMike Christie wait_for_completion(&vtsk->exited); 103e297cd54SMike Christie kfree(vtsk); 104e297cd54SMike Christie } 105e297cd54SMike Christie EXPORT_SYMBOL_GPL(vhost_task_stop); 106e297cd54SMike Christie 107e297cd54SMike Christie /** 108f9010dbdSMike Christie * vhost_task_create - create a copy of a task to be used by the kernel 109f9010dbdSMike Christie * @fn: vhost worker function 110*db5247d9SMike Christie * @handle_sigkill: vhost function to handle when we are killed 111*db5247d9SMike Christie * @arg: data to be passed to fn and handled_kill 112e297cd54SMike Christie * @name: the thread's name 113e297cd54SMike Christie * 114e297cd54SMike Christie * This returns a specialized task for use by the vhost layer or NULL on 115e297cd54SMike Christie * failure. The returned task is inactive, and the caller must fire it up 116e297cd54SMike Christie * through vhost_task_start(). 117e297cd54SMike Christie */ 118*db5247d9SMike Christie struct vhost_task *vhost_task_create(bool (*fn)(void *), 119*db5247d9SMike Christie void (*handle_sigkill)(void *), void *arg, 120e297cd54SMike Christie const char *name) 121e297cd54SMike Christie { 122e297cd54SMike Christie struct kernel_clone_args args = { 123f9010dbdSMike Christie .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM | 124f9010dbdSMike Christie CLONE_THREAD | CLONE_SIGHAND, 125e297cd54SMike Christie .exit_signal = 0, 126e297cd54SMike Christie .fn = vhost_task_fn, 127e297cd54SMike Christie .name = name, 128e297cd54SMike Christie .user_worker = 1, 129e297cd54SMike Christie .no_files = 1, 130e297cd54SMike Christie }; 131e297cd54SMike Christie struct vhost_task *vtsk; 132e297cd54SMike Christie struct task_struct *tsk; 133e297cd54SMike Christie 134e297cd54SMike Christie vtsk = kzalloc(sizeof(*vtsk), GFP_KERNEL); 135e297cd54SMike Christie if (!vtsk) 136e297cd54SMike Christie return NULL; 137e297cd54SMike Christie init_completion(&vtsk->exited); 138*db5247d9SMike Christie mutex_init(&vtsk->exit_mutex); 139e297cd54SMike Christie vtsk->data = arg; 140e297cd54SMike Christie vtsk->fn = fn; 141*db5247d9SMike Christie vtsk->handle_sigkill = handle_sigkill; 142e297cd54SMike Christie 143e297cd54SMike Christie args.fn_arg = vtsk; 144e297cd54SMike Christie 145e297cd54SMike Christie tsk = copy_process(NULL, 0, NUMA_NO_NODE, &args); 146e297cd54SMike Christie if (IS_ERR(tsk)) { 147e297cd54SMike Christie kfree(vtsk); 148e297cd54SMike Christie return NULL; 149e297cd54SMike Christie } 150e297cd54SMike Christie 151e297cd54SMike Christie vtsk->task = tsk; 152e297cd54SMike Christie return vtsk; 153e297cd54SMike Christie } 154e297cd54SMike Christie EXPORT_SYMBOL_GPL(vhost_task_create); 155e297cd54SMike Christie 156e297cd54SMike Christie /** 157e297cd54SMike Christie * vhost_task_start - start a vhost_task created with vhost_task_create 158e297cd54SMike Christie * @vtsk: vhost_task to wake up 159e297cd54SMike Christie */ 160e297cd54SMike Christie void vhost_task_start(struct vhost_task *vtsk) 161e297cd54SMike Christie { 162e297cd54SMike Christie wake_up_new_task(vtsk->task); 163e297cd54SMike Christie } 164e297cd54SMike Christie EXPORT_SYMBOL_GPL(vhost_task_start); 165