// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2021 Oracle Corporation */ #include #include #include #include #include enum vhost_task_flags { VHOST_TASK_FLAGS_STOP, }; static int vhost_task_fn(void *data) { struct vhost_task *vtsk = data; int ret; ret = vtsk->fn(vtsk->data); complete(&vtsk->exited); do_exit(ret); } /** * vhost_task_stop - stop a vhost_task * @vtsk: vhost_task to stop * * Callers must call vhost_task_should_stop and return from their worker * function when it returns true; */ void vhost_task_stop(struct vhost_task *vtsk) { pid_t pid = vtsk->task->pid; set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags); wake_up_process(vtsk->task); /* * Make sure vhost_task_fn is no longer accessing the vhost_task before * freeing it below. If userspace crashed or exited without closing, * then the vhost_task->task could already be marked dead so * kernel_wait will return early. */ wait_for_completion(&vtsk->exited); /* * If we are just closing/removing a device and the parent process is * not exiting then reap the task. */ kernel_wait4(pid, NULL, __WCLONE, NULL); kfree(vtsk); } EXPORT_SYMBOL_GPL(vhost_task_stop); /** * vhost_task_should_stop - should the vhost task return from the work function * @vtsk: vhost_task to stop */ bool vhost_task_should_stop(struct vhost_task *vtsk) { return test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags); } EXPORT_SYMBOL_GPL(vhost_task_should_stop); /** * vhost_task_create - create a copy of a process to be used by the kernel * @fn: thread stack * @arg: data to be passed to fn * @name: the thread's name * * This returns a specialized task for use by the vhost layer or NULL on * failure. The returned task is inactive, and the caller must fire it up * through vhost_task_start(). */ struct vhost_task *vhost_task_create(int (*fn)(void *), void *arg, const char *name) { struct kernel_clone_args args = { .flags = CLONE_FS | CLONE_UNTRACED | CLONE_VM, .exit_signal = 0, .fn = vhost_task_fn, .name = name, .user_worker = 1, .no_files = 1, .ignore_signals = 1, }; struct vhost_task *vtsk; struct task_struct *tsk; vtsk = kzalloc(sizeof(*vtsk), GFP_KERNEL); if (!vtsk) return NULL; init_completion(&vtsk->exited); vtsk->data = arg; vtsk->fn = fn; args.fn_arg = vtsk; tsk = copy_process(NULL, 0, NUMA_NO_NODE, &args); if (IS_ERR(tsk)) { kfree(vtsk); return NULL; } vtsk->task = tsk; return vtsk; } EXPORT_SYMBOL_GPL(vhost_task_create); /** * vhost_task_start - start a vhost_task created with vhost_task_create * @vtsk: vhost_task to wake up */ void vhost_task_start(struct vhost_task *vtsk) { wake_up_new_task(vtsk->task); } EXPORT_SYMBOL_GPL(vhost_task_start);