mirror of
https://github.com/torvalds/linux.git
synced 2024-12-26 04:42:12 +00:00
arm64: arch_timer: Allows a CPU-specific erratum to only affect a subset of CPUs
Instead of applying a CPU-specific workaround to all CPUs in the system, allow it to only affect a subset of them (typical big-little case). This is done by turning the erratum pointer into a per-CPU variable. Acked-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
This commit is contained in:
parent
8c64621bf9
commit
6acc71ccac
@ -25,6 +25,7 @@
|
|||||||
#include <linux/bug.h>
|
#include <linux/bug.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/jump_label.h>
|
#include <linux/jump_label.h>
|
||||||
|
#include <linux/smp.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
|
||||||
#include <clocksource/arm_arch_timer.h>
|
#include <clocksource/arm_arch_timer.h>
|
||||||
@ -55,17 +56,25 @@ struct arch_timer_erratum_workaround {
|
|||||||
int (*set_next_event_virt)(unsigned long, struct clock_event_device *);
|
int (*set_next_event_virt)(unsigned long, struct clock_event_device *);
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround;
|
DECLARE_PER_CPU(const struct arch_timer_erratum_workaround *,
|
||||||
|
timer_unstable_counter_workaround);
|
||||||
|
|
||||||
#define arch_timer_reg_read_stable(reg) \
|
#define arch_timer_reg_read_stable(reg) \
|
||||||
({ \
|
({ \
|
||||||
u64 _val; \
|
u64 _val; \
|
||||||
if (needs_unstable_timer_counter_workaround() && \
|
if (needs_unstable_timer_counter_workaround()) { \
|
||||||
timer_unstable_counter_workaround->read_##reg) \
|
const struct arch_timer_erratum_workaround *wa; \
|
||||||
_val = timer_unstable_counter_workaround->read_##reg(); \
|
preempt_disable(); \
|
||||||
else \
|
wa = __this_cpu_read(timer_unstable_counter_workaround); \
|
||||||
_val = read_sysreg(reg); \
|
if (wa && wa->read_##reg) \
|
||||||
_val; \
|
_val = wa->read_##reg(); \
|
||||||
|
else \
|
||||||
|
_val = read_sysreg(reg); \
|
||||||
|
preempt_enable(); \
|
||||||
|
} else { \
|
||||||
|
_val = read_sysreg(reg); \
|
||||||
|
} \
|
||||||
|
_val; \
|
||||||
})
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -235,7 +235,8 @@ static u64 notrace hisi_161010101_read_cntvct_el0(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
|
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
|
||||||
const struct arch_timer_erratum_workaround *timer_unstable_counter_workaround = NULL;
|
DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *,
|
||||||
|
timer_unstable_counter_workaround);
|
||||||
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
|
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
|
||||||
|
|
||||||
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
|
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
|
||||||
@ -338,9 +339,18 @@ arch_timer_iterate_errata(enum arch_timer_erratum_match_type type,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static
|
static
|
||||||
void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa)
|
void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa,
|
||||||
|
bool local)
|
||||||
{
|
{
|
||||||
timer_unstable_counter_workaround = wa;
|
int i;
|
||||||
|
|
||||||
|
if (local) {
|
||||||
|
__this_cpu_write(timer_unstable_counter_workaround, wa);
|
||||||
|
} else {
|
||||||
|
for_each_possible_cpu(i)
|
||||||
|
per_cpu(timer_unstable_counter_workaround, i) = wa;
|
||||||
|
}
|
||||||
|
|
||||||
static_branch_enable(&arch_timer_read_ool_enabled);
|
static_branch_enable(&arch_timer_read_ool_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,14 +379,17 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (needs_unstable_timer_counter_workaround()) {
|
if (needs_unstable_timer_counter_workaround()) {
|
||||||
if (wa != timer_unstable_counter_workaround)
|
const struct arch_timer_erratum_workaround *__wa;
|
||||||
|
__wa = __this_cpu_read(timer_unstable_counter_workaround);
|
||||||
|
if (__wa && wa != __wa)
|
||||||
pr_warn("Can't enable workaround for %s (clashes with %s\n)",
|
pr_warn("Can't enable workaround for %s (clashes with %s\n)",
|
||||||
wa->desc,
|
wa->desc, __wa->desc);
|
||||||
timer_unstable_counter_workaround->desc);
|
|
||||||
return;
|
if (__wa)
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
arch_timer_enable_workaround(wa);
|
arch_timer_enable_workaround(wa, local);
|
||||||
pr_info("Enabling %s workaround for %s\n",
|
pr_info("Enabling %s workaround for %s\n",
|
||||||
local ? "local" : "global", wa->desc);
|
local ? "local" : "global", wa->desc);
|
||||||
}
|
}
|
||||||
@ -384,10 +397,15 @@ static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type t
|
|||||||
#define erratum_handler(fn, r, ...) \
|
#define erratum_handler(fn, r, ...) \
|
||||||
({ \
|
({ \
|
||||||
bool __val; \
|
bool __val; \
|
||||||
if (needs_unstable_timer_counter_workaround() && \
|
if (needs_unstable_timer_counter_workaround()) { \
|
||||||
timer_unstable_counter_workaround->fn) { \
|
const struct arch_timer_erratum_workaround *__wa; \
|
||||||
r = timer_unstable_counter_workaround->fn(__VA_ARGS__); \
|
__wa = __this_cpu_read(timer_unstable_counter_workaround); \
|
||||||
__val = true; \
|
if (__wa && __wa->fn) { \
|
||||||
|
r = __wa->fn(__VA_ARGS__); \
|
||||||
|
__val = true; \
|
||||||
|
} else { \
|
||||||
|
__val = false; \
|
||||||
|
} \
|
||||||
} else { \
|
} else { \
|
||||||
__val = false; \
|
__val = false; \
|
||||||
} \
|
} \
|
||||||
|
Loading…
Reference in New Issue
Block a user