提交 ec866be6 编写于 作者: M Michael Kelley 提交者: Wei Liu

clocksource/drivers/hyper-v: Move handling of STIMER0 interrupts

STIMER0 interrupts are most naturally modeled as per-cpu IRQs. But
because x86/x64 doesn't have per-cpu IRQs, the core STIMER0 interrupt
handling machinery is done in code under arch/x86 and Linux IRQs are
not used. Adding support for ARM64 means adding equivalent code
using per-cpu IRQs under arch/arm64.

A better model is to treat per-cpu IRQs as the normal path (which it is
for modern architectures), and the x86/x64 path as the exception. Do this
by incorporating standard Linux per-cpu IRQ allocation into the main
SITMER0 driver code, and bypass it in the x86/x64 exception case. For
x86/x64, special case code is retained under arch/x86, but no STIMER0
interrupt handling code is needed under arch/arm64.

No functional change.
Signed-off-by: NMichael Kelley <mikelley@microsoft.com>
Acked-by: NDaniel Lezcano <daniel.lezcano@linaro.org>
Link: https://lore.kernel.org/r/1614721102-2241-11-git-send-email-mikelley@microsoft.comSigned-off-by: NWei Liu <wei.liu@kernel.org>
上级 4c78738e
...@@ -327,7 +327,7 @@ static void __init hv_stimer_setup_percpu_clockev(void) ...@@ -327,7 +327,7 @@ static void __init hv_stimer_setup_percpu_clockev(void)
* Ignore any errors in setting up stimer clockevents * Ignore any errors in setting up stimer clockevents
* as we can run with the LAPIC timer as a fallback. * as we can run with the LAPIC timer as a fallback.
*/ */
(void)hv_stimer_alloc(); (void)hv_stimer_alloc(false);
/* /*
* Still register the LAPIC timer, because the direct-mode STIMER is * Still register the LAPIC timer, because the direct-mode STIMER is
......
...@@ -31,10 +31,6 @@ static inline u64 hv_get_register(unsigned int reg) ...@@ -31,10 +31,6 @@ static inline u64 hv_get_register(unsigned int reg)
void hyperv_vector_handler(struct pt_regs *regs); void hyperv_vector_handler(struct pt_regs *regs);
static inline void hv_enable_stimer0_percpu_irq(int irq) {}
static inline void hv_disable_stimer0_percpu_irq(int irq) {}
#if IS_ENABLED(CONFIG_HYPERV) #if IS_ENABLED(CONFIG_HYPERV)
extern int hyperv_init_cpuhp; extern int hyperv_init_cpuhp;
......
...@@ -90,21 +90,17 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0) ...@@ -90,21 +90,17 @@ DEFINE_IDTENTRY_SYSVEC(sysvec_hyperv_stimer0)
set_irq_regs(old_regs); set_irq_regs(old_regs);
} }
int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) /* For x86/x64, override weak placeholders in hyperv_timer.c */
void hv_setup_stimer0_handler(void (*handler)(void))
{ {
*vector = HYPERV_STIMER0_VECTOR;
*irq = -1; /* Unused on x86/x64 */
hv_stimer0_handler = handler; hv_stimer0_handler = handler;
return 0;
} }
EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
void hv_remove_stimer0_irq(int irq) void hv_remove_stimer0_handler(void)
{ {
/* We have no way to deallocate the interrupt gate */ /* We have no way to deallocate the interrupt gate */
hv_stimer0_handler = NULL; hv_stimer0_handler = NULL;
} }
EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
void hv_setup_kexec_handler(void (*handler)(void)) void hv_setup_kexec_handler(void (*handler)(void))
{ {
......
...@@ -18,6 +18,9 @@ ...@@ -18,6 +18,9 @@
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/cpuhotplug.h> #include <linux/cpuhotplug.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/acpi.h>
#include <clocksource/hyperv_timer.h> #include <clocksource/hyperv_timer.h>
#include <asm/hyperv-tlfs.h> #include <asm/hyperv-tlfs.h>
#include <asm/mshyperv.h> #include <asm/mshyperv.h>
...@@ -43,14 +46,13 @@ static u64 hv_sched_clock_offset __ro_after_init; ...@@ -43,14 +46,13 @@ static u64 hv_sched_clock_offset __ro_after_init;
*/ */
static bool direct_mode_enabled; static bool direct_mode_enabled;
static int stimer0_irq; static int stimer0_irq = -1;
static int stimer0_vector;
static int stimer0_message_sint; static int stimer0_message_sint;
static DEFINE_PER_CPU(long, stimer0_evt);
/* /*
* ISR for when stimer0 is operating in Direct Mode. Direct Mode * Common code for stimer0 interrupts coming via Direct Mode or
* does not use VMbus or any VMbus messages, so process here and not * as a VMbus message.
* in the VMbus driver code.
*/ */
void hv_stimer0_isr(void) void hv_stimer0_isr(void)
{ {
...@@ -61,6 +63,16 @@ void hv_stimer0_isr(void) ...@@ -61,6 +63,16 @@ void hv_stimer0_isr(void)
} }
EXPORT_SYMBOL_GPL(hv_stimer0_isr); EXPORT_SYMBOL_GPL(hv_stimer0_isr);
/*
* stimer0 interrupt handler for architectures that support
* per-cpu interrupts, which also implies Direct Mode.
*/
static irqreturn_t hv_stimer0_percpu_isr(int irq, void *dev_id)
{
hv_stimer0_isr();
return IRQ_HANDLED;
}
static int hv_ce_set_next_event(unsigned long delta, static int hv_ce_set_next_event(unsigned long delta,
struct clock_event_device *evt) struct clock_event_device *evt)
{ {
...@@ -76,8 +88,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt) ...@@ -76,8 +88,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
{ {
hv_set_register(HV_REGISTER_STIMER0_COUNT, 0); hv_set_register(HV_REGISTER_STIMER0_COUNT, 0);
hv_set_register(HV_REGISTER_STIMER0_CONFIG, 0); hv_set_register(HV_REGISTER_STIMER0_CONFIG, 0);
if (direct_mode_enabled) if (direct_mode_enabled && stimer0_irq >= 0)
hv_disable_stimer0_percpu_irq(stimer0_irq); disable_percpu_irq(stimer0_irq);
return 0; return 0;
} }
...@@ -95,8 +107,9 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt) ...@@ -95,8 +107,9 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
* on the specified hardware vector/IRQ. * on the specified hardware vector/IRQ.
*/ */
timer_cfg.direct_mode = 1; timer_cfg.direct_mode = 1;
timer_cfg.apic_vector = stimer0_vector; timer_cfg.apic_vector = HYPERV_STIMER0_VECTOR;
hv_enable_stimer0_percpu_irq(stimer0_irq); if (stimer0_irq >= 0)
enable_percpu_irq(stimer0_irq, IRQ_TYPE_NONE);
} else { } else {
/* /*
* When it expires, the timer will generate a VMbus message, * When it expires, the timer will generate a VMbus message,
...@@ -169,10 +182,58 @@ int hv_stimer_cleanup(unsigned int cpu) ...@@ -169,10 +182,58 @@ int hv_stimer_cleanup(unsigned int cpu)
} }
EXPORT_SYMBOL_GPL(hv_stimer_cleanup); EXPORT_SYMBOL_GPL(hv_stimer_cleanup);
/*
* These placeholders are overridden by arch specific code on
* architectures that need special setup of the stimer0 IRQ because
* they don't support per-cpu IRQs (such as x86/x64).
*/
void __weak hv_setup_stimer0_handler(void (*handler)(void))
{
};
void __weak hv_remove_stimer0_handler(void)
{
};
/* Called only on architectures with per-cpu IRQs (i.e., not x86/x64) */
static int hv_setup_stimer0_irq(void)
{
int ret;
ret = acpi_register_gsi(NULL, HYPERV_STIMER0_VECTOR,
ACPI_EDGE_SENSITIVE, ACPI_ACTIVE_HIGH);
if (ret < 0) {
pr_err("Can't register Hyper-V stimer0 GSI. Error %d", ret);
return ret;
}
stimer0_irq = ret;
ret = request_percpu_irq(stimer0_irq, hv_stimer0_percpu_isr,
"Hyper-V stimer0", &stimer0_evt);
if (ret) {
pr_err("Can't request Hyper-V stimer0 IRQ %d. Error %d",
stimer0_irq, ret);
acpi_unregister_gsi(stimer0_irq);
stimer0_irq = -1;
}
return ret;
}
static void hv_remove_stimer0_irq(void)
{
if (stimer0_irq == -1) {
hv_remove_stimer0_handler();
} else {
free_percpu_irq(stimer0_irq, &stimer0_evt);
acpi_unregister_gsi(stimer0_irq);
stimer0_irq = -1;
}
}
/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */ /* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
int hv_stimer_alloc(void) int hv_stimer_alloc(bool have_percpu_irqs)
{ {
int ret = 0; int ret;
/* /*
* Synthetic timers are always available except on old versions of * Synthetic timers are always available except on old versions of
...@@ -188,29 +249,37 @@ int hv_stimer_alloc(void) ...@@ -188,29 +249,37 @@ int hv_stimer_alloc(void)
direct_mode_enabled = ms_hyperv.misc_features & direct_mode_enabled = ms_hyperv.misc_features &
HV_STIMER_DIRECT_MODE_AVAILABLE; HV_STIMER_DIRECT_MODE_AVAILABLE;
if (direct_mode_enabled) {
ret = hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector, /*
hv_stimer0_isr); * If Direct Mode isn't enabled, the remainder of the initialization
* is done later by hv_stimer_legacy_init()
*/
if (!direct_mode_enabled)
return 0;
if (have_percpu_irqs) {
ret = hv_setup_stimer0_irq();
if (ret) if (ret)
goto free_percpu; goto free_clock_event;
} else {
hv_setup_stimer0_handler(hv_stimer0_isr);
}
/* /*
* Since we are in Direct Mode, stimer initialization * Since we are in Direct Mode, stimer initialization
* can be done now with a CPUHP value in the same range * can be done now with a CPUHP value in the same range
* as other clockevent devices. * as other clockevent devices.
*/ */
ret = cpuhp_setup_state(CPUHP_AP_HYPERV_TIMER_STARTING, ret = cpuhp_setup_state(CPUHP_AP_HYPERV_TIMER_STARTING,
"clockevents/hyperv/stimer:starting", "clockevents/hyperv/stimer:starting",
hv_stimer_init, hv_stimer_cleanup); hv_stimer_init, hv_stimer_cleanup);
if (ret < 0) if (ret < 0) {
goto free_stimer0_irq; hv_remove_stimer0_irq();
goto free_clock_event;
} }
return ret; return ret;
free_stimer0_irq: free_clock_event:
hv_remove_stimer0_irq(stimer0_irq);
stimer0_irq = 0;
free_percpu:
free_percpu(hv_clock_event); free_percpu(hv_clock_event);
hv_clock_event = NULL; hv_clock_event = NULL;
return ret; return ret;
...@@ -254,23 +323,6 @@ void hv_stimer_legacy_cleanup(unsigned int cpu) ...@@ -254,23 +323,6 @@ void hv_stimer_legacy_cleanup(unsigned int cpu)
} }
EXPORT_SYMBOL_GPL(hv_stimer_legacy_cleanup); EXPORT_SYMBOL_GPL(hv_stimer_legacy_cleanup);
/* hv_stimer_free - Free global resources allocated by hv_stimer_alloc() */
void hv_stimer_free(void)
{
if (!hv_clock_event)
return;
if (direct_mode_enabled) {
cpuhp_remove_state(CPUHP_AP_HYPERV_TIMER_STARTING);
hv_remove_stimer0_irq(stimer0_irq);
stimer0_irq = 0;
}
free_percpu(hv_clock_event);
hv_clock_event = NULL;
}
EXPORT_SYMBOL_GPL(hv_stimer_free);
/* /*
* Do a global cleanup of clockevents for the cases of kexec and * Do a global cleanup of clockevents for the cases of kexec and
* vmbus exit * vmbus exit
...@@ -287,12 +339,17 @@ void hv_stimer_global_cleanup(void) ...@@ -287,12 +339,17 @@ void hv_stimer_global_cleanup(void)
hv_stimer_legacy_cleanup(cpu); hv_stimer_legacy_cleanup(cpu);
} }
/* if (!hv_clock_event)
* If Direct Mode is enabled, the cpuhp teardown callback return;
* (hv_stimer_cleanup) will be run on all CPUs to stop the
* stimers. if (direct_mode_enabled) {
*/ cpuhp_remove_state(CPUHP_AP_HYPERV_TIMER_STARTING);
hv_stimer_free(); hv_remove_stimer0_irq();
stimer0_irq = -1;
}
free_percpu(hv_clock_event);
hv_clock_event = NULL;
} }
EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup); EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup);
...@@ -457,9 +514,14 @@ static bool __init hv_init_tsc_clocksource(void) ...@@ -457,9 +514,14 @@ static bool __init hv_init_tsc_clocksource(void)
* Hyper-V Reference TSC rating, causing the generic TSC to be used. * Hyper-V Reference TSC rating, causing the generic TSC to be used.
* TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference * TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference
* TSC will be preferred over the virtualized ARM64 arch counter. * TSC will be preferred over the virtualized ARM64 arch counter.
* While the Hyper-V MSR clocksource won't be used since the
* Reference TSC clocksource is present, change its rating as
* well for consistency.
*/ */
if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) if (ms_hyperv.features & HV_ACCESS_TSC_INVARIANT) {
hyperv_cs_tsc.rating = 250; hyperv_cs_tsc.rating = 250;
hyperv_cs_msr.rating = 250;
}
hv_read_reference_counter = read_hv_clock_tsc; hv_read_reference_counter = read_hv_clock_tsc;
phys_addr = virt_to_phys(hv_get_tsc_page()); phys_addr = virt_to_phys(hv_get_tsc_page());
......
...@@ -183,9 +183,4 @@ static inline bool hv_is_hibernation_supported(void) { return false; } ...@@ -183,9 +183,4 @@ static inline bool hv_is_hibernation_supported(void) { return false; }
static inline void hyperv_cleanup(void) {} static inline void hyperv_cleanup(void) {}
#endif /* CONFIG_HYPERV */ #endif /* CONFIG_HYPERV */
#if IS_ENABLED(CONFIG_HYPERV)
extern int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void));
extern void hv_remove_stimer0_irq(int irq);
#endif
#endif #endif
...@@ -21,8 +21,7 @@ ...@@ -21,8 +21,7 @@
#define HV_MIN_DELTA_TICKS 1 #define HV_MIN_DELTA_TICKS 1
/* Routines called by the VMbus driver */ /* Routines called by the VMbus driver */
extern int hv_stimer_alloc(void); extern int hv_stimer_alloc(bool have_percpu_irqs);
extern void hv_stimer_free(void);
extern int hv_stimer_cleanup(unsigned int cpu); extern int hv_stimer_cleanup(unsigned int cpu);
extern void hv_stimer_legacy_init(unsigned int cpu, int sint); extern void hv_stimer_legacy_init(unsigned int cpu, int sint);
extern void hv_stimer_legacy_cleanup(unsigned int cpu); extern void hv_stimer_legacy_cleanup(unsigned int cpu);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册