diff options
-rw-r--r-- | arch/arm/kernel/asm-offsets.c | 3 | ||||
-rw-r--r-- | arch/arm/vfp/vfphw.S | 43 | ||||
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 98 |
3 files changed, 89 insertions, 55 deletions
diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index 927522cfc12e..16baba2e4369 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c | |||
@@ -59,6 +59,9 @@ int main(void) | |||
59 | DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); | 59 | DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); |
60 | DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); | 60 | DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); |
61 | DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); | 61 | DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate)); |
62 | #ifdef CONFIG_SMP | ||
63 | DEFINE(VFP_CPU, offsetof(union vfp_state, hard.cpu)); | ||
64 | #endif | ||
62 | #ifdef CONFIG_ARM_THUMBEE | 65 | #ifdef CONFIG_ARM_THUMBEE |
63 | DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); | 66 | DEFINE(TI_THUMBEE_STATE, offsetof(struct thread_info, thumbee_state)); |
64 | #endif | 67 | #endif |
diff --git a/arch/arm/vfp/vfphw.S b/arch/arm/vfp/vfphw.S index 404538ae591d..2d30c7f6edd3 100644 --- a/arch/arm/vfp/vfphw.S +++ b/arch/arm/vfp/vfphw.S | |||
@@ -82,19 +82,22 @@ ENTRY(vfp_support_entry) | |||
82 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer | 82 | ldr r4, [r3, r11, lsl #2] @ vfp_current_hw_state pointer |
83 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled | 83 | bic r5, r1, #FPEXC_EX @ make sure exceptions are disabled |
84 | cmp r4, r10 @ this thread owns the hw context? | 84 | cmp r4, r10 @ this thread owns the hw context? |
85 | #ifndef CONFIG_SMP | ||
86 | @ For UP, checking that this thread owns the hw context is | ||
87 | @ sufficient to determine that the hardware state is valid. | ||
85 | beq vfp_hw_state_valid | 88 | beq vfp_hw_state_valid |
86 | 89 | ||
90 | @ On UP, we lazily save the VFP context. As a different | ||
91 | @ thread wants ownership of the VFP hardware, save the old | ||
92 | @ state if there was a previous (valid) owner. | ||
93 | |||
87 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | 94 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending |
88 | @ exceptions, so we can get at the | 95 | @ exceptions, so we can get at the |
89 | @ rest of it | 96 | @ rest of it |
90 | 97 | ||
91 | #ifndef CONFIG_SMP | ||
92 | @ Save out the current registers to the old thread state | ||
93 | @ No need for SMP since this is not done lazily | ||
94 | |||
95 | DBGSTR1 "save old state %p", r4 | 98 | DBGSTR1 "save old state %p", r4 |
96 | cmp r4, #0 | 99 | cmp r4, #0 @ if the vfp_current_hw_state is NULL |
97 | beq no_old_VFP_process | 100 | beq vfp_reload_hw @ then the hw state needs reloading |
98 | VFPFSTMIA r4, r5 @ save the working registers | 101 | VFPFSTMIA r4, r5 @ save the working registers |
99 | VFPFMRX r5, FPSCR @ current status | 102 | VFPFMRX r5, FPSCR @ current status |
100 | #ifndef CONFIG_CPU_FEROCEON | 103 | #ifndef CONFIG_CPU_FEROCEON |
@@ -107,11 +110,33 @@ ENTRY(vfp_support_entry) | |||
107 | 1: | 110 | 1: |
108 | #endif | 111 | #endif |
109 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 | 112 | stmia r4, {r1, r5, r6, r8} @ save FPEXC, FPSCR, FPINST, FPINST2 |
110 | @ and point r4 at the word at the | 113 | vfp_reload_hw: |
111 | @ start of the register dump | 114 | |
115 | #else | ||
116 | @ For SMP, if this thread does not own the hw context, then we | ||
117 | @ need to reload it. No need to save the old state as on SMP, | ||
118 | @ we always save the state when we switch away from a thread. | ||
119 | bne vfp_reload_hw | ||
120 | |||
121 | @ This thread has ownership of the current hardware context. | ||
122 | @ However, it may have been migrated to another CPU, in which | ||
123 | @ case the saved state is newer than the hardware context. | ||
124 | @ Check this by looking at the CPU number which the state was | ||
125 | @ last loaded onto. | ||
126 | ldr ip, [r10, #VFP_CPU] | ||
127 | teq ip, r11 | ||
128 | beq vfp_hw_state_valid | ||
129 | |||
130 | vfp_reload_hw: | ||
131 | @ We're loading this threads state into the VFP hardware. Update | ||
132 | @ the CPU number which contains the most up to date VFP context. | ||
133 | str r11, [r10, #VFP_CPU] | ||
134 | |||
135 | VFPFMXR FPEXC, r5 @ enable VFP, disable any pending | ||
136 | @ exceptions, so we can get at the | ||
137 | @ rest of it | ||
112 | #endif | 138 | #endif |
113 | 139 | ||
114 | no_old_VFP_process: | ||
115 | DBGSTR1 "load state %p", r10 | 140 | DBGSTR1 "load state %p", r10 |
116 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer | 141 | str r10, [r3, r11, lsl #2] @ update the vfp_current_hw_state pointer |
117 | @ Load the saved state back into the VFP | 142 | @ Load the saved state back into the VFP |
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 3640351171b8..08ff93fa533c 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c | |||
@@ -35,18 +35,51 @@ void vfp_null_entry(void); | |||
35 | void (*vfp_vector)(void) = vfp_null_entry; | 35 | void (*vfp_vector)(void) = vfp_null_entry; |
36 | 36 | ||
37 | /* | 37 | /* |
38 | * Dual-use variable. | ||
39 | * Used in startup: set to non-zero if VFP checks fail | ||
40 | * After startup, holds VFP architecture | ||
41 | */ | ||
42 | unsigned int VFP_arch; | ||
43 | |||
44 | /* | ||
38 | * The pointer to the vfpstate structure of the thread which currently | 45 | * The pointer to the vfpstate structure of the thread which currently |
39 | * owns the context held in the VFP hardware, or NULL if the hardware | 46 | * owns the context held in the VFP hardware, or NULL if the hardware |
40 | * context is invalid. | 47 | * context is invalid. |
48 | * | ||
49 | * For UP, this is sufficient to tell which thread owns the VFP context. | ||
50 | * However, for SMP, we also need to check the CPU number stored in the | ||
51 | * saved state too to catch migrations. | ||
41 | */ | 52 | */ |
42 | union vfp_state *vfp_current_hw_state[NR_CPUS]; | 53 | union vfp_state *vfp_current_hw_state[NR_CPUS]; |
43 | 54 | ||
44 | /* | 55 | /* |
45 | * Dual-use variable. | 56 | * Is 'thread's most up to date state stored in this CPUs hardware? |
46 | * Used in startup: set to non-zero if VFP checks fail | 57 | * Must be called from non-preemptible context. |
47 | * After startup, holds VFP architecture | ||
48 | */ | 58 | */ |
49 | unsigned int VFP_arch; | 59 | static bool vfp_state_in_hw(unsigned int cpu, struct thread_info *thread) |
60 | { | ||
61 | #ifdef CONFIG_SMP | ||
62 | if (thread->vfpstate.hard.cpu != cpu) | ||
63 | return false; | ||
64 | #endif | ||
65 | return vfp_current_hw_state[cpu] == &thread->vfpstate; | ||
66 | } | ||
67 | |||
68 | /* | ||
69 | * Force a reload of the VFP context from the thread structure. We do | ||
70 | * this by ensuring that access to the VFP hardware is disabled, and | ||
71 | * clear last_VFP_context. Must be called from non-preemptible context. | ||
72 | */ | ||
73 | static void vfp_force_reload(unsigned int cpu, struct thread_info *thread) | ||
74 | { | ||
75 | if (vfp_state_in_hw(cpu, thread)) { | ||
76 | fmxr(FPEXC, fmrx(FPEXC) & ~FPEXC_EN); | ||
77 | vfp_current_hw_state[cpu] = NULL; | ||
78 | } | ||
79 | #ifdef CONFIG_SMP | ||
80 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
81 | #endif | ||
82 | } | ||
50 | 83 | ||
51 | /* | 84 | /* |
52 | * Per-thread VFP initialization. | 85 | * Per-thread VFP initialization. |
@@ -60,6 +93,9 @@ static void vfp_thread_flush(struct thread_info *thread) | |||
60 | 93 | ||
61 | vfp->hard.fpexc = FPEXC_EN; | 94 | vfp->hard.fpexc = FPEXC_EN; |
62 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; | 95 | vfp->hard.fpscr = FPSCR_ROUND_NEAREST; |
96 | #ifdef CONFIG_SMP | ||
97 | vfp->hard.cpu = NR_CPUS; | ||
98 | #endif | ||
63 | 99 | ||
64 | /* | 100 | /* |
65 | * Disable VFP to ensure we initialize it first. We must ensure | 101 | * Disable VFP to ensure we initialize it first. We must ensure |
@@ -90,6 +126,9 @@ static void vfp_thread_copy(struct thread_info *thread) | |||
90 | 126 | ||
91 | vfp_sync_hwstate(parent); | 127 | vfp_sync_hwstate(parent); |
92 | thread->vfpstate = parent->vfpstate; | 128 | thread->vfpstate = parent->vfpstate; |
129 | #ifdef CONFIG_SMP | ||
130 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
131 | #endif | ||
93 | } | 132 | } |
94 | 133 | ||
95 | /* | 134 | /* |
@@ -135,17 +174,8 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) | |||
135 | * case the thread migrates to a different CPU. The | 174 | * case the thread migrates to a different CPU. The |
136 | * restoring is done lazily. | 175 | * restoring is done lazily. |
137 | */ | 176 | */ |
138 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) { | 177 | if ((fpexc & FPEXC_EN) && vfp_current_hw_state[cpu]) |
139 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); | 178 | vfp_save_state(vfp_current_hw_state[cpu], fpexc); |
140 | vfp_current_hw_state[cpu]->hard.cpu = cpu; | ||
141 | } | ||
142 | /* | ||
143 | * Thread migration, just force the reloading of the | ||
144 | * state on the new CPU in case the VFP registers | ||
145 | * contain stale data. | ||
146 | */ | ||
147 | if (thread->vfpstate.hard.cpu != cpu) | ||
148 | vfp_current_hw_state[cpu] = NULL; | ||
149 | #endif | 179 | #endif |
150 | 180 | ||
151 | /* | 181 | /* |
@@ -449,15 +479,15 @@ static void vfp_pm_init(void) | |||
449 | static inline void vfp_pm_init(void) { } | 479 | static inline void vfp_pm_init(void) { } |
450 | #endif /* CONFIG_PM */ | 480 | #endif /* CONFIG_PM */ |
451 | 481 | ||
482 | /* | ||
483 | * Ensure that the VFP state stored in 'thread->vfpstate' is up to date | ||
484 | * with the hardware state. | ||
485 | */ | ||
452 | void vfp_sync_hwstate(struct thread_info *thread) | 486 | void vfp_sync_hwstate(struct thread_info *thread) |
453 | { | 487 | { |
454 | unsigned int cpu = get_cpu(); | 488 | unsigned int cpu = get_cpu(); |
455 | 489 | ||
456 | /* | 490 | if (vfp_state_in_hw(cpu, thread)) { |
457 | * If the thread we're interested in is the current owner of the | ||
458 | * hardware VFP state, then we need to save its state. | ||
459 | */ | ||
460 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { | ||
461 | u32 fpexc = fmrx(FPEXC); | 491 | u32 fpexc = fmrx(FPEXC); |
462 | 492 | ||
463 | /* | 493 | /* |
@@ -471,36 +501,13 @@ void vfp_sync_hwstate(struct thread_info *thread) | |||
471 | put_cpu(); | 501 | put_cpu(); |
472 | } | 502 | } |
473 | 503 | ||
504 | /* Ensure that the thread reloads the hardware VFP state on the next use. */ | ||
474 | void vfp_flush_hwstate(struct thread_info *thread) | 505 | void vfp_flush_hwstate(struct thread_info *thread) |
475 | { | 506 | { |
476 | unsigned int cpu = get_cpu(); | 507 | unsigned int cpu = get_cpu(); |
477 | 508 | ||
478 | /* | 509 | vfp_force_reload(cpu, thread); |
479 | * If the thread we're interested in is the current owner of the | ||
480 | * hardware VFP state, then we need to save its state. | ||
481 | */ | ||
482 | if (vfp_current_hw_state[cpu] == &thread->vfpstate) { | ||
483 | u32 fpexc = fmrx(FPEXC); | ||
484 | 510 | ||
485 | fmxr(FPEXC, fpexc & ~FPEXC_EN); | ||
486 | |||
487 | /* | ||
488 | * Set the context to NULL to force a reload the next time | ||
489 | * the thread uses the VFP. | ||
490 | */ | ||
491 | vfp_current_hw_state[cpu] = NULL; | ||
492 | } | ||
493 | |||
494 | #ifdef CONFIG_SMP | ||
495 | /* | ||
496 | * For SMP we still have to take care of the case where the thread | ||
497 | * migrates to another CPU and then back to the original CPU on which | ||
498 | * the last VFP user is still the same thread. Mark the thread VFP | ||
499 | * state as belonging to a non-existent CPU so that the saved one will | ||
500 | * be reloaded in the above case. | ||
501 | */ | ||
502 | thread->vfpstate.hard.cpu = NR_CPUS; | ||
503 | #endif | ||
504 | put_cpu(); | 511 | put_cpu(); |
505 | } | 512 | } |
506 | 513 | ||
@@ -519,8 +526,7 @@ static int vfp_hotplug(struct notifier_block *b, unsigned long action, | |||
519 | void *hcpu) | 526 | void *hcpu) |
520 | { | 527 | { |
521 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { | 528 | if (action == CPU_DYING || action == CPU_DYING_FROZEN) { |
522 | unsigned int cpu = (long)hcpu; | 529 | vfp_force_reload((long)hcpu, current_thread_info()); |
523 | vfp_current_hw_state[cpu] = NULL; | ||
524 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) | 530 | } else if (action == CPU_STARTING || action == CPU_STARTING_FROZEN) |
525 | vfp_enable(NULL); | 531 | vfp_enable(NULL); |
526 | return NOTIFY_OK; | 532 | return NOTIFY_OK; |