On Thu, Jan 15, 2015 at 12:36:05PM +0000, Suzuki K. Poulose wrote: > From: "Suzuki K. Poulose" <suzuki.poul...@arm.com> > > As of now each insn_emulation has a cpu hotplug notifier that > enables/disables the CPU feature bit for the functionality. This > patch re-arranges the code, such that there is only one notifier > that runs through the list of registered emulation hooks and runs > their corresponding set_hw_mode. > > We do nothing when a CPU is dying as we will set the appropriate bits > as it comes back online based on the state of the hooks. > > Signed-off-by: Suzuki K. Poulose <suzuki.poul...@arm.com> > Signed-off-by: Mark Rutland <mark.rutl...@arm.com> > --- > Documentation/arm64/legacy_instructions.txt | 4 + > arch/arm64/include/asm/cpufeature.h | 2 + > arch/arm64/kernel/armv8_deprecated.c | 113 > +++++++++++++++------------ > 3 files changed, 69 insertions(+), 50 deletions(-) > > diff --git a/Documentation/arm64/legacy_instructions.txt > b/Documentation/arm64/legacy_instructions.txt > index a3b3da2..0a4dc26 100644 > --- a/Documentation/arm64/legacy_instructions.txt > +++ b/Documentation/arm64/legacy_instructions.txt > @@ -27,6 +27,10 @@ behaviours and the corresponding values of the sysctl > nodes - > instructions. Using hardware execution generally provides better > performance, but at the loss of ability to gather runtime statistics > about the use of the deprecated instructions. > + Note: Emulation of a deprecated instruction depends on the availability > + of the feature on all the active CPUs. In case of CPU hotplug, if a new > + CPU doesn't support a feature, it could result in the abortion of the > + hotplug operation.
Is this true? We should be able to *emulate* the instruction anywhere, it's the "hardware execution" setting that needs CPU support. > The default mode depends on the status of the instruction in the > architecture. Deprecated instructions should default to emulation > diff --git a/arch/arm64/include/asm/cpufeature.h > b/arch/arm64/include/asm/cpufeature.h > index c7f68d1..a56b45f 100644 > --- a/arch/arm64/include/asm/cpufeature.h > +++ b/arch/arm64/include/asm/cpufeature.h > @@ -29,6 +29,8 @@ > #define ID_AA64MMFR0_EL1_BigEndEL0 (0x1UL << 16) > #define ID_AA64MMFR0_EL1_BigEnd (0x1UL << 8) > > +#define SCTLR_EL1_CP15BEN (1 << 5) > + > #ifndef __ASSEMBLY__ > > extern DECLARE_BITMAP(cpu_hwcaps, ARM64_NCAPS); > diff --git a/arch/arm64/kernel/armv8_deprecated.c > b/arch/arm64/kernel/armv8_deprecated.c > index c363671..be64218 100644 > --- a/arch/arm64/kernel/armv8_deprecated.c > +++ b/arch/arm64/kernel/armv8_deprecated.c > @@ -19,6 +19,7 @@ > #include <asm/system_misc.h> > #include <asm/traps.h> > #include <asm/uaccess.h> > +#include <asm/cpufeature.h> > > #define CREATE_TRACE_POINTS > #include "trace-events-emulation.h" > @@ -46,7 +47,7 @@ struct insn_emulation_ops { > const char *name; > enum legacy_insn_status status; > struct undef_hook *hooks; > - int (*set_hw_mode)(bool enable); > + int (*set_hw_mode)(void *enable); I think it would be cleaner to have a wrapper for the on_each_cpu variant of this, otherwise we lose the type information altogether. > }; > > struct insn_emulation { > @@ -85,6 +86,40 @@ static void remove_emulation_hooks(struct > insn_emulation_ops *ops) > pr_notice("Removed %s emulation handler\n", ops->name); > } > > +/* Run set_hw_mode(mode) on all active CPUs */ > +static int run_all_cpu_set_hw_mode(struct insn_emulation *insn, bool enable) > +{ > + void (*set_hw_mode)(void *) = (void (*) (void *))insn->ops->set_hw_mode; > + if (!set_hw_mode) > + return -EINVAL; > + on_each_cpu(set_hw_mode, (void *)enable, true); > + return 0; > +} > + > +/* > + * Run set_hw_mode for all insns on a starting CPU. > + * Returns: > + * 0 - If all the hooks ran successfully. > + * -EINVAL - At least one hook is not supported by the CPU. > + * abort the hotplug. > + */ > +static int run_all_insn_set_hw_mode(void) > +{ > + int rc = 0; > + unsigned long flags; > + struct insn_emulation *insn; > + > + raw_spin_lock_irqsave(&insn_emulation_lock, flags); > + list_for_each_entry(insn, &insn_emulation, node) { > + bool hw_mode = (insn->current_mode == INSN_HW); > + if (insn->ops->set_hw_mode && > + insn->ops->set_hw_mode((void*)hw_mode)) > + rc = -EINVAL; > + } > + raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); > + return rc; > +} > + > static int update_insn_emulation_mode(struct insn_emulation *insn, > enum insn_emulation_mode prev) > { > @@ -97,10 +132,8 @@ static int update_insn_emulation_mode(struct > insn_emulation *insn, > remove_emulation_hooks(insn->ops); > break; > case INSN_HW: > - if (insn->ops->set_hw_mode) { > - insn->ops->set_hw_mode(false); > + if (!run_all_cpu_set_hw_mode(insn, false)) > pr_notice("Disabled %s support\n", insn->ops->name); > - } > break; > } > > @@ -111,10 +144,9 @@ static int update_insn_emulation_mode(struct > insn_emulation *insn, > register_emulation_hooks(insn->ops); > break; > case INSN_HW: > - if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true)) > + ret = run_all_cpu_set_hw_mode(insn, true); > + if (!ret) > pr_notice("Enabled %s support\n", insn->ops->name); > - else > - ret = -EINVAL; > break; > } > > @@ -133,6 +165,8 @@ static void register_insn_emulation(struct > insn_emulation_ops *ops) > switch (ops->status) { > case INSN_DEPRECATED: > insn->current_mode = INSN_EMULATE; > + /* Disable the HW mode if it was turned on at early boot time */ > + run_all_cpu_set_hw_mode(insn, false); > insn->max = INSN_HW; > break; > case INSN_OBSOLETE: > @@ -453,8 +487,6 @@ ret: > return 0; > } > > -#define SCTLR_EL1_CP15BEN (1 << 5) > - > static inline void config_sctlr_el1(u32 clear, u32 set) > { > u32 val; > @@ -465,48 +497,13 @@ static inline void config_sctlr_el1(u32 clear, u32 set) > asm volatile("msr sctlr_el1, %0" : : "r" (val)); > } > > -static void enable_cp15_ben(void *info) > -{ > - config_sctlr_el1(0, SCTLR_EL1_CP15BEN); > -} > - > -static void disable_cp15_ben(void *info) > +static int cp15_barrier_set_hw_mode(void *enable) > { > - config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); > -} > - > -static int cpu_hotplug_notify(struct notifier_block *b, > - unsigned long action, void *hcpu) > -{ > - switch (action) { > - case CPU_STARTING: > - case CPU_STARTING_FROZEN: > - enable_cp15_ben(NULL); > - return NOTIFY_DONE; > - case CPU_DYING: > - case CPU_DYING_FROZEN: > - disable_cp15_ben(NULL); > - return NOTIFY_DONE; > - } > - > - return NOTIFY_OK; > -} > - > -static struct notifier_block cpu_hotplug_notifier = { > - .notifier_call = cpu_hotplug_notify, > -}; > - > -static int cp15_barrier_set_hw_mode(bool enable) > -{ > - if (enable) { > - register_cpu_notifier(&cpu_hotplug_notifier); > - on_each_cpu(enable_cp15_ben, NULL, true); > - } else { > - unregister_cpu_notifier(&cpu_hotplug_notifier); > - on_each_cpu(disable_cp15_ben, NULL, true); > - } > - > - return true; > + if (enable) > + config_sctlr_el1(0, SCTLR_EL1_CP15BEN); > + else > + config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); > + return 0; > } > > static struct undef_hook cp15_barrier_hooks[] = { > @@ -534,6 +531,21 @@ static struct insn_emulation_ops cp15_barrier_ops = { > .set_hw_mode = cp15_barrier_set_hw_mode, > }; > > +static int insn_cpu_hotplug_notify(struct notifier_block *b, > + unsigned long action, void *hcpu) > +{ > + int rc = 0; > + if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) > + rc = run_all_insn_set_hw_mode(); > + > + /* Abort the CPU hotplug if there is an incompatibility */ > + return notifier_from_errno(rc); Could we restrict the emulation options instead and just disable hardware support for the instruction? Will -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/