mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	 32d89a405a
			
		
	
	
		32d89a405a
		
	
	
	
	
		
			
			cocci warning: ./kernel/vhost_task.c:148:9-16: WARNING: ERR_CAST can be used with tsk Use ERR_CAST inlined function instead of ERR_PTR(PTR_ERR(...)). Signed-off-by: Pei Xiao <xiaopei01@kylinos.cn> Message-Id: <1a8499a5da53e4f72cf21aca044ae4b26db8b2ad.1749020055.git.xiaopei01@kylinos.cn> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
		
			
				
	
	
		
			164 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
	
		
			3.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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,
 | |
| 	VHOST_TASK_FLAGS_KILLED,
 | |
| };
 | |
| 
 | |
| struct vhost_task {
 | |
| 	bool (*fn)(void *data);
 | |
| 	void (*handle_sigkill)(void *data);
 | |
| 	void *data;
 | |
| 	struct completion exited;
 | |
| 	unsigned long flags;
 | |
| 	struct task_struct *task;
 | |
| 	/* serialize SIGKILL and vhost_task_stop calls */
 | |
| 	struct mutex exit_mutex;
 | |
| };
 | |
| 
 | |
| static int vhost_task_fn(void *data)
 | |
| {
 | |
| 	struct vhost_task *vtsk = data;
 | |
| 
 | |
| 	for (;;) {
 | |
| 		bool did_work;
 | |
| 
 | |
| 		if (signal_pending(current)) {
 | |
| 			struct ksignal ksig;
 | |
| 
 | |
| 			if (get_signal(&ksig))
 | |
| 				break;
 | |
| 		}
 | |
| 
 | |
| 		/* mb paired w/ vhost_task_stop */
 | |
| 		set_current_state(TASK_INTERRUPTIBLE);
 | |
| 
 | |
| 		if (test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
 | |
| 			__set_current_state(TASK_RUNNING);
 | |
| 			break;
 | |
| 		}
 | |
| 
 | |
| 		did_work = vtsk->fn(vtsk->data);
 | |
| 		if (!did_work)
 | |
| 			schedule();
 | |
| 	}
 | |
| 
 | |
| 	mutex_lock(&vtsk->exit_mutex);
 | |
| 	/*
 | |
| 	 * If a vhost_task_stop and SIGKILL race, we can ignore the SIGKILL.
 | |
| 	 * When the vhost layer has called vhost_task_stop it's already stopped
 | |
| 	 * new work and flushed.
 | |
| 	 */
 | |
| 	if (!test_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags)) {
 | |
| 		set_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags);
 | |
| 		vtsk->handle_sigkill(vtsk->data);
 | |
| 	}
 | |
| 	mutex_unlock(&vtsk->exit_mutex);
 | |
| 	complete(&vtsk->exited);
 | |
| 
 | |
| 	do_exit(0);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * vhost_task_wake - wakeup the vhost_task
 | |
|  * @vtsk: vhost_task to wake
 | |
|  *
 | |
|  * wake up the vhost_task worker thread
 | |
|  */
 | |
| void vhost_task_wake(struct vhost_task *vtsk)
 | |
| {
 | |
| 	wake_up_process(vtsk->task);
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(vhost_task_wake);
 | |
| 
 | |
| /**
 | |
|  * vhost_task_stop - stop a vhost_task
 | |
|  * @vtsk: vhost_task to stop
 | |
|  *
 | |
|  * vhost_task_fn ensures the worker thread exits after
 | |
|  * VHOST_TASK_FLAGS_STOP becomes true.
 | |
|  */
 | |
| void vhost_task_stop(struct vhost_task *vtsk)
 | |
| {
 | |
| 	mutex_lock(&vtsk->exit_mutex);
 | |
| 	if (!test_bit(VHOST_TASK_FLAGS_KILLED, &vtsk->flags)) {
 | |
| 		set_bit(VHOST_TASK_FLAGS_STOP, &vtsk->flags);
 | |
| 		vhost_task_wake(vtsk);
 | |
| 	}
 | |
| 	mutex_unlock(&vtsk->exit_mutex);
 | |
| 
 | |
| 	/*
 | |
| 	 * Make sure vhost_task_fn is no longer accessing the vhost_task before
 | |
| 	 * freeing it below.
 | |
| 	 */
 | |
| 	wait_for_completion(&vtsk->exited);
 | |
| 	kfree(vtsk);
 | |
| }
 | |
| EXPORT_SYMBOL_GPL(vhost_task_stop);
 | |
| 
 | |
| /**
 | |
|  * vhost_task_create - create a copy of a task to be used by the kernel
 | |
|  * @fn: vhost worker function
 | |
|  * @handle_sigkill: vhost function to handle when we are killed
 | |
|  * @arg: data to be passed to fn and handled_kill
 | |
|  * @name: the thread's name
 | |
|  *
 | |
|  * This returns a specialized task for use by the vhost layer or ERR_PTR() on
 | |
|  * failure. The returned task is inactive, and the caller must fire it up
 | |
|  * through vhost_task_start().
 | |
|  */
 | |
| struct vhost_task *vhost_task_create(bool (*fn)(void *),
 | |
| 				     void (*handle_sigkill)(void *), void *arg,
 | |
| 				     const char *name)
 | |
| {
 | |
| 	struct kernel_clone_args args = {
 | |
| 		.flags		= CLONE_FS | CLONE_UNTRACED | CLONE_VM |
 | |
| 				  CLONE_THREAD | CLONE_SIGHAND,
 | |
| 		.exit_signal	= 0,
 | |
| 		.fn		= vhost_task_fn,
 | |
| 		.name		= name,
 | |
| 		.user_worker	= 1,
 | |
| 		.no_files	= 1,
 | |
| 	};
 | |
| 	struct vhost_task *vtsk;
 | |
| 	struct task_struct *tsk;
 | |
| 
 | |
| 	vtsk = kzalloc(sizeof(*vtsk), GFP_KERNEL);
 | |
| 	if (!vtsk)
 | |
| 		return ERR_PTR(-ENOMEM);
 | |
| 	init_completion(&vtsk->exited);
 | |
| 	mutex_init(&vtsk->exit_mutex);
 | |
| 	vtsk->data = arg;
 | |
| 	vtsk->fn = fn;
 | |
| 	vtsk->handle_sigkill = handle_sigkill;
 | |
| 
 | |
| 	args.fn_arg = vtsk;
 | |
| 
 | |
| 	tsk = copy_process(NULL, 0, NUMA_NO_NODE, &args);
 | |
| 	if (IS_ERR(tsk)) {
 | |
| 		kfree(vtsk);
 | |
| 		return ERR_CAST(tsk);
 | |
| 	}
 | |
| 
 | |
| 	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);
 |