arm64: Use generic kernel_thread() implementation
This patch enables CONFIG_GENERIC_KERNEL_THREAD on arm64, changes copy_threads to cope with kernel threads creation and adapts ret_from_fork accordingly. The arm64-specific kernel_thread implementation is no longer needed. Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
This commit is contained in:
parent
ddffeb8c4d
commit
c34501d21b
4 changed files with 32 additions and 62 deletions
|
@ -6,6 +6,7 @@ config ARM64
|
|||
select GENERIC_IOMAP
|
||||
select GENERIC_IRQ_PROBE
|
||||
select GENERIC_IRQ_SHOW
|
||||
select GENERIC_KERNEL_THREAD
|
||||
select GENERIC_SMP_IDLE_THREAD
|
||||
select GENERIC_TIME_VSYSCALL
|
||||
select HARDIRQS_SW_RESEND
|
||||
|
|
|
@ -136,11 +136,6 @@ unsigned long get_wchan(struct task_struct *p);
|
|||
extern struct task_struct *cpu_switch_to(struct task_struct *prev,
|
||||
struct task_struct *next);
|
||||
|
||||
/*
|
||||
* 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)
|
||||
|
||||
|
|
|
@ -611,7 +611,10 @@ ENDPROC(ret_to_user)
|
|||
*/
|
||||
ENTRY(ret_from_fork)
|
||||
bl schedule_tail
|
||||
get_thread_info tsk
|
||||
cbz x19, 1f // not a kernel thread
|
||||
mov x0, x20
|
||||
blr x19
|
||||
1: get_thread_info tsk
|
||||
b ret_to_user
|
||||
ENDPROC(ret_from_fork)
|
||||
|
||||
|
|
|
@ -240,27 +240,35 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start,
|
|||
struct pt_regs *childregs = task_pt_regs(p);
|
||||
unsigned long tls = p->thread.tp_value;
|
||||
|
||||
*childregs = *regs;
|
||||
childregs->regs[0] = 0;
|
||||
|
||||
if (is_compat_thread(task_thread_info(p)))
|
||||
childregs->compat_sp = stack_start;
|
||||
else {
|
||||
/*
|
||||
* Read the current TLS pointer from tpidr_el0 as it may be
|
||||
* out-of-sync with the saved value.
|
||||
*/
|
||||
asm("mrs %0, tpidr_el0" : "=r" (tls));
|
||||
childregs->sp = stack_start;
|
||||
}
|
||||
|
||||
memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context));
|
||||
p->thread.cpu_context.sp = (unsigned long)childregs;
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
|
||||
/* If a TLS pointer was passed to clone, use that for the new thread. */
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
tls = regs->regs[3];
|
||||
if (likely(regs)) {
|
||||
*childregs = *regs;
|
||||
childregs->regs[0] = 0;
|
||||
if (is_compat_thread(task_thread_info(p))) {
|
||||
childregs->compat_sp = stack_start;
|
||||
} else {
|
||||
/*
|
||||
* Read the current TLS pointer from tpidr_el0 as it may be
|
||||
* out-of-sync with the saved value.
|
||||
*/
|
||||
asm("mrs %0, tpidr_el0" : "=r" (tls));
|
||||
childregs->sp = stack_start;
|
||||
}
|
||||
/*
|
||||
* If a TLS pointer was passed to clone (4th argument), use it
|
||||
* for the new thread.
|
||||
*/
|
||||
if (clone_flags & CLONE_SETTLS)
|
||||
tls = regs->regs[3];
|
||||
} else {
|
||||
memset(childregs, 0, sizeof(struct pt_regs));
|
||||
childregs->pstate = PSR_MODE_EL1h;
|
||||
p->thread.cpu_context.x19 = stack_start;
|
||||
p->thread.cpu_context.x20 = stk_sz;
|
||||
}
|
||||
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;
|
||||
p->thread.cpu_context.sp = (unsigned long)childregs;
|
||||
p->thread.tp_value = tls;
|
||||
|
||||
ptrace_hw_copy_thread(p);
|
||||
|
@ -327,43 +335,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. x1 is the thread argument, x2 is the pointer to
|
||||
* the thread function, and x3 points to the exit function.
|
||||
*/
|
||||
extern void kernel_thread_helper(void);
|
||||
asm( ".section .text\n"
|
||||
" .align\n"
|
||||
" .type kernel_thread_helper, #function\n"
|
||||
"kernel_thread_helper:\n"
|
||||
" mov x0, x1\n"
|
||||
" mov x30, x3\n"
|
||||
" br x2\n"
|
||||
" .size kernel_thread_helper, . - kernel_thread_helper\n"
|
||||
" .previous");
|
||||
|
||||
#define kernel_thread_exit do_exit
|
||||
|
||||
/*
|
||||
* Create a kernel thread.
|
||||
*/
|
||||
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
|
||||
memset(®s, 0, sizeof(regs));
|
||||
|
||||
regs.regs[1] = (unsigned long)arg;
|
||||
regs.regs[2] = (unsigned long)fn;
|
||||
regs.regs[3] = (unsigned long)kernel_thread_exit;
|
||||
regs.pc = (unsigned long)kernel_thread_helper;
|
||||
regs.pstate = PSR_MODE_EL1h;
|
||||
|
||||
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL(kernel_thread);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p)
|
||||
{
|
||||
struct stackframe frame;
|
||||
|
|
Loading…
Reference in a new issue