arm: split ret_from_fork, simplify kernel_thread() [based on patch by rmk]
authorAl Viro <viro@zeniv.linux.org.uk>
Mon, 10 Sep 2012 01:31:07 +0000 (21:31 -0400)
committerAl Viro <viro@zeniv.linux.org.uk>
Mon, 1 Oct 2012 02:21:36 +0000 (22:21 -0400)
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
arch/arm/Kconfig
arch/arm/include/asm/processor.h
arch/arm/kernel/entry-common.S
arch/arm/kernel/process.c

index 2f88d8d..a949eec 100644 (file)
@@ -49,6 +49,7 @@ config ARM
        select GENERIC_STRNCPY_FROM_USER
        select GENERIC_STRNLEN_USER
        select DCACHE_WORD_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && !CPU_BIG_ENDIAN
+       select GENERIC_KERNEL_THREAD
        help
          The ARM series is a line of low-power-consumption RISC chip designs
          licensed by ARM Ltd and targeted at embedded applications and
index 99afa74..06e7d50 100644 (file)
@@ -85,11 +85,6 @@ unsigned long get_wchan(struct task_struct *p);
 #define cpu_relax()                    barrier()
 #endif
 
-/*
- * Create a new kernel thread
- */
-extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags);
-
 #define task_pt_regs(p) \
        ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
 
index 978eac5..9a48b7a 100644 (file)
@@ -91,6 +91,18 @@ ENTRY(ret_from_fork)
        b       ret_slow_syscall
 ENDPROC(ret_from_fork)
 
+ENTRY(ret_from_kernel_thread)
+ UNWIND(.fnstart)
+ UNWIND(.cantunwind)
+       bl      schedule_tail
+       mov     r0, r4
+       adr     lr, BSYM(1f)    @ kernel threads should not exit
+       mov     pc, r5
+1:     bl      do_exit
+       nop
+ UNWIND(.fnend)
+ENDPROC(ret_from_kernel_thread)
+
        .equ NR_syscalls,0
 #define CALL(x) .equ NR_syscalls,NR_syscalls+1
 #include "calls.S"
index 693b744..c10e439 100644 (file)
@@ -373,6 +373,7 @@ void release_thread(struct task_struct *dead_task)
 }
 
 asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
+asmlinkage void ret_from_kernel_thread(void) __asm__("ret_from_kernel_thread");
 
 int
 copy_thread(unsigned long clone_flags, unsigned long stack_start,
@@ -381,13 +382,20 @@ copy_thread(unsigned long clone_flags, unsigned long stack_start,
        struct thread_info *thread = task_thread_info(p);
        struct pt_regs *childregs = task_pt_regs(p);
 
-       *childregs = *regs;
-       childregs->ARM_r0 = 0;
-       childregs->ARM_sp = stack_start;
-
        memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
+
+       if (likely(regs)) {
+               *childregs = *regs;
+               childregs->ARM_r0 = 0;
+               childregs->ARM_sp = stack_start;
+               thread->cpu_context.pc = (unsigned long)ret_from_fork;
+       } else {
+               thread->cpu_context.r4 = stk_sz;
+               thread->cpu_context.r5 = stack_start;
+               thread->cpu_context.pc = (unsigned long)ret_from_kernel_thread;
+               childregs->ARM_cpsr = SVC_MODE;
+       }
        thread->cpu_context.sp = (unsigned long)childregs;
-       thread->cpu_context.pc = (unsigned long)ret_from_fork;
 
        clear_ptrace_hw_breakpoint(p);
 
@@ -423,63 +431,6 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp)
 }
 EXPORT_SYMBOL(dump_fpu);
 
-/*
- * Shuffle the argument into the correct register before calling the
- * thread function.  r4 is the thread argument, r5 is the pointer to
- * the thread function, and r6 points to the exit function.
- */
-extern void kernel_thread_helper(void);
-asm(   ".pushsection .text\n"
-"      .align\n"
-"      .type   kernel_thread_helper, #function\n"
-"kernel_thread_helper:\n"
-#ifdef CONFIG_TRACE_IRQFLAGS
-"      bl      trace_hardirqs_on\n"
-#endif
-"      msr     cpsr_c, r7\n"
-"      mov     r0, r4\n"
-"      mov     lr, r6\n"
-"      mov     pc, r5\n"
-"      .size   kernel_thread_helper, . - kernel_thread_helper\n"
-"      .popsection");
-
-#ifdef CONFIG_ARM_UNWIND
-extern void kernel_thread_exit(long code);
-asm(   ".pushsection .text\n"
-"      .align\n"
-"      .type   kernel_thread_exit, #function\n"
-"kernel_thread_exit:\n"
-"      .fnstart\n"
-"      .cantunwind\n"
-"      bl      do_exit\n"
-"      nop\n"
-"      .fnend\n"
-"      .size   kernel_thread_exit, . - kernel_thread_exit\n"
-"      .popsection");
-#else
-#define kernel_thread_exit     do_exit
-#endif
-
-/*
- * Create a kernel thread.
- */
-pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
-{
-       struct pt_regs regs;
-
-       memset(&regs, 0, sizeof(regs));
-
-       regs.ARM_r4 = (unsigned long)arg;
-       regs.ARM_r5 = (unsigned long)fn;
-       regs.ARM_r6 = (unsigned long)kernel_thread_exit;
-       regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
-       regs.ARM_pc = (unsigned long)kernel_thread_helper;
-       regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;
-
-       return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
-}
-EXPORT_SYMBOL(kernel_thread);
-
 unsigned long get_wchan(struct task_struct *p)
 {
        struct stackframe frame;