[Symptom]
Kernel crash during boot up stage for dual core cpu.
[Root cause]
In vfp_init() in"arch/arm/vfp/vfpmodule.c", if there is process migration betweenvfp_enable() and smp_call_function() then kernel crashes.
[Analyze in code]
===== original code=====
if (cpu_arch >=CPU_ARCH_ARMv6)
vfp_enable(NULL); <== if migration happens justafter vfp_enable(NULL), kernel crashes.
...
vfpsid = fmrx(FPSID); <== if migration happens, readtries to access disbled VFP unit.
...
if (VFP_arch)
printk("notpresent\n");
else if (vfpsid & FPSID_NODOUBLE){
printk("no double precisionsupport\n");
} else {
hotcpu_notifier(vfp_hotplug, 0);
smp_call_function(vfp_enable, NULL, 1); <== if migration happens, smp_call_function will not work as it is expected.
=======================
[Patch]
From f916ccc19694ca3823ee7c4c3d458380b9c4e7d5 Mon Sep 17 00:00:00 2001
From: Will Deacon <will.deacon@arm.com>
Date: Fri, 11 May 2012 17:42:37 +0100
Subject: [PATCH] ARM: 7417/1: vfp: ensure preemption is disabled when enabling VFP access
The vfp_enable function enables access to the VFP co-processor register
space (cp10 and cp11) on the current CPU and must be called with
preemption disabled. Unfortunately, the vfp_init late initcall does not
disable preemption and can lead to an oops during boot if thread
migration occurs at the wrong time and we end up attempting to access
the FPSID on a CPU with VFP access disabled.
This patch fixes the initcall to call vfp_enable from a non-preemptible
context on each CPU and adds a BUG_ON(preemptible) to ensure that any
similar problems are easily spotted in the future.
Change-Id: Ifa685c65a6ff81709e9cf719f066a59d44562e22
Cc: stable@vger.kernel.org
Reported-by: Hyungwoo Yang <hwoo.yang@gmail.com>
Signed-off-by: Hyungwoo Yang <hyungwooy@nvidia.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
arch/arm/vfp/vfpmodule.c | 10 ++++++----
1 files changed, 6 insertions(+), 4 deletions(-)
diff --git a/arch/arm/vfp/vfpmodule.c b/arch/arm/vfp/vfpmodule.c
index 871f03c..7209fcd 100644
--- a/arch/arm/vfp/vfpmodule.c
+++ b/arch/arm/vfp/vfpmodule.c
@@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/types.h>
#include <linux/cpu.h>
+#include <linux/hardirq.h>
#include <linux/kernel.h>
#include <linux/notifier.h>
#include <linux/signal.h>
@@ -395,7 +396,10 @@ void VFP_bounce(u32 trigger, u32 fpexc, struct pt_regs *regs)
static void vfp_enable(void *unused)
{
- u32 access = get_copro_access();
+ u32 access;
+
+ BUG_ON(preemptible());
+ access = get_copro_access();
/*
* Enable full access to VFP (cp10 and cp11)
@@ -539,7 +543,7 @@ static int __init vfp_init(void)
unsigned int cpu_arch = cpu_architecture();
if (cpu_arch >= CPU_ARCH_ARMv6)
- vfp_enable(NULL);
+ on_each_cpu(vfp_enable, NULL, 1);
/*
* First check that there is a VFP that we can use.
@@ -560,8 +564,6 @@ static int __init vfp_init(void)
} else {
hotcpu_notifier(vfp_hotplug, 0);
- smp_call_function(vfp_enable, NULL, 1);
-
VFP_arch = (vfpsid & FPSID_ARCH_MASK) >> FPSID_ARCH_BIT; /* Extract the architecture version */
printk("implementor %02x architecture %d part %02x variant %x rev %x\n",
(vfpsid & FPSID_IMPLEMENTER_MASK) >> FPSID_IMPLEMENTER_BIT,
--
1.7.0.4