[ARM] Add thread_notify infrastructure
authorRussell King <rmk@dyn-67.arm.linux.org.uk>
Wed, 21 Jun 2006 12:31:52 +0000 (13:31 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Thu, 22 Jun 2006 09:24:18 +0000 (10:24 +0100)
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>
arch/arm/kernel/entry-armv.S
arch/arm/kernel/iwmmxt.S
arch/arm/kernel/process.c
arch/arm/nwfpe/fpmodule.c
arch/arm/vfp/vfpmodule.c
include/asm-arm/thread_notify.h [new file with mode: 0644]

index ab8e600c18c8effe7b5549714672352db02c8439..86c92523a346704e7d94c7c1bc9660e5bc383aa6 100644 (file)
@@ -20,6 +20,7 @@
 #include <asm/glue.h>
 #include <asm/vfpmacros.h>
 #include <asm/arch/entry-macro.S>
+#include <asm/thread_notify.h>
 
 #include "entry-header.S"
 
@@ -560,10 +561,8 @@ ENTRY(__switch_to)
        add     ip, r1, #TI_CPU_SAVE
        ldr     r3, [r2, #TI_TP_VALUE]
        stmia   ip!, {r4 - sl, fp, sp, lr}      @ Store most regs on stack
-#ifndef CONFIG_MMU
-       add     r2, r2, #TI_CPU_DOMAIN
-#else
-       ldr     r6, [r2, #TI_CPU_DOMAIN]!
+#ifdef CONFIG_MMU
+       ldr     r6, [r2, #TI_CPU_DOMAIN]
 #endif
 #if __LINUX_ARM_ARCH__ >= 6
 #ifdef CONFIG_CPU_32v6K
@@ -585,21 +584,20 @@ ENTRY(__switch_to)
 #ifdef CONFIG_MMU
        mcr     p15, 0, r6, c3, c0, 0           @ Set domain register
 #endif
-#ifdef CONFIG_VFP
-       @ Always disable VFP so we can lazily save/restore the old
-       @ state. This occurs in the context of the previous thread.
-       VFPFMRX r4, FPEXC
-       bic     r4, r4, #FPEXC_ENABLE
-       VFPFMXR FPEXC, r4
-#endif
 #if defined(CONFIG_IWMMXT)
        bl      iwmmxt_task_switch
 #elif defined(CONFIG_CPU_XSCALE)
-       add     r4, r2, #40                     @ cpu_context_save->extra
+       add     r4, r2, #TI_CPU_DOMAIN + 40     @ cpu_context_save->extra
        ldmib   r4, {r4, r5}
        mar     acc0, r4, r5
 #endif
-       ldmib   r2, {r4 - sl, fp, sp, pc}       @ Load all regs saved previously
+       mov     r5, r0
+       add     r4, r2, #TI_CPU_SAVE
+       ldr     r0, =thread_notify_head
+       mov     r1, #THREAD_NOTIFY_SWITCH
+       bl      atomic_notifier_call_chain
+       mov     r0, r5
+       ldmia   r4, {r4 - sl, fp, sp, pc}       @ Load all regs saved previously
 
        __INIT
 
index 24c7b0477a09612ac65af3bc614812f5b4b7b276..af9e0ae952d5d92aee8bc501b2b00f7fa5eeddb5 100644 (file)
@@ -285,7 +285,7 @@ ENTRY(iwmmxt_task_switch)
        bne     1f                              @ yes: block them for next task
 
        ldr     r5, =concan_owner
-       add     r6, r2, #(TI_IWMMXT_STATE - TI_CPU_DOMAIN) @ get next task Concan save area
+       add     r6, r2, #TI_IWMMXT_STATE        @ get next task Concan save area
        ldr     r5, [r5]                        @ get current Concan owner
        teq     r5, r6                          @ next task owns it?
        movne   pc, lr                          @ no: leave Concan disabled
index 17c38dbf2f3c87e231331e39bc430cb639c5825d..e1c77ee885a7f98eaaa9ac213991874561d2b34d 100644 (file)
@@ -33,6 +33,7 @@
 #include <asm/leds.h>
 #include <asm/processor.h>
 #include <asm/system.h>
+#include <asm/thread_notify.h>
 #include <asm/uaccess.h>
 #include <asm/mach/time.h>
 
@@ -338,13 +339,9 @@ void exit_thread(void)
 {
 }
 
-static void default_fp_init(union fp_state *fp)
-{
-       memset(fp, 0, sizeof(union fp_state));
-}
+ATOMIC_NOTIFIER_HEAD(thread_notify_head);
 
-void (*fp_init)(union fp_state *) = default_fp_init;
-EXPORT_SYMBOL(fp_init);
+EXPORT_SYMBOL_GPL(thread_notify_head);
 
 void flush_thread(void)
 {
@@ -353,22 +350,21 @@ void flush_thread(void)
 
        memset(thread->used_cp, 0, sizeof(thread->used_cp));
        memset(&tsk->thread.debug, 0, sizeof(struct debug_info));
+       memset(&thread->fpstate, 0, sizeof(union fp_state));
+
+       thread_notify(THREAD_NOTIFY_FLUSH, thread);
 #if defined(CONFIG_IWMMXT)
        iwmmxt_task_release(thread);
-#endif
-       fp_init(&thread->fpstate);
-#if defined(CONFIG_VFP)
-       vfp_flush_thread(&thread->vfpstate);
 #endif
 }
 
 void release_thread(struct task_struct *dead_task)
 {
-#if defined(CONFIG_VFP)
-       vfp_release_thread(&task_thread_info(dead_task)->vfpstate);
-#endif
+       struct thread_info *thread = task_thread_info(dead_task);
+
+       thread_notify(THREAD_NOTIFY_RELEASE, thread);
 #if defined(CONFIG_IWMMXT)
-       iwmmxt_task_release(task_thread_info(dead_task));
+       iwmmxt_task_release(thread);
 #endif
 }
 
index 2dfe1ac42ee8916cc2734d22a671a2f3858ce19d..7d977d23f026701601a460f90123296bcbf32051 100644 (file)
@@ -33,7 +33,8 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/init.h>
-/* XXX */
+
+#include <asm/thread_notify.h>
 
 #include "softfloat.h"
 #include "fpopcode.h"
@@ -56,16 +57,28 @@ void fp_send_sig(unsigned long sig, struct task_struct *p, int priv);
 extern char fpe_type[];
 #endif
 
+static int nwfpe_notify(struct notifier_block *self, unsigned long cmd, void *v)
+{
+       struct thread_info *thread = v;
+
+       if (cmd == THREAD_NOTIFY_FLUSH)
+               nwfpe_init_fpa(&thread->fpstate);
+
+       return NOTIFY_DONE;
+}
+
+static struct notifier_block nwfpe_notifier_block = {
+       .notifier_call = nwfpe_notify,
+};
+
 /* kernel function prototypes required */
 void fp_setup(void);
 
 /* external declarations for saved kernel symbols */
 extern void (*kern_fp_enter)(void);
-extern void (*fp_init)(union fp_state *);
 
 /* Original value of fp_enter from kernel before patched by fpe_init. */
 static void (*orig_fp_enter)(void);
-static void (*orig_fp_init)(union fp_state *);
 
 /* forward declarations */
 extern void nwfpe_enter(void);
@@ -88,20 +101,20 @@ static int __init fpe_init(void)
        printk(KERN_WARNING "NetWinder Floating Point Emulator V0.97 ("
               NWFPE_BITS " precision)\n");
 
+       thread_register_notifier(&nwfpe_notifier_block);
+
        /* Save pointer to the old FP handler and then patch ourselves in */
        orig_fp_enter = kern_fp_enter;
-       orig_fp_init = fp_init;
        kern_fp_enter = nwfpe_enter;
-       fp_init = nwfpe_init_fpa;
 
        return 0;
 }
 
 static void __exit fpe_exit(void)
 {
+       thread_unregister_notifier(&nwfpe_notifier_block);
        /* Restore the values we saved earlier. */
        kern_fp_enter = orig_fp_enter;
-       fp_init = orig_fp_init;
 }
 
 /*
index 03486be04193e0497f8258d45d4be1ef02656258..2476f4c2e760c0e9e6236305bcd728d161952557 100644 (file)
@@ -15,6 +15,8 @@
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/init.h>
+
+#include <asm/thread_notify.h>
 #include <asm/vfp.h>
 
 #include "vfpinstr.h"
@@ -36,38 +38,55 @@ union vfp_state *last_VFP_context;
  */
 unsigned int VFP_arch;
 
-/*
- * Per-thread VFP initialisation.
- */
-void vfp_flush_thread(union vfp_state *vfp)
+static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v)
 {
-       memset(vfp, 0, sizeof(union vfp_state));
+       struct thread_info *thread = v;
+       union vfp_state *vfp = &thread->vfpstate;
 
-       vfp->hard.fpexc = FPEXC_ENABLE;
-       vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
+       switch (cmd) {
+       case THREAD_NOTIFY_FLUSH:
+               /*
+                * Per-thread VFP initialisation.
+                */
+               memset(vfp, 0, sizeof(union vfp_state));
 
-       /*
-        * Disable VFP to ensure we initialise it first.
-        */
-       fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
+               vfp->hard.fpexc = FPEXC_ENABLE;
+               vfp->hard.fpscr = FPSCR_ROUND_NEAREST;
 
-       /*
-        * Ensure we don't try to overwrite our newly initialised
-        * state information on the first fault.
-        */
-       if (last_VFP_context == vfp)
-               last_VFP_context = NULL;
-}
+               /*
+                * Disable VFP to ensure we initialise it first.
+                */
+               fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
 
-/*
- * Per-thread VFP cleanup.
- */
-void vfp_release_thread(union vfp_state *vfp)
-{
-       if (last_VFP_context == vfp)
-               last_VFP_context = NULL;
+               /*
+                * FALLTHROUGH: Ensure we don't try to overwrite our newly
+                * initialised state information on the first fault.
+                */
+
+       case THREAD_NOTIFY_RELEASE:
+               /*
+                * Per-thread VFP cleanup.
+                */
+               if (last_VFP_context == vfp)
+                       last_VFP_context = NULL;
+               break;
+
+       case THREAD_NOTIFY_SWITCH:
+               /*
+                * Always disable VFP so we can lazily save/restore the
+                * old state.
+                */
+               fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_ENABLE);
+               break;
+       }
+
+       return NOTIFY_DONE;
 }
 
+static struct notifier_block vfp_notifier_block = {
+       .notifier_call  = vfp_notifier,
+};
+
 /*
  * Raise a SIGFPE for the current process.
  * sicode describes the signal being raised.
@@ -281,6 +300,8 @@ static int __init vfp_init(void)
                        (vfpsid & FPSID_VARIANT_MASK) >> FPSID_VARIANT_BIT,
                        (vfpsid & FPSID_REV_MASK) >> FPSID_REV_BIT);
                vfp_vector = vfp_support_entry;
+
+               thread_register_notifier(&vfp_notifier_block);
        }
        return 0;
 }
diff --git a/include/asm-arm/thread_notify.h b/include/asm-arm/thread_notify.h
new file mode 100644 (file)
index 0000000..8866e52
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ *  linux/include/asm-arm/thread_notify.h
+ *
+ *  Copyright (C) 2006 Russell King.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#ifndef ASMARM_THREAD_NOTIFY_H
+#define ASMARM_THREAD_NOTIFY_H
+
+#ifdef __KERNEL__
+
+#ifndef __ASSEMBLY__
+
+#include <linux/notifier.h>
+#include <asm/thread_info.h>
+
+static inline int thread_register_notifier(struct notifier_block *n)
+{
+       extern struct atomic_notifier_head thread_notify_head;
+       return atomic_notifier_chain_register(&thread_notify_head, n);
+}
+
+static inline void thread_unregister_notifier(struct notifier_block *n)
+{
+       extern struct atomic_notifier_head thread_notify_head;
+       atomic_notifier_chain_unregister(&thread_notify_head, n);
+}
+
+static inline void thread_notify(unsigned long rc, struct thread_info *thread)
+{
+       extern struct atomic_notifier_head thread_notify_head;
+       atomic_notifier_call_chain(&thread_notify_head, rc, thread);
+}
+
+#endif
+
+/*
+ * These are the reason codes for the thread notifier.
+ */
+#define THREAD_NOTIFY_FLUSH    0
+#define THREAD_NOTIFY_RELEASE  1
+#define THREAD_NOTIFY_SWITCH   2
+
+#endif
+#endif