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 | |
| 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>
| -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; |
