Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[ARM] Add thread_notify infrastructure

Some machine classes need to allow VFP support to be built into the
kernel, but still allow the kernel to run even though VFP isn't
present. Unfortunately, the kernel hard-codes VFP instructions
into the thread switch, which prevents this being run-time selectable.

Solve this by introducing a notifier which things such as VFP can
hook into to be informed of events which affect the VFP subsystem
(eg, creation and destruction of threads, switches between threads.)

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Russell King and committed by
Russell King
d6551e88 52ab3f3d

+135 -59
+11 -13
arch/arm/kernel/entry-armv.S
··· 20 20 #include <asm/glue.h> 21 21 #include <asm/vfpmacros.h> 22 22 #include <asm/arch/entry-macro.S> 23 + #include <asm/thread_notify.h> 23 24 24 25 #include "entry-header.S" 25 26 ··· 561 560 add ip, r1, #TI_CPU_SAVE 562 561 ldr r3, [r2, #TI_TP_VALUE] 563 562 stmia ip!, {r4 - sl, fp, sp, lr} @ Store most regs on stack 564 - #ifndef CONFIG_MMU 565 - add r2, r2, #TI_CPU_DOMAIN 566 - #else 567 - ldr r6, [r2, #TI_CPU_DOMAIN]! 563 + #ifdef CONFIG_MMU 564 + ldr r6, [r2, #TI_CPU_DOMAIN] 568 565 #endif 569 566 #if __LINUX_ARM_ARCH__ >= 6 570 567 #ifdef CONFIG_CPU_32v6K ··· 584 585 #ifdef CONFIG_MMU 585 586 mcr p15, 0, r6, c3, c0, 0 @ Set domain register 586 587 #endif 587 - #ifdef CONFIG_VFP 588 - @ Always disable VFP so we can lazily save/restore the old 589 - @ state. This occurs in the context of the previous thread. 590 - VFPFMRX r4, FPEXC 591 - bic r4, r4, #FPEXC_ENABLE 592 - VFPFMXR FPEXC, r4 593 - #endif 594 588 #if defined(CONFIG_IWMMXT) 595 589 bl iwmmxt_task_switch 596 590 #elif defined(CONFIG_CPU_XSCALE) 597 - add r4, r2, #40 @ cpu_context_save->extra 591 + add r4, r2, #TI_CPU_DOMAIN + 40 @ cpu_context_save->extra 598 592 ldmib r4, {r4, r5} 599 593 mar acc0, r4, r5 600 594 #endif 601 - ldmib r2, {r4 - sl, fp, sp, pc} @ Load all regs saved previously 595 + mov r5, r0 596 + add r4, r2, #TI_CPU_SAVE 597 + ldr r0, =thread_notify_head 598 + mov r1, #THREAD_NOTIFY_SWITCH 599 + bl atomic_notifier_call_chain 600 + mov r0, r5 601 + ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously 602 602 603 603 __INIT 604 604
+1 -1
arch/arm/kernel/iwmmxt.S
··· 285 285 bne 1f @ yes: block them for next task 286 286 287 287 ldr r5, =concan_owner 288 - add r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area 288 + add r6, r2, #TI_IWMMXT_STATE @ get next task Concan save area 289 289 ldr r5, [r5] @ get current Concan owner 290 290 teq r5, r6 @ next task owns it? 291 291 movne pc, lr @ no: leave Concan disabled
+10 -14
arch/arm/kernel/process.c
··· 33 33 #include <asm/leds.h> 34 34 #include <asm/processor.h> 35 35 #include <asm/system.h> 36 + #include <asm/thread_notify.h> 36 37 #include <asm/uaccess.h> 37 38 #include <asm/mach/time.h> 38 39 ··· 339 338 { 340 339 } 341 340 342 - static void default_fp_init(union fp_state *fp) 343 - { 344 - memset(fp, 0, sizeof(union fp_state)); 345 - } 341 + ATOMIC_NOTIFIER_HEAD(thread_notify_head); 346 342 347 - void (*fp_init)(union fp_state *) = default_fp_init; 348 - EXPORT_SYMBOL(fp_init); 343 + EXPORT_SYMBOL_GPL(thread_notify_head); 349 344 350 345 void flush_thread(void) 351 346 { ··· 350 353 351 354 memset(thread->used_cp, 0, sizeof(thread->used_cp)); 352 355 memset(&tsk->thread.debug, 0, sizeof(struct debug_info)); 356 + memset(&thread->fpstate, 0, sizeof(union fp_state)); 357 + 358 + thread_notify(THREAD_NOTIFY_FLUSH, thread); 353 359 #if defined(CONFIG_IWMMXT) 354 360 iwmmxt_task_release(thread); 355 - #endif 356 - fp_init(&thread->fpstate); 357 - #if defined(CONFIG_VFP) 358 - vfp_flush_thread(&thread->vfpstate); 359 361 #endif 360 362 } 361 363 362 364 void release_thread(struct task_struct *dead_task) 363 365 { 364 - #if defined(CONFIG_VFP) 365 - vfp_release_thread(&task_thread_info(dead_task)->vfpstate); 366 - #endif 366 + struct thread_info *thread = task_thread_info(dead_task); 367 + 368 + thread_notify(THREAD_NOTIFY_RELEASE, thread); 367 369 #if defined(CONFIG_IWMMXT) 368 - iwmmxt_task_release(task_thread_info(dead_task)); 370 + iwmmxt_task_release(thread); 369 371 #endif 370 372 } 371 373
+19 -6
arch/arm/nwfpe/fpmodule.c
··· 33 33 #include <linux/signal.h> 34 34 #include <linux/sched.h> 35 35 #include <linux/init.h> 36 - /* XXX */ 36 + 37 + #include <asm/thread_notify.h> 37 38 38 39 #include "softfloat.h" 39 40 #include "fpopcode.h" ··· 57 56 extern char fpe_type[]; 58 57 #endif 59 58 59 + static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v) 60 + { 61 + struct thread_info *thread = v; 62 + 63 + if (cmd == THREAD_NOTIFY_FLUSH) 64 + nwfpe_init_fpa(&thread->fpstate); 65 + 66 + return NOTIFY_DONE; 67 + } 68 + 69 + static struct notifier_block nwfpe_notifier_block = { 70 + .notifier_call = nwfpe_notify, 71 + }; 72 + 60 73 /* kernel function prototypes required */ 61 74 void fp_setup(void); 62 75 63 76 /* external declarations for saved kernel symbols */ 64 77 extern void (*kern_fp_enter)(void); 65 - extern void (*fp_init)(union fp_state *); 66 78 67 79 /* Original value of fp_enter from kernel before patched by fpe_init. */ 68 80 static void (*orig_fp_enter)(void); 69 - static void (*orig_fp_init)(union fp_state *); 70 81 71 82 /* forward declarations */ 72 83 extern void nwfpe_enter(void); ··· 101 88 printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 (" 102 89 NWFPE_BITS " precision)\n"); 103 90 91 + thread_register_notifier(&nwfpe_notifier_block); 92 + 104 93 /* Save pointer to the old FP handler and then patch ourselves in */ 105 94 orig_fp_enter = kern_fp_enter; 106 - orig_fp_init = fp_init; 107 95 kern_fp_enter = nwfpe_enter; 108 - fp_init = nwfpe_init_fpa; 109 96 110 97 return 0; 111 98 } 112 99 113 100 static void __exit fpe_exit(void) 114 101 { 102 + thread_unregister_notifier(&nwfpe_notifier_block); 115 103 /* Restore the values we saved earlier. */ 116 104 kern_fp_enter = orig_fp_enter; 117 - fp_init = orig_fp_init; 118 105 } 119 106 120 107 /*
+46 -25
arch/arm/vfp/vfpmodule.c
··· 15 15 #include <linux/signal.h> 16 16 #include <linux/sched.h> 17 17 #include <linux/init.h> 18 + 19 + #include <asm/thread_notify.h> 18 20 #include <asm/vfp.h> 19 21 20 22 #include "vfpinstr.h" ··· 38 36 */ 39 37 unsigned int VFP_arch; 40 38 41 - /* 42 - * Per-thread VFP initialisation. 43 - */ 44 - void vfp_flush_thread(union vfp_state *vfp) 39 + static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) 45 40 { 46 - memset(vfp, 0, sizeof(union vfp_state)); 41 + struct thread_info *thread = v; 42 + union vfp_state *vfp = &thread->vfpstate; 47 43 48 - vfp->hard.fpexc = FPEXC_ENABLE; 49 - vfp->hard.fpscr = FPSCR_ROUND_NEAREST; 44 + switch (cmd) { 45 + case THREAD_NOTIFY_FLUSH: 46 + /* 47 + * Per-thread VFP initialisation. 48 + */ 49 + memset(vfp, 0, sizeof(union vfp_state)); 50 50 51 - /* 52 - * Disable VFP to ensure we initialise it first. 53 - */ 54 - fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); 51 + vfp->hard.fpexc = FPEXC_ENABLE; 52 + vfp->hard.fpscr = FPSCR_ROUND_NEAREST; 55 53 56 - /* 57 - * Ensure we don't try to overwrite our newly initialised 58 - * state information on the first fault. 59 - */ 60 - if (last_VFP_context == vfp) 61 - last_VFP_context = NULL; 54 + /* 55 + * Disable VFP to ensure we initialise it first. 56 + */ 57 + fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); 58 + 59 + /* 60 + * FALLTHROUGH: Ensure we don't try to overwrite our newly 61 + * initialised state information on the first fault. 62 + */ 63 + 64 + case THREAD_NOTIFY_RELEASE: 65 + /* 66 + * Per-thread VFP cleanup. 67 + */ 68 + if (last_VFP_context == vfp) 69 + last_VFP_context = NULL; 70 + break; 71 + 72 + case THREAD_NOTIFY_SWITCH: 73 + /* 74 + * Always disable VFP so we can lazily save/restore the 75 + * old state. 76 + */ 77 + fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE); 78 + break; 79 + } 80 + 81 + return NOTIFY_DONE; 62 82 } 63 83 64 - /* 65 - * Per-thread VFP cleanup. 66 - */ 67 - void vfp_release_thread(union vfp_state *vfp) 68 - { 69 - if (last_VFP_context == vfp) 70 - last_VFP_context = NULL; 71 - } 84 + static struct notifier_block vfp_notifier_block = { 85 + .notifier_call = vfp_notifier, 86 + }; 72 87 73 88 /* 74 89 * Raise a SIGFPE for the current process. ··· 300 281 (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT, 301 282 (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT); 302 283 vfp_vector = vfp_support_entry; 284 + 285 + thread_register_notifier(&vfp_notifier_block); 303 286 } 304 287 return 0; 305 288 }
+48
include/asm-arm/thread_notify.h
··· 1 + /* 2 + * linux/include/asm-arm/thread_notify.h 3 + * 4 + * Copyright (C) 2006 Russell King. 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License version 2 as 8 + * published by the Free Software Foundation. 9 + */ 10 + #ifndef ASMARM_THREAD_NOTIFY_H 11 + #define ASMARM_THREAD_NOTIFY_H 12 + 13 + #ifdef __KERNEL__ 14 + 15 + #ifndef __ASSEMBLY__ 16 + 17 + #include <linux/notifier.h> 18 + #include <asm/thread_info.h> 19 + 20 + static inline int thread_register_notifier(struct notifier_block *n) 21 + { 22 + extern struct atomic_notifier_head thread_notify_head; 23 + return atomic_notifier_chain_register(&thread_notify_head, n); 24 + } 25 + 26 + static inline void thread_unregister_notifier(struct notifier_block *n) 27 + { 28 + extern struct atomic_notifier_head thread_notify_head; 29 + atomic_notifier_chain_unregister(&thread_notify_head, n); 30 + } 31 + 32 + static inline void thread_notify(unsigned long rc, struct thread_info *thread) 33 + { 34 + extern struct atomic_notifier_head thread_notify_head; 35 + atomic_notifier_call_chain(&thread_notify_head, rc, thread); 36 + } 37 + 38 + #endif 39 + 40 + /* 41 + * These are the reason codes for the thread notifier. 42 + */ 43 + #define THREAD_NOTIFY_FLUSH 0 44 + #define THREAD_NOTIFY_RELEASE 1 45 + #define THREAD_NOTIFY_SWITCH 2 46 + 47 + #endif 48 + #endif