LoongArch: entry: Migrate ret_from_fork() to C

LoongArch is the only architecture that calls syscall_exit_to_user_mode()
from assembly.

Move the call into C so that this function can be inlined across all
architectures.

Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lore.kernel.org/all/20250320-riscv_optimize_entry-v6-3-63e187e26041@rivosinc.com
This commit is contained in:
Charlie Jenkins 2025-03-20 10:29:23 -07:00 committed by Thomas Gleixner
parent 5b3d6103b3
commit 7ace1602ab
3 changed files with 45 additions and 18 deletions

View file

@ -12,3 +12,11 @@ __int128_t __ashlti3(__int128_t a, int b);
__int128_t __ashrti3(__int128_t a, int b); __int128_t __ashrti3(__int128_t a, int b);
__int128_t __lshrti3(__int128_t a, int b); __int128_t __lshrti3(__int128_t a, int b);
#endif #endif
asmlinkage void noinstr __no_stack_protector ret_from_fork(struct task_struct *prev,
struct pt_regs *regs);
asmlinkage void noinstr __no_stack_protector ret_from_kernel_thread(struct task_struct *prev,
struct pt_regs *regs,
int (*fn)(void *),
void *fn_arg);

View file

@ -77,24 +77,22 @@ SYM_CODE_START(handle_syscall)
SYM_CODE_END(handle_syscall) SYM_CODE_END(handle_syscall)
_ASM_NOKPROBE(handle_syscall) _ASM_NOKPROBE(handle_syscall)
SYM_CODE_START(ret_from_fork) SYM_CODE_START(ret_from_fork_asm)
UNWIND_HINT_REGS UNWIND_HINT_REGS
bl schedule_tail # a0 = struct task_struct *prev move a1, sp
move a0, sp bl ret_from_fork
bl syscall_exit_to_user_mode
RESTORE_STATIC RESTORE_STATIC
RESTORE_SOME RESTORE_SOME
RESTORE_SP_AND_RET RESTORE_SP_AND_RET
SYM_CODE_END(ret_from_fork) SYM_CODE_END(ret_from_fork_asm)
SYM_CODE_START(ret_from_kernel_thread) SYM_CODE_START(ret_from_kernel_thread_asm)
UNWIND_HINT_REGS UNWIND_HINT_REGS
bl schedule_tail # a0 = struct task_struct *prev move a1, sp
move a0, s1 move a2, s0
jirl ra, s0, 0 move a3, s1
move a0, sp bl ret_from_kernel_thread
bl syscall_exit_to_user_mode
RESTORE_STATIC RESTORE_STATIC
RESTORE_SOME RESTORE_SOME
RESTORE_SP_AND_RET RESTORE_SP_AND_RET
SYM_CODE_END(ret_from_kernel_thread) SYM_CODE_END(ret_from_kernel_thread_asm)

View file

@ -14,6 +14,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/entry-common.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/sched/task.h> #include <linux/sched/task.h>
@ -33,6 +34,7 @@
#include <linux/prctl.h> #include <linux/prctl.h>
#include <linux/nmi.h> #include <linux/nmi.h>
#include <asm/asm-prototypes.h>
#include <asm/asm.h> #include <asm/asm.h>
#include <asm/bootinfo.h> #include <asm/bootinfo.h>
#include <asm/cpu.h> #include <asm/cpu.h>
@ -47,6 +49,7 @@
#include <asm/pgtable.h> #include <asm/pgtable.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/reg.h> #include <asm/reg.h>
#include <asm/switch_to.h>
#include <asm/unwind.h> #include <asm/unwind.h>
#include <asm/vdso.h> #include <asm/vdso.h>
@ -63,8 +66,9 @@ EXPORT_SYMBOL(__stack_chk_guard);
unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE; unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
EXPORT_SYMBOL(boot_option_idle_override); EXPORT_SYMBOL(boot_option_idle_override);
asmlinkage void ret_from_fork(void); asmlinkage void restore_and_ret(void);
asmlinkage void ret_from_kernel_thread(void); asmlinkage void ret_from_fork_asm(void);
asmlinkage void ret_from_kernel_thread_asm(void);
void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp) void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
{ {
@ -138,6 +142,23 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
return 0; return 0;
} }
asmlinkage void noinstr __no_stack_protector ret_from_fork(struct task_struct *prev,
struct pt_regs *regs)
{
schedule_tail(prev);
syscall_exit_to_user_mode(regs);
}
asmlinkage void noinstr __no_stack_protector ret_from_kernel_thread(struct task_struct *prev,
struct pt_regs *regs,
int (*fn)(void *),
void *fn_arg)
{
schedule_tail(prev);
fn(fn_arg);
syscall_exit_to_user_mode(regs);
}
/* /*
* Copy architecture-specific thread state * Copy architecture-specific thread state
*/ */
@ -165,8 +186,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
p->thread.reg03 = childksp; p->thread.reg03 = childksp;
p->thread.reg23 = (unsigned long)args->fn; p->thread.reg23 = (unsigned long)args->fn;
p->thread.reg24 = (unsigned long)args->fn_arg; p->thread.reg24 = (unsigned long)args->fn_arg;
p->thread.reg01 = (unsigned long)ret_from_kernel_thread; p->thread.reg01 = (unsigned long)ret_from_kernel_thread_asm;
p->thread.sched_ra = (unsigned long)ret_from_kernel_thread; p->thread.sched_ra = (unsigned long)ret_from_kernel_thread_asm;
memset(childregs, 0, sizeof(struct pt_regs)); memset(childregs, 0, sizeof(struct pt_regs));
childregs->csr_euen = p->thread.csr_euen; childregs->csr_euen = p->thread.csr_euen;
childregs->csr_crmd = p->thread.csr_crmd; childregs->csr_crmd = p->thread.csr_crmd;
@ -182,8 +203,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
childregs->regs[3] = usp; childregs->regs[3] = usp;
p->thread.reg03 = (unsigned long) childregs; p->thread.reg03 = (unsigned long) childregs;
p->thread.reg01 = (unsigned long) ret_from_fork; p->thread.reg01 = (unsigned long) ret_from_fork_asm;
p->thread.sched_ra = (unsigned long) ret_from_fork; p->thread.sched_ra = (unsigned long) ret_from_fork_asm;
/* /*
* New tasks lose permission to use the fpu. This accelerates context * New tasks lose permission to use the fpu. This accelerates context