Commit 384151a9 authored by Russell King's avatar Russell King

[ARM] Avoid using clone syscall from kernel_thread()

Don't issue a system call from kernel_thread(), but call do_fork()
directly.  This avoids all the unnecessary syscall overhead.
parent 13a69a2e
...@@ -312,8 +312,8 @@ void release_thread(struct task_struct *dead_task) ...@@ -312,8 +312,8 @@ void release_thread(struct task_struct *dead_task)
asmlinkage void ret_from_fork(void) __asm__("ret_from_fork"); asmlinkage void ret_from_fork(void) __asm__("ret_from_fork");
int int
copy_thread(int nr, unsigned long clone_flags, unsigned long esp, copy_thread(int nr, unsigned long clone_flags, unsigned long stack_start,
unsigned long unused, struct task_struct *p, struct pt_regs *regs) unsigned long stk_sz, struct task_struct *p, struct pt_regs *regs)
{ {
struct thread_info *thread = p->thread_info; struct thread_info *thread = p->thread_info;
struct pt_regs *childregs; struct pt_regs *childregs;
...@@ -321,7 +321,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long esp, ...@@ -321,7 +321,7 @@ copy_thread(int nr, unsigned long clone_flags, unsigned long esp,
childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_SIZE - 8)) - 1; childregs = ((struct pt_regs *)((unsigned long)thread + THREAD_SIZE - 8)) - 1;
*childregs = *regs; *childregs = *regs;
childregs->ARM_r0 = 0; childregs->ARM_r0 = 0;
childregs->ARM_sp = esp; childregs->ARM_sp = stack_start;
memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save)); memset(&thread->cpu_context, 0, sizeof(struct cpu_context_save));
thread->cpu_context.sp = (unsigned long)childregs; thread->cpu_context.sp = (unsigned long)childregs;
...@@ -373,33 +373,35 @@ void dump_thread(struct pt_regs * regs, struct user * dump) ...@@ -373,33 +373,35 @@ void dump_thread(struct pt_regs * regs, struct user * dump)
} }
/* /*
* This is the mechanism for creating a new kernel thread. * Shuffle the argument into the correct register before calling the
* * thread function. r1 is the thread argument, r2 is the pointer to
* NOTE! Only a kernel-only process(ie the swapper or direct descendants * the thread function, and r3 points to the exit function.
* who haven't done an "execve()") should use this: it will work within */
* a system call from a "real" process, but the process memory space will extern void kernel_thread_helper(void);
* not be free'd until both the parent and the child have exited. asm( ".align\n"
" .type kernel_thread_helper, #function\n"
"kernel_thread_helper:\n"
" mov r0, r1\n"
" mov lr, r3\n"
" mov pc, r2\n"
" .size kernel_thread_helper, . - kernel_thread_helper");
/*
* Create a kernel thread.
*/ */
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{ {
register unsigned int r0 asm("r0") = flags | CLONE_VM | CLONE_UNTRACED; struct pt_regs regs;
register unsigned int r1 asm("r1") = 0;
register pid_t __ret asm("r0"); memset(&regs, 0, sizeof(regs));
__asm__ __volatile__( regs.ARM_r1 = (unsigned long)arg;
__syscall(clone)" @ kernel_thread sys_clone \n\ regs.ARM_r2 = (unsigned long)fn;
movs %0, r0 @ if we are the child \n\ regs.ARM_r3 = (unsigned long)do_exit;
bne 1f \n\ regs.ARM_pc = (unsigned long)kernel_thread_helper;
mov fp, #0 @ ensure that fp is zero \n\ regs.ARM_cpsr = SVC_MODE;
mov r0, %4 \n\
mov lr, pc \n\ return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);
mov pc, %3 \n\
b sys_exit \n\
1: "
: "=r" (__ret)
: "0" (r0), "r" (r1), "r" (fn), "r" (arg)
: "lr");
return __ret;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment