powerpc: use switch frame for ret_from_kernel_thread parameters
authorNicholas Piggin <npiggin@gmail.com>
Sat, 25 Mar 2023 12:28:59 +0000 (22:28 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 11 Apr 2023 13:13:32 +0000 (23:13 +1000)
The kernel thread path in copy_thread creates a user interrupt frame on
stack and stores the function and arg parameters there, and
ret_from_kernel_thread loads them. This is a slightly confusing way to
overload that frame. Non-volatile registers are loaded from the switch
frame, so the parameters can be stored there. The user interrupt frame
is now only used by user threads when they return to user.

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://msgid.link/20230325122904.2375060-4-npiggin@gmail.com
arch/powerpc/kernel/entry_32.S
arch/powerpc/kernel/interrupt_64.S
arch/powerpc/kernel/process.c

index 755408c63be8c37ad045f22e3f8f0d9104599b07..c3fdb3081d3dfe501560e8967cc1d033aff1e889 100644 (file)
@@ -188,7 +188,6 @@ ret_from_fork:
 
        .globl  ret_from_kernel_thread
 ret_from_kernel_thread:
-       REST_NVGPRS(r1)
        bl      schedule_tail
        mtctr   r14
        mr      r3,r15
index fccc34489add8456bb060b8c0a476acfc3428a88..d60e7e7564df10ef7e2518bd5807b3554f042f2a 100644 (file)
@@ -741,7 +741,6 @@ _GLOBAL(ret_from_fork)
 
 _GLOBAL(ret_from_kernel_thread)
        bl      schedule_tail
-       REST_NVGPRS(r1)
        mtctr   r14
        mr      r3,r15
 #ifdef CONFIG_PPC64_ELF_ABI_V2
index 18f6971121930b6d9aa87ceaa15fba47d2199721..3b34bd9a6dffe7c48b8523c49d41a9da226ac2d5 100644 (file)
@@ -1765,14 +1765,10 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
                ((unsigned long *)sp)[0] = 0;
                memset(childregs, 0, sizeof(struct pt_regs));
                childregs->gpr[1] = sp + STACK_USER_INT_FRAME_SIZE;
-               /* function */
-               if (args->fn)
-                       childregs->gpr[14] = ppc_function_entry((void *)args->fn);
 #ifdef CONFIG_PPC64
                clear_tsk_thread_flag(p, TIF_32BIT);
                childregs->softe = IRQS_ENABLED;
 #endif
-               childregs->gpr[15] = (unsigned long)args->fn_arg;
                p->thread.regs = NULL;  /* no user register state */
                ti->flags |= _TIF_RESTOREALL;
                f = ret_from_kernel_thread;
@@ -1811,6 +1807,15 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
        ((unsigned long *)sp)[0] = sp + STACK_SWITCH_FRAME_SIZE;
        kregs = (struct pt_regs *)(sp + STACK_SWITCH_FRAME_REGS);
        kregs->nip = ppc_function_entry(f);
+       if (unlikely(args->fn)) {
+               /*
+                * Put kthread fn, arg parameters in non-volatile GPRs in the
+                * switch frame so they are loaded by _switch before it returns
+                * to ret_from_kernel_thread.
+                */
+               kregs->gpr[14] = ppc_function_entry((void *)args->fn);
+               kregs->gpr[15] = (unsigned long)args->fn_arg;
+       }
        p->thread.ksp = sp;
 
 #ifdef CONFIG_HAVE_HW_BREAKPOINT