117 lines · 2.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* * Copyright (C) 2021 Oracle Corporation */#include <linux/slab.h>#include <linux/completion.h>#include <linux/sched/task.h>#include <linux/sched/vhost_task.h>#include <linux/sched/signal.h> 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);