aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSuzuki K. Poulose <suzuki.poulose@arm.com>2015-10-19 09:24:50 -0400
committerCatalin Marinas <catalin.marinas@arm.com>2015-10-21 10:35:58 -0400
commitdbb4e152b8da1f977d9d8cd7e494ab4ee3622f72 (patch)
treeec927ea2f262dd4205a32c028c119fd304ef2d13
parentce8b602c694c9482e0ffb7432cd59fa2276673fe (diff)
arm64: Delay cpu feature capability checks
At the moment we run through the arm64_features capability list for each CPU and set the capability if one of the CPU supports it. This could be problematic in a heterogeneous system with differing capabilities. Delay the CPU feature checks until all the enabled CPUs are up(i.e, smp_cpus_done(), so that we can make better decisions based on the overall system capability. Once we decide and advertise the capabilities the alternatives can be applied. From this state, we cannot roll back a feature to disabled based on the values from a new hotplugged CPU, due to the runtime patching and other reasons. So, for all new CPUs, we need to make sure that they have the established system capabilities. Failing which, we bring the CPU down, preventing it from turning online. Once the capabilities are decided, any new CPU booting up goes through verification to ensure that it has all the enabled capabilities and also invokes the respective enable() method on the CPU. The CPU errata checks are not delayed and is still executed per-CPU to detect the respective capabilities. If we ever come across a non-errata capability that needs to be checked on each-CPU, we could introduce them via a new capability table(or introduce a flag), which can be processed per CPU. The next patch will make the feature checks use the system wide safe value of a feature register. NOTE: The enable() methods associated with the capability is scheduled on all the CPUs (which is the only use case at the moment). If we need a different type of 'enable()' which only needs to be run once on any CPU, we should be able to handle that when needed. Signed-off-by: Suzuki K. Poulose <suzuki.poulose@arm.com> Tested-by: Dave Martin <Dave.Martin@arm.com> [catalin.marinas@arm.com: static variable and coding style fixes] Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
-rw-r--r--arch/arm64/include/asm/cpufeature.h11
-rw-r--r--arch/arm64/include/asm/processor.h2
-rw-r--r--arch/arm64/kernel/cpufeature.c97
-rw-r--r--arch/arm64/kernel/cpuinfo.c1
-rw-r--r--arch/arm64/kernel/smp.c7
-rw-r--r--arch/arm64/mm/fault.c2
6 files changed, 111 insertions, 9 deletions
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 12d883a067c4..1603ae84ea45 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -70,7 +70,7 @@ struct arm64_cpu_capabilities {
70 const char *desc; 70 const char *desc;
71 u16 capability; 71 u16 capability;
72 bool (*matches)(const struct arm64_cpu_capabilities *); 72 bool (*matches)(const struct arm64_cpu_capabilities *);
73 void (*enable)(void); 73 void (*enable)(void *); /* Called on all active CPUs */
74 union { 74 union {
75 struct { /* To be used for erratum handling only */ 75 struct { /* To be used for erratum handling only */
76 u32 midr_model; 76 u32 midr_model;
@@ -140,7 +140,14 @@ void __init setup_cpu_features(void);
140void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps, 140void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
141 const char *info); 141 const char *info);
142void check_local_cpu_errata(void); 142void check_local_cpu_errata(void);
143void check_local_cpu_features(void); 143
144#ifdef CONFIG_HOTPLUG_CPU
145void verify_local_cpu_capabilities(void);
146#else
147static inline void verify_local_cpu_capabilities(void)
148{
149}
150#endif
144 151
145u64 read_system_reg(u32 id); 152u64 read_system_reg(u32 id);
146 153
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index 98f32355dc97..4acb7ca94fcd 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -186,6 +186,6 @@ static inline void spin_lock_prefetch(const void *x)
186 186
187#endif 187#endif
188 188
189void cpu_enable_pan(void); 189void cpu_enable_pan(void *__unused);
190 190
191#endif /* __ASM_PROCESSOR_H */ 191#endif /* __ASM_PROCESSOR_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 78c4cb7af20e..e002cadf84bf 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -23,6 +23,7 @@
23#include <linux/types.h> 23#include <linux/types.h>
24#include <asm/cpu.h> 24#include <asm/cpu.h>
25#include <asm/cpufeature.h> 25#include <asm/cpufeature.h>
26#include <asm/cpu_ops.h>
26#include <asm/processor.h> 27#include <asm/processor.h>
27#include <asm/sysreg.h> 28#include <asm/sysreg.h>
28 29
@@ -645,19 +646,101 @@ void update_cpu_capabilities(const struct arm64_cpu_capabilities *caps,
645} 646}
646 647
647/* 648/*
648 * Run through the enabled capabilities and enable() it on the CPUs 649 * Run through the enabled capabilities and enable() it on all active
650 * CPUs
649 */ 651 */
650void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps) 652void enable_cpu_capabilities(const struct arm64_cpu_capabilities *caps)
651{ 653{
652 int i; 654 int i;
653 655
656 for (i = 0; caps[i].desc; i++)
657 if (caps[i].enable && cpus_have_cap(caps[i].capability))
658 on_each_cpu(caps[i].enable, NULL, true);
659}
660
661#ifdef CONFIG_HOTPLUG_CPU
662
663/*
664 * Flag to indicate if we have computed the system wide
665 * capabilities based on the boot time active CPUs. This
666 * will be used to determine if a new booting CPU should
667 * go through the verification process to make sure that it
668 * supports the system capabilities, without using a hotplug
669 * notifier.
670 */
671static bool sys_caps_initialised;
672
673static inline void set_sys_caps_initialised(void)
674{
675 sys_caps_initialised = true;
676}
677
678/*
679 * Park the CPU which doesn't have the capability as advertised
680 * by the system.
681 */
682static void fail_incapable_cpu(char *cap_type,
683 const struct arm64_cpu_capabilities *cap)
684{
685 int cpu = smp_processor_id();
686
687 pr_crit("CPU%d: missing %s : %s\n", cpu, cap_type, cap->desc);
688 /* Mark this CPU absent */
689 set_cpu_present(cpu, 0);
690
691 /* Check if we can park ourselves */
692 if (cpu_ops[cpu] && cpu_ops[cpu]->cpu_die)
693 cpu_ops[cpu]->cpu_die(cpu);
694 asm(
695 "1: wfe\n"
696 " wfi\n"
697 " b 1b");
698}
699
700/*
701 * Run through the enabled system capabilities and enable() it on this CPU.
702 * The capabilities were decided based on the available CPUs at the boot time.
703 * Any new CPU should match the system wide status of the capability. If the
704 * new CPU doesn't have a capability which the system now has enabled, we
705 * cannot do anything to fix it up and could cause unexpected failures. So
706 * we park the CPU.
707 */
708void verify_local_cpu_capabilities(void)
709{
710 int i;
711 const struct arm64_cpu_capabilities *caps;
712
713 /*
714 * If we haven't computed the system capabilities, there is nothing
715 * to verify.
716 */
717 if (!sys_caps_initialised)
718 return;
719
720 caps = arm64_features;
654 for (i = 0; caps[i].desc; i++) { 721 for (i = 0; caps[i].desc; i++) {
655 if (cpus_have_cap(caps[i].capability) && caps[i].enable) 722 if (!cpus_have_cap(caps[i].capability))
656 caps[i].enable(); 723 continue;
724 /*
725 * If the new CPU misses an advertised feature, we cannot proceed
726 * further, park the cpu.
727 */
728 if (!caps[i].matches(&caps[i]))
729 fail_incapable_cpu("arm64_features", &caps[i]);
730 if (caps[i].enable)
731 caps[i].enable(NULL);
657 } 732 }
658} 733}
659 734
660void check_local_cpu_features(void) 735#else /* !CONFIG_HOTPLUG_CPU */
736
737static inline void set_sys_caps_initialised(void)
738{
739}
740
741#endif /* CONFIG_HOTPLUG_CPU */
742
743static void setup_feature_capabilities(void)
661{ 744{
662 update_cpu_capabilities(arm64_features, "detected feature:"); 745 update_cpu_capabilities(arm64_features, "detected feature:");
663 enable_cpu_capabilities(arm64_features); 746 enable_cpu_capabilities(arm64_features);
@@ -670,6 +753,12 @@ void __init setup_cpu_features(void)
670 u32 cwg; 753 u32 cwg;
671 int cls; 754 int cls;
672 755
756 /* Set the CPU feature capabilies */
757 setup_feature_capabilities();
758
759 /* Advertise that we have computed the system capabilities */
760 set_sys_caps_initialised();
761
673 /* 762 /*
674 * Check for sane CTR_EL0.CWG value. 763 * Check for sane CTR_EL0.CWG value.
675 */ 764 */
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index f25869ea2646..789fbea92b79 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -229,7 +229,6 @@ static void __cpuinfo_store_cpu(struct cpuinfo_arm64 *info)
229 cpuinfo_detect_icache_policy(info); 229 cpuinfo_detect_icache_policy(info);
230 230
231 check_local_cpu_errata(); 231 check_local_cpu_errata();
232 check_local_cpu_features();
233} 232}
234 233
235void cpuinfo_store_cpu(void) 234void cpuinfo_store_cpu(void)
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index d1d00499f0cf..2bbdc0e4fd14 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -156,6 +156,13 @@ asmlinkage void secondary_start_kernel(void)
156 preempt_disable(); 156 preempt_disable();
157 trace_hardirqs_off(); 157 trace_hardirqs_off();
158 158
159 /*
160 * If the system has established the capabilities, make sure
161 * this CPU ticks all of those. If it doesn't, the CPU will
162 * fail to come online.
163 */
164 verify_local_cpu_capabilities();
165
159 if (cpu_ops[cpu]->cpu_postboot) 166 if (cpu_ops[cpu]->cpu_postboot)
160 cpu_ops[cpu]->cpu_postboot(); 167 cpu_ops[cpu]->cpu_postboot();
161 168
diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index aba9ead1384c..066d5aea16c6 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -555,7 +555,7 @@ asmlinkage int __exception do_debug_exception(unsigned long addr,
555} 555}
556 556
557#ifdef CONFIG_ARM64_PAN 557#ifdef CONFIG_ARM64_PAN
558void cpu_enable_pan(void) 558void cpu_enable_pan(void *__unused)
559{ 559{
560 config_sctlr_el1(SCTLR_EL1_SPAN, 0); 560 config_sctlr_el1(SCTLR_EL1_SPAN, 0);
561} 561}