diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2012-10-05 07:31:20 -0400 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2012-10-17 09:33:31 -0400 |
commit | c34501d21b005a6e363386a19519bd11cf92a67c (patch) | |
tree | c17d7a800d21418ef12d635a8275ebb063e2816a /arch/arm64 | |
parent | ddffeb8c4d0331609ef2581d84de4d763607bd37 (diff) |
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>
Diffstat (limited to 'arch/arm64')
-rw-r--r-- | arch/arm64/Kconfig | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/processor.h | 5 | ||||
-rw-r--r-- | arch/arm64/kernel/entry.S | 5 | ||||
-rw-r--r-- | arch/arm64/kernel/process.c | 77 |
4 files changed, 29 insertions, 59 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 7ff68c946073..4077b71b1258 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -6,6 +6,7 @@ config ARM64 | |||
6 | select GENERIC_IOMAP | 6 | select GENERIC_IOMAP |
7 | select GENERIC_IRQ_PROBE | 7 | select GENERIC_IRQ_PROBE |
8 | select GENERIC_IRQ_SHOW | 8 | select GENERIC_IRQ_SHOW |
9 | select GENERIC_KERNEL_THREAD | ||
9 | select GENERIC_SMP_IDLE_THREAD | 10 | select GENERIC_SMP_IDLE_THREAD |
10 | select GENERIC_TIME_VSYSCALL | 11 | select GENERIC_TIME_VSYSCALL |
11 | select HARDIRQS_SW_RESEND | 12 | select HARDIRQS_SW_RESEND |
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h index 39a208a392f7..d6331acaf64e 100644 --- a/arch/arm64/include/asm/processor.h +++ b/arch/arm64/include/asm/processor.h | |||
@@ -136,11 +136,6 @@ unsigned long get_wchan(struct task_struct *p); | |||
136 | extern struct task_struct *cpu_switch_to(struct task_struct *prev, | 136 | extern struct task_struct *cpu_switch_to(struct task_struct *prev, |
137 | struct task_struct *next); | 137 | struct task_struct *next); |
138 | 138 | ||
139 | /* | ||
140 | * Create a new kernel thread | ||
141 | */ | ||
142 | extern int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags); | ||
143 | |||
144 | #define task_pt_regs(p) \ | 139 | #define task_pt_regs(p) \ |
145 | ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) | 140 | ((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1) |
146 | 141 | ||
diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index a6f3f7da6880..08db8972ebcc 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S | |||
@@ -611,7 +611,10 @@ ENDPROC(ret_to_user) | |||
611 | */ | 611 | */ |
612 | ENTRY(ret_from_fork) | 612 | ENTRY(ret_from_fork) |
613 | bl schedule_tail | 613 | bl schedule_tail |
614 | get_thread_info tsk | 614 | cbz x19, 1f // not a kernel thread |
615 | mov x0, x20 | ||
616 | blr x19 | ||
617 | 1: get_thread_info tsk | ||
615 | b ret_to_user | 618 | b ret_to_user |
616 | ENDPROC(ret_from_fork) | 619 | ENDPROC(ret_from_fork) |
617 | 620 | ||
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c index f22965ea1cfc..bf615e212c6c 100644 --- a/arch/arm64/kernel/process.c +++ b/arch/arm64/kernel/process.c | |||
@@ -240,27 +240,35 @@ int copy_thread(unsigned long clone_flags, unsigned long stack_start, | |||
240 | struct pt_regs *childregs = task_pt_regs(p); | 240 | struct pt_regs *childregs = task_pt_regs(p); |
241 | unsigned long tls = p->thread.tp_value; | 241 | unsigned long tls = p->thread.tp_value; |
242 | 242 | ||
243 | *childregs = *regs; | 243 | memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); |
244 | childregs->regs[0] = 0; | ||
245 | 244 | ||
246 | if (is_compat_thread(task_thread_info(p))) | 245 | if (likely(regs)) { |
247 | childregs->compat_sp = stack_start; | 246 | *childregs = *regs; |
248 | else { | 247 | childregs->regs[0] = 0; |
248 | if (is_compat_thread(task_thread_info(p))) { | ||
249 | childregs->compat_sp = stack_start; | ||
250 | } else { | ||
251 | /* | ||
252 | * Read the current TLS pointer from tpidr_el0 as it may be | ||
253 | * out-of-sync with the saved value. | ||
254 | */ | ||
255 | asm("mrs %0, tpidr_el0" : "=r" (tls)); | ||
256 | childregs->sp = stack_start; | ||
257 | } | ||
249 | /* | 258 | /* |
250 | * Read the current TLS pointer from tpidr_el0 as it may be | 259 | * If a TLS pointer was passed to clone (4th argument), use it |
251 | * out-of-sync with the saved value. | 260 | * for the new thread. |
252 | */ | 261 | */ |
253 | asm("mrs %0, tpidr_el0" : "=r" (tls)); | 262 | if (clone_flags & CLONE_SETTLS) |
254 | childregs->sp = stack_start; | 263 | tls = regs->regs[3]; |
264 | } else { | ||
265 | memset(childregs, 0, sizeof(struct pt_regs)); | ||
266 | childregs->pstate = PSR_MODE_EL1h; | ||
267 | p->thread.cpu_context.x19 = stack_start; | ||
268 | p->thread.cpu_context.x20 = stk_sz; | ||
255 | } | 269 | } |
256 | |||
257 | memset(&p->thread.cpu_context, 0, sizeof(struct cpu_context)); | ||
258 | p->thread.cpu_context.sp = (unsigned long)childregs; | ||
259 | p->thread.cpu_context.pc = (unsigned long)ret_from_fork; | 270 | p->thread.cpu_context.pc = (unsigned long)ret_from_fork; |
260 | 271 | p->thread.cpu_context.sp = (unsigned long)childregs; | |
261 | /* If a TLS pointer was passed to clone, use that for the new thread. */ | ||
262 | if (clone_flags & CLONE_SETTLS) | ||
263 | tls = regs->regs[3]; | ||
264 | p->thread.tp_value = tls; | 272 | p->thread.tp_value = tls; |
265 | 273 | ||
266 | ptrace_hw_copy_thread(p); | 274 | ptrace_hw_copy_thread(p); |
@@ -327,43 +335,6 @@ int dump_fpu (struct pt_regs *regs, struct user_fp *fp) | |||
327 | } | 335 | } |
328 | EXPORT_SYMBOL(dump_fpu); | 336 | EXPORT_SYMBOL(dump_fpu); |
329 | 337 | ||
330 | /* | ||
331 | * Shuffle the argument into the correct register before calling the | ||
332 | * thread function. x1 is the thread argument, x2 is the pointer to | ||
333 | * the thread function, and x3 points to the exit function. | ||
334 | */ | ||
335 | extern void kernel_thread_helper(void); | ||
336 | asm( ".section .text\n" | ||
337 | " .align\n" | ||
338 | " .type kernel_thread_helper, #function\n" | ||
339 | "kernel_thread_helper:\n" | ||
340 | " mov x0, x1\n" | ||
341 | " mov x30, x3\n" | ||
342 | " br x2\n" | ||
343 | " .size kernel_thread_helper, . - kernel_thread_helper\n" | ||
344 | " .previous"); | ||
345 | |||
346 | #define kernel_thread_exit do_exit | ||
347 | |||
348 | /* | ||
349 | * Create a kernel thread. | ||
350 | */ | ||
351 | pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags) | ||
352 | { | ||
353 | struct pt_regs regs; | ||
354 | |||
355 | memset(®s, 0, sizeof(regs)); | ||
356 | |||
357 | regs.regs[1] = (unsigned long)arg; | ||
358 | regs.regs[2] = (unsigned long)fn; | ||
359 | regs.regs[3] = (unsigned long)kernel_thread_exit; | ||
360 | regs.pc = (unsigned long)kernel_thread_helper; | ||
361 | regs.pstate = PSR_MODE_EL1h; | ||
362 | |||
363 | return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL); | ||
364 | } | ||
365 | EXPORT_SYMBOL(kernel_thread); | ||
366 | |||
367 | unsigned long get_wchan(struct task_struct *p) | 338 | unsigned long get_wchan(struct task_struct *p) |
368 | { | 339 | { |
369 | struct stackframe frame; | 340 | struct stackframe frame; |