powerpc: differentiate kthread from user kernel thread start
authorNicholas Piggin <npiggin@gmail.com>
Sat, 25 Mar 2023 12:29:02 +0000 (22:29 +1000)
committerMichael Ellerman <mpe@ellerman.id.au>
Tue, 11 Apr 2023 13:13:33 +0000 (23:13 +1000)
Kernel created user threads start similarly to kernel threads in that
they call a kernel function after first returning from _switch, so
they share ret_from_kernel_thread for this. Kernel threads never return
from that function though, whereas user threads often do (although some
don't, e.g., IO threads).

Split these startup functions in two, and catch kernel threads that
improperly return from their function. This is intended to make the
complicated code a little bit easier to understand.

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

index c3fdb3081d3dfe501560e8967cc1d033aff1e889..47f0dd9a45adc28c0ce249b05e5b7783fe2cca7d 100644 (file)
@@ -186,8 +186,8 @@ ret_from_fork:
        li      r3,0    /* fork() return value */
        b       ret_from_syscall
 
-       .globl  ret_from_kernel_thread
-ret_from_kernel_thread:
+       .globl  ret_from_kernel_user_thread
+ret_from_kernel_user_thread:
        bl      schedule_tail
        mtctr   r14
        mr      r3,r15
@@ -196,6 +196,22 @@ ret_from_kernel_thread:
        li      r3,0
        b       ret_from_syscall
 
+       .globl  start_kernel_thread
+start_kernel_thread:
+       bl      schedule_tail
+       mtctr   r14
+       mr      r3,r15
+       PPC440EP_ERR42
+       bctrl
+       /*
+        * This must not return. We actually want to BUG here, not WARN,
+        * because BUG will exit the process which is what the kernel thread
+        * should have done, which may give some hope of continuing.
+        */
+100:   trap
+       EMIT_BUG_ENTRY 100b,__FILE__,__LINE__,0
+
+
 /*
  * This routine switches between two different tasks.  The process
  * state of one is saved on its kernel stack.  Then the state
index bac1f89501acdfce3bcb6ff20fc3c08ea43a28d3..a44c8aab63ec92c8e16358f4d12b036d3558b173 100644 (file)
@@ -739,7 +739,7 @@ _GLOBAL(ret_from_fork)
        li      r3,0    /* fork() return value */
        b       .Lsyscall_exit
 
-_GLOBAL(ret_from_kernel_thread)
+_GLOBAL(ret_from_kernel_user_thread)
        bl      schedule_tail
        mtctr   r14
        mr      r3,r15
@@ -749,3 +749,19 @@ _GLOBAL(ret_from_kernel_thread)
        bctrl
        li      r3,0
        b       .Lsyscall_exit
+
+_GLOBAL(start_kernel_thread)
+       bl      schedule_tail
+       mtctr   r14
+       mr      r3,r15
+#ifdef CONFIG_PPC64_ELF_ABI_V2
+       mr      r12,r14
+#endif
+       bctrl
+       /*
+        * This must not return. We actually want to BUG here, not WARN,
+        * because BUG will exit the process which is what the kernel thread
+        * should have done, which may give some hope of continuing.
+        */
+100:   trap
+       EMIT_BUG_ENTRY 100b,__FILE__,__LINE__,0
index 88898ca7ab0b11d86ffac85689ed35b6cfde58d2..14fe4702a098b7749774de5f5a4514898855b3b8 100644 (file)
@@ -1741,7 +1741,8 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
        struct pt_regs *kregs; /* Switch frame regs */
        extern void ret_from_fork(void);
        extern void ret_from_fork_scv(void);
-       extern void ret_from_kernel_thread(void);
+       extern void ret_from_kernel_user_thread(void);
+       extern void start_kernel_thread(void);
        void (*f)(void);
        unsigned long sp = (unsigned long)task_stack_page(p) + THREAD_SIZE;
        struct thread_info *ti = task_thread_info(p);
@@ -1758,7 +1759,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
                sp -= STACK_FRAME_MIN_SIZE;
                ((unsigned long *)sp)[0] = 0;
 
-               f = ret_from_kernel_thread;
+               f = start_kernel_thread;
                p->thread.regs = NULL;  /* no user register state */
                clear_tsk_compat_task(p);
        } else {
@@ -1784,7 +1785,7 @@ int copy_thread(struct task_struct *p, const struct kernel_clone_args *args)
                        childregs->softe = IRQS_ENABLED;
 #endif
                        ti->flags |= _TIF_RESTOREALL;
-                       f = ret_from_kernel_thread;
+                       f = ret_from_kernel_user_thread;
                } else {
                        struct pt_regs *regs = current_pt_regs();
                        unsigned long clone_flags = args->flags;