diff options
author | Suzuki K. Poulose <suzuki.poulose@arm.com> | 2015-01-21 07:43:10 -0500 |
---|---|---|
committer | Catalin Marinas <catalin.marinas@arm.com> | 2015-01-23 12:11:30 -0500 |
commit | 736d474f0fafd1486f178570bc47660ee9dfdef8 (patch) | |
tree | 5d2980222088d482e73ec8047cbca23d7695ec85 /arch/arm64/kernel | |
parent | 04597a65c5efc207257a736d339c6f2f5b00250f (diff) |
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>
Diffstat (limited to 'arch/arm64/kernel')
-rw-r--r-- | arch/arm64/kernel/armv8_deprecated.c | 125 |
1 files changed, 77 insertions, 48 deletions
diff --git a/arch/arm64/kernel/armv8_deprecated.c b/arch/arm64/kernel/armv8_deprecated.c index c363671d7509..68b955e1fd99 100644 --- a/arch/arm64/kernel/armv8_deprecated.c +++ b/arch/arm64/kernel/armv8_deprecated.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <asm/system_misc.h> | 19 | #include <asm/system_misc.h> |
20 | #include <asm/traps.h> | 20 | #include <asm/traps.h> |
21 | #include <asm/uaccess.h> | 21 | #include <asm/uaccess.h> |
22 | #include <asm/cpufeature.h> | ||
22 | 23 | ||
23 | #define CREATE_TRACE_POINTS | 24 | #define CREATE_TRACE_POINTS |
24 | #include "trace-events-emulation.h" | 25 | #include "trace-events-emulation.h" |
@@ -85,6 +86,57 @@ static void remove_emulation_hooks(struct insn_emulation_ops *ops) | |||
85 | pr_notice("Removed %s emulation handler\n", ops->name); | 86 | pr_notice("Removed %s emulation handler\n", ops->name); |
86 | } | 87 | } |
87 | 88 | ||
89 | static void enable_insn_hw_mode(void *data) | ||
90 | { | ||
91 | struct insn_emulation *insn = (struct insn_emulation *)data; | ||
92 | if (insn->ops->set_hw_mode) | ||
93 | insn->ops->set_hw_mode(true); | ||
94 | } | ||
95 | |||
96 | static void disable_insn_hw_mode(void *data) | ||
97 | { | ||
98 | struct insn_emulation *insn = (struct insn_emulation *)data; | ||
99 | if (insn->ops->set_hw_mode) | ||
100 | insn->ops->set_hw_mode(false); | ||
101 | } | ||
102 | |||
103 | /* Run set_hw_mode(mode) on all active CPUs */ | ||
104 | static int run_all_cpu_set_hw_mode(struct insn_emulation *insn, bool enable) | ||
105 | { | ||
106 | if (!insn->ops->set_hw_mode) | ||
107 | return -EINVAL; | ||
108 | if (enable) | ||
109 | on_each_cpu(enable_insn_hw_mode, (void *)insn, true); | ||
110 | else | ||
111 | on_each_cpu(disable_insn_hw_mode, (void *)insn, true); | ||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * Run set_hw_mode for all insns on a starting CPU. | ||
117 | * Returns: | ||
118 | * 0 - If all the hooks ran successfully. | ||
119 | * -EINVAL - At least one hook is not supported by the CPU. | ||
120 | */ | ||
121 | static int run_all_insn_set_hw_mode(unsigned long cpu) | ||
122 | { | ||
123 | int rc = 0; | ||
124 | unsigned long flags; | ||
125 | struct insn_emulation *insn; | ||
126 | |||
127 | raw_spin_lock_irqsave(&insn_emulation_lock, flags); | ||
128 | list_for_each_entry(insn, &insn_emulation, node) { | ||
129 | bool enable = (insn->current_mode == INSN_HW); | ||
130 | if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(enable)) { | ||
131 | pr_warn("CPU[%ld] cannot support the emulation of %s", | ||
132 | cpu, insn->ops->name); | ||
133 | rc = -EINVAL; | ||
134 | } | ||
135 | } | ||
136 | raw_spin_unlock_irqrestore(&insn_emulation_lock, flags); | ||
137 | return rc; | ||
138 | } | ||
139 | |||
88 | static int update_insn_emulation_mode(struct insn_emulation *insn, | 140 | static int update_insn_emulation_mode(struct insn_emulation *insn, |
89 | enum insn_emulation_mode prev) | 141 | enum insn_emulation_mode prev) |
90 | { | 142 | { |
@@ -97,10 +149,8 @@ static int update_insn_emulation_mode(struct insn_emulation *insn, | |||
97 | remove_emulation_hooks(insn->ops); | 149 | remove_emulation_hooks(insn->ops); |
98 | break; | 150 | break; |
99 | case INSN_HW: | 151 | case INSN_HW: |
100 | if (insn->ops->set_hw_mode) { | 152 | if (!run_all_cpu_set_hw_mode(insn, false)) |
101 | insn->ops->set_hw_mode(false); | ||
102 | pr_notice("Disabled %s support\n", insn->ops->name); | 153 | pr_notice("Disabled %s support\n", insn->ops->name); |
103 | } | ||
104 | break; | 154 | break; |
105 | } | 155 | } |
106 | 156 | ||
@@ -111,10 +161,9 @@ static int update_insn_emulation_mode(struct insn_emulation *insn, | |||
111 | register_emulation_hooks(insn->ops); | 161 | register_emulation_hooks(insn->ops); |
112 | break; | 162 | break; |
113 | case INSN_HW: | 163 | case INSN_HW: |
114 | if (insn->ops->set_hw_mode && insn->ops->set_hw_mode(true)) | 164 | ret = run_all_cpu_set_hw_mode(insn, true); |
165 | if (!ret) | ||
115 | pr_notice("Enabled %s support\n", insn->ops->name); | 166 | pr_notice("Enabled %s support\n", insn->ops->name); |
116 | else | ||
117 | ret = -EINVAL; | ||
118 | break; | 167 | break; |
119 | } | 168 | } |
120 | 169 | ||
@@ -133,6 +182,8 @@ static void register_insn_emulation(struct insn_emulation_ops *ops) | |||
133 | switch (ops->status) { | 182 | switch (ops->status) { |
134 | case INSN_DEPRECATED: | 183 | case INSN_DEPRECATED: |
135 | insn->current_mode = INSN_EMULATE; | 184 | insn->current_mode = INSN_EMULATE; |
185 | /* Disable the HW mode if it was turned on at early boot time */ | ||
186 | run_all_cpu_set_hw_mode(insn, false); | ||
136 | insn->max = INSN_HW; | 187 | insn->max = INSN_HW; |
137 | break; | 188 | break; |
138 | case INSN_OBSOLETE: | 189 | case INSN_OBSOLETE: |
@@ -453,8 +504,6 @@ ret: | |||
453 | return 0; | 504 | return 0; |
454 | } | 505 | } |
455 | 506 | ||
456 | #define SCTLR_EL1_CP15BEN (1 << 5) | ||
457 | |||
458 | static inline void config_sctlr_el1(u32 clear, u32 set) | 507 | static inline void config_sctlr_el1(u32 clear, u32 set) |
459 | { | 508 | { |
460 | u32 val; | 509 | u32 val; |
@@ -465,48 +514,13 @@ static inline void config_sctlr_el1(u32 clear, u32 set) | |||
465 | asm volatile("msr sctlr_el1, %0" : : "r" (val)); | 514 | asm volatile("msr sctlr_el1, %0" : : "r" (val)); |
466 | } | 515 | } |
467 | 516 | ||
468 | static void enable_cp15_ben(void *info) | ||
469 | { | ||
470 | config_sctlr_el1(0, SCTLR_EL1_CP15BEN); | ||
471 | } | ||
472 | |||
473 | static void disable_cp15_ben(void *info) | ||
474 | { | ||
475 | config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); | ||
476 | } | ||
477 | |||
478 | static int cpu_hotplug_notify(struct notifier_block *b, | ||
479 | unsigned long action, void *hcpu) | ||
480 | { | ||
481 | switch (action) { | ||
482 | case CPU_STARTING: | ||
483 | case CPU_STARTING_FROZEN: | ||
484 | enable_cp15_ben(NULL); | ||
485 | return NOTIFY_DONE; | ||
486 | case CPU_DYING: | ||
487 | case CPU_DYING_FROZEN: | ||
488 | disable_cp15_ben(NULL); | ||
489 | return NOTIFY_DONE; | ||
490 | } | ||
491 | |||
492 | return NOTIFY_OK; | ||
493 | } | ||
494 | |||
495 | static struct notifier_block cpu_hotplug_notifier = { | ||
496 | .notifier_call = cpu_hotplug_notify, | ||
497 | }; | ||
498 | |||
499 | static int cp15_barrier_set_hw_mode(bool enable) | 517 | static int cp15_barrier_set_hw_mode(bool enable) |
500 | { | 518 | { |
501 | if (enable) { | 519 | if (enable) |
502 | register_cpu_notifier(&cpu_hotplug_notifier); | 520 | config_sctlr_el1(0, SCTLR_EL1_CP15BEN); |
503 | on_each_cpu(enable_cp15_ben, NULL, true); | 521 | else |
504 | } else { | 522 | config_sctlr_el1(SCTLR_EL1_CP15BEN, 0); |
505 | unregister_cpu_notifier(&cpu_hotplug_notifier); | 523 | return 0; |
506 | on_each_cpu(disable_cp15_ben, NULL, true); | ||
507 | } | ||
508 | |||
509 | return true; | ||
510 | } | 524 | } |
511 | 525 | ||
512 | static struct undef_hook cp15_barrier_hooks[] = { | 526 | static struct undef_hook cp15_barrier_hooks[] = { |
@@ -534,6 +548,20 @@ static struct insn_emulation_ops cp15_barrier_ops = { | |||
534 | .set_hw_mode = cp15_barrier_set_hw_mode, | 548 | .set_hw_mode = cp15_barrier_set_hw_mode, |
535 | }; | 549 | }; |
536 | 550 | ||
551 | static int insn_cpu_hotplug_notify(struct notifier_block *b, | ||
552 | unsigned long action, void *hcpu) | ||
553 | { | ||
554 | int rc = 0; | ||
555 | if ((action & ~CPU_TASKS_FROZEN) == CPU_STARTING) | ||
556 | rc = run_all_insn_set_hw_mode((unsigned long)hcpu); | ||
557 | |||
558 | return notifier_from_errno(rc); | ||
559 | } | ||
560 | |||
561 | static struct notifier_block insn_cpu_hotplug_notifier = { | ||
562 | .notifier_call = insn_cpu_hotplug_notify, | ||
563 | }; | ||
564 | |||
537 | /* | 565 | /* |
538 | * Invoked as late_initcall, since not needed before init spawned. | 566 | * Invoked as late_initcall, since not needed before init spawned. |
539 | */ | 567 | */ |
@@ -545,6 +573,7 @@ static int __init armv8_deprecated_init(void) | |||
545 | if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) | 573 | if (IS_ENABLED(CONFIG_CP15_BARRIER_EMULATION)) |
546 | register_insn_emulation(&cp15_barrier_ops); | 574 | register_insn_emulation(&cp15_barrier_ops); |
547 | 575 | ||
576 | register_cpu_notifier(&insn_cpu_hotplug_notifier); | ||
548 | register_insn_emulation_sysctl(ctl_abi); | 577 | register_insn_emulation_sysctl(ctl_abi); |
549 | 578 | ||
550 | return 0; | 579 | return 0; |