Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

arm64: Consolidate hotplug notifier for instruction emulation

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: Mark Rutland <mark.rutland@arm.com>
Signed-off-by: Suzuki K. Poulose <suzuki.poulose@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Punit Agrawal <punit.agrawal@arm.com>
[catalin.marinas@arm.com: fix pr_warn compilation error]
[catalin.marinas@arm.com: remove unnecessary "insn" check]
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>

authored by

Suzuki K. Poulose and committed by
Catalin Marinas
736d474f 04597a65

+79 -48
+2
arch/arm64/include/asm/cputype.h
··· 81 81 #define ID_AA64MMFR0_BIGEND(mmfr0) \ 82 82 (((mmfr0) & ID_AA64MMFR0_BIGEND_MASK) >> ID_AA64MMFR0_BIGEND_SHIFT) 83 83 84 + #define SCTLR_EL1_CP15BEN (0x1 << 5) 85 + 84 86 #ifndef __ASSEMBLY__ 85 87 86 88 /*
+77 -48
arch/arm64/kernel/armv8_deprecated.c
··· 19 19 #include <asm/system_misc.h> 20 20 #include <asm/traps.h> 21 21 #include <asm/uaccess.h> 22 + #include <asm/cpufeature.h> 22 23 23 24 #define CREATE_TRACE_POINTS 24 25 #include "trace-events-emulation.h" ··· 86 85 pr_notice("Removed %s emulation handler\n", ops->name); 87 86 } 88 87 88 + static void enable_insn_hw_mode(void *data) 89 + { 90 + struct insn_emulation *insn = (struct insn_emulation *)data; 91 + if (insn->ops->set_hw_mode) 92 + insn->ops->set_hw_mode(true); 93 + } 94 + 95 + static void disable_insn_hw_mode(void *data) 96 + { 97 + struct insn_emulation *insn = (struct insn_emulation *)data; 98 + if (insn->ops->set_hw_mode) 99 + insn->ops->set_hw_mode(false); 100 + } 101 + 102 + /* Run set_hw_mode(mode) on all active CPUs */ 103 + static int run_all_cpu_set_hw_mode(struct insn_emulation *insn, bool enable) 104 + { 105 + if (!insn->ops->set_hw_mode) 106 + return -EINVAL; 107 + if (enable) 108 + on_each_cpu(enable_insn_hw_mode, (void *)insn, true); 109 + else 110 + on_each_cpu(disable_insn_hw_mode, (void *)insn, true); 111 + return 0; 112 + } 113 + 114 + /* 115 + * Run set_hw_mode for all insns on a starting CPU. 116 + * Returns: 117 + * 0 - If all the hooks ran successfully. 118 + * -EINVAL - At least one hook is not supported by the CPU. 119 + */ 120 + static int run_all_insn_set_hw_mode(unsigned long cpu) 121 + { 122 + int rc = 0; 123 + unsigned long flags; 124 + struct insn_emulation *insn; 125 + 126 + raw_spin_lock_irqsave(&insn_emulation_lock, flags); 127 + list_for_each_entry(insn, &insn_emulation, node) { 128 + bool enable = (insn->current_mode == INSN_HW); 129 + if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(enable)) { 130 + pr_warn("CPU[%ld] cannot support the emulation of %s", 131 + cpu, insn->ops->name); 132 + rc = -EINVAL; 133 + } 134 + } 135 + raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); 136 + return rc; 137 + } 138 + 89 139 static int update_insn_emulation_mode(struct insn_emulation *insn, 90 140 enum insn_emulation_mode prev) 91 141 { ··· 149 97 remove_emulation_hooks(insn->ops); 150 98 break; 151 99 case INSN_HW: 152 - if (insn->ops->set_hw_mode) { 153 - insn->ops->set_hw_mode(false); 100 + if (!run_all_cpu_set_hw_mode(insn, false)) 154 101 pr_notice("Disabled %s support\n", insn->ops->name); 155 - } 156 102 break; 157 103 } 158 104 ··· 161 111 register_emulation_hooks(insn->ops); 162 112 break; 163 113 case INSN_HW: 164 - if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true)) 114 + ret = run_all_cpu_set_hw_mode(insn, true); 115 + if (!ret) 165 116 pr_notice("Enabled %s support\n", insn->ops->name); 166 - else 167 - ret = -EINVAL; 168 117 break; 169 118 } 170 119 ··· 182 133 switch (ops->status) { 183 134 case INSN_DEPRECATED: 184 135 insn->current_mode = INSN_EMULATE; 136 + /* Disable the HW mode if it was turned on at early boot time */ 137 + run_all_cpu_set_hw_mode(insn, false); 185 138 insn->max = INSN_HW; 186 139 break; 187 140 case INSN_OBSOLETE: ··· 504 453 return 0; 505 454 } 506 455 507 - #define SCTLR_EL1_CP15BEN (1 << 5) 508 - 509 456 static inline void config_sctlr_el1(u32 clear, u32 set) 510 457 { 511 458 u32 val; ··· 514 465 asm volatile("msr sctlr_el1, %0" : : "r" (val)); 515 466 } 516 467 517 - static void enable_cp15_ben(void *info) 518 - { 519 - config_sctlr_el1(0, SCTLR_EL1_CP15BEN); 520 - } 521 - 522 - static void disable_cp15_ben(void *info) 523 - { 524 - config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); 525 - } 526 - 527 - static int cpu_hotplug_notify(struct notifier_block *b, 528 - unsigned long action, void *hcpu) 529 - { 530 - switch (action) { 531 - case CPU_STARTING: 532 - case CPU_STARTING_FROZEN: 533 - enable_cp15_ben(NULL); 534 - return NOTIFY_DONE; 535 - case CPU_DYING: 536 - case CPU_DYING_FROZEN: 537 - disable_cp15_ben(NULL); 538 - return NOTIFY_DONE; 539 - } 540 - 541 - return NOTIFY_OK; 542 - } 543 - 544 - static struct notifier_block cpu_hotplug_notifier = { 545 - .notifier_call = cpu_hotplug_notify, 546 - }; 547 - 548 468 static int cp15_barrier_set_hw_mode(bool enable) 549 469 { 550 - if (enable) { 551 - register_cpu_notifier(&cpu_hotplug_notifier); 552 - on_each_cpu(enable_cp15_ben, NULL, true); 553 - } else { 554 - unregister_cpu_notifier(&cpu_hotplug_notifier); 555 - on_each_cpu(disable_cp15_ben, NULL, true); 556 - } 557 - 558 - return true; 470 + if (enable) 471 + config_sctlr_el1(0, SCTLR_EL1_CP15BEN); 472 + else 473 + config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); 474 + return 0; 559 475 } 560 476 561 477 static struct undef_hook cp15_barrier_hooks[] = { ··· 548 534 .set_hw_mode = cp15_barrier_set_hw_mode, 549 535 }; 550 536 537 + static int insn_cpu_hotplug_notify(struct notifier_block *b, 538 + unsigned long action, void *hcpu) 539 + { 540 + int rc = 0; 541 + if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) 542 + rc = run_all_insn_set_hw_mode((unsigned long)hcpu); 543 + 544 + return notifier_from_errno(rc); 545 + } 546 + 547 + static struct notifier_block insn_cpu_hotplug_notifier = { 548 + .notifier_call = insn_cpu_hotplug_notify, 549 + }; 550 + 551 551 /* 552 552 * Invoked as late_initcall, since not needed before init spawned. 553 553 */ ··· 573 545 if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) 574 546 register_insn_emulation(&cp15_barrier_ops); 575 547 548 + register_cpu_notifier(&insn_cpu_hotplug_notifier); 576 549 register_insn_emulation_sysctl(ctl_abi); 577 550 578 551 return 0;