diff options
author | Catalin Marinas <catalin.marinas@arm.com> | 2009-03-10 10:23:07 +0000 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2009-03-10 10:23:07 +0000 |
commit | 2b46fed05976e26825890cbeada18a5f1b6d8d66 (patch) | |
tree | effddafc85ea3a70e7ce37e4ab1ac116b4997e65 /arch/arm/vfp | |
parent | 6b6d872f65d7feb5a91918978e01d07e2823c5c8 (diff) |
Fix a race in the vfp_notifier() function on SMP systems
The vfp_notifier(THREAD_NOTIFY_RELEASE) maybe be called with thread->cpu
different from the current one, causing a race condition with both the
THREAD_NOTIFY_SWITCH path and vfp_support_entry().
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Diffstat (limited to 'arch/arm/vfp')
-rw-r--r-- | arch/arm/vfp/vfpmodule.c | 25 |
1 files changed, 22 insertions, 3 deletions
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c index 63bf97580ccb..83d24e2096f4 100644 --- a/arch/arm/vfp/vfpmodule.c +++ b/arch/arm/vfp/vfpmodule.c @@ -14,6 +14,7 @@ #include <linux/signal.h> #include <linux/sched.h> #include <linux/init.h> +#include <linux/rcupdate.h> #include <asm/thread_notify.h> #include <asm/vfp.h> @@ -49,14 +50,21 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) #ifdef CONFIG_SMP /* + * RCU locking is needed in case last_VFP_context[cpu] is + * released on a different CPU. + */ + rcu_read_lock(); + vfp = last_VFP_context[cpu]; + /* * On SMP, if VFP is enabled, save the old state in * case the thread migrates to a different CPU. The * restoring is done lazily. */ - if ((fpexc & FPEXC_EN) && last_VFP_context[cpu]) { - vfp_save_state(last_VFP_context[cpu], fpexc); - last_VFP_context[cpu]->hard.cpu = cpu; + if ((fpexc & FPEXC_EN) && vfp) { + vfp_save_state(vfp, fpexc); + vfp->hard.cpu = cpu; } + rcu_read_unlock(); /* * Thread migration, just force the reloading of the * state on the new CPU in case the VFP registers @@ -91,8 +99,19 @@ static int vfp_notifier(struct notifier_block *self, unsigned long cmd, void *v) } /* flush and release case: Per-thread VFP cleanup. */ +#ifndef CONFIG_SMP if (last_VFP_context[cpu] == vfp) last_VFP_context[cpu] = NULL; +#else + /* + * Since release_thread() may be called from a different CPU, we use + * cmpxchg() here to avoid a race with the vfp_support_entry() code + * which modifies last_VFP_context[cpu]. Note that on SMP systems, a + * STR instruction on a different CPU clears the global exclusive + * monitor state. + */ + (void)cmpxchg(&last_VFP_context[cpu], vfp, NULL); +#endif return NOTIFY_DONE; } |