Add fn and fn_arg members into struct kernel_clone_args and test for them in copy_thread (instead of testing for PF_KTHREAD | PF_IO_WORKER). This allows any task that wants to be a user space task that only runs in kernel mode to use this functionality. The code on x86 is an exception and still retains a PF_KTHREAD test because x86 unlikely everything else handles kthreads slightly differently than user space tasks that start with a function. The functions that created tasks that start with a function have been updated to set ".fn" and ".fn_arg" instead of ".stack" and ".stack_size". These functions are fork_idle(), create_io_thread(), kernel_thread(), and user_mode_thread(). Link: https://lkml.kernel.org/r/20220506141512.516114-4-ebiederm@xmission.com Signed-off-by: "Eric W. Biederman" <ebiederm@xmission.com>
		
			
				
	
	
		
			92 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			92 lines
		
	
	
		
			2.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* SPDX-License-Identifier: GPL-2.0 */
 | 
						|
#ifndef _ASM_X86_SWITCH_TO_H
 | 
						|
#define _ASM_X86_SWITCH_TO_H
 | 
						|
 | 
						|
#include <linux/sched/task_stack.h>
 | 
						|
 | 
						|
struct task_struct; /* one of the stranger aspects of C forward declarations */
 | 
						|
 | 
						|
struct task_struct *__switch_to_asm(struct task_struct *prev,
 | 
						|
				    struct task_struct *next);
 | 
						|
 | 
						|
__visible struct task_struct *__switch_to(struct task_struct *prev,
 | 
						|
					  struct task_struct *next);
 | 
						|
 | 
						|
asmlinkage void ret_from_fork(void);
 | 
						|
 | 
						|
/*
 | 
						|
 * This is the structure pointed to by thread.sp for an inactive task.  The
 | 
						|
 * order of the fields must match the code in __switch_to_asm().
 | 
						|
 */
 | 
						|
struct inactive_task_frame {
 | 
						|
#ifdef CONFIG_X86_64
 | 
						|
	unsigned long r15;
 | 
						|
	unsigned long r14;
 | 
						|
	unsigned long r13;
 | 
						|
	unsigned long r12;
 | 
						|
#else
 | 
						|
	unsigned long flags;
 | 
						|
	unsigned long si;
 | 
						|
	unsigned long di;
 | 
						|
#endif
 | 
						|
	unsigned long bx;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * These two fields must be together.  They form a stack frame header,
 | 
						|
	 * needed by get_frame_pointer().
 | 
						|
	 */
 | 
						|
	unsigned long bp;
 | 
						|
	unsigned long ret_addr;
 | 
						|
};
 | 
						|
 | 
						|
struct fork_frame {
 | 
						|
	struct inactive_task_frame frame;
 | 
						|
	struct pt_regs regs;
 | 
						|
};
 | 
						|
 | 
						|
#define switch_to(prev, next, last)					\
 | 
						|
do {									\
 | 
						|
	((last) = __switch_to_asm((prev), (next)));			\
 | 
						|
} while (0)
 | 
						|
 | 
						|
#ifdef CONFIG_X86_32
 | 
						|
static inline void refresh_sysenter_cs(struct thread_struct *thread)
 | 
						|
{
 | 
						|
	/* Only happens when SEP is enabled, no need to test "SEP"arately: */
 | 
						|
	if (unlikely(this_cpu_read(cpu_tss_rw.x86_tss.ss1) == thread->sysenter_cs))
 | 
						|
		return;
 | 
						|
 | 
						|
	this_cpu_write(cpu_tss_rw.x86_tss.ss1, thread->sysenter_cs);
 | 
						|
	wrmsr(MSR_IA32_SYSENTER_CS, thread->sysenter_cs, 0);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
/* This is used when switching tasks or entering/exiting vm86 mode. */
 | 
						|
static inline void update_task_stack(struct task_struct *task)
 | 
						|
{
 | 
						|
	/* sp0 always points to the entry trampoline stack, which is constant: */
 | 
						|
#ifdef CONFIG_X86_32
 | 
						|
	if (static_cpu_has(X86_FEATURE_XENPV))
 | 
						|
		load_sp0(task->thread.sp0);
 | 
						|
	else
 | 
						|
		this_cpu_write(cpu_tss_rw.x86_tss.sp1, task->thread.sp0);
 | 
						|
#else
 | 
						|
	/* Xen PV enters the kernel on the thread stack. */
 | 
						|
	if (static_cpu_has(X86_FEATURE_XENPV))
 | 
						|
		load_sp0(task_top_of_stack(task));
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
static inline void kthread_frame_init(struct inactive_task_frame *frame,
 | 
						|
				      int (*fun)(void *), void *arg)
 | 
						|
{
 | 
						|
	frame->bx = (unsigned long)fun;
 | 
						|
#ifdef CONFIG_X86_32
 | 
						|
	frame->di = (unsigned long)arg;
 | 
						|
#else
 | 
						|
	frame->r12 = (unsigned long)arg;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
#endif /* _ASM_X86_SWITCH_TO_H */
 |