1818#include <linux/sched_clock.h>
1919#include <linux/mm.h>
2020#include <linux/cpuhotplug.h>
21+ #include <linux/interrupt.h>
22+ #include <linux/irq.h>
23+ #include <linux/acpi.h>
2124#include <clocksource/hyperv_timer.h>
2225#include <asm/hyperv-tlfs.h>
2326#include <asm/mshyperv.h>
@@ -43,14 +46,13 @@ static u64 hv_sched_clock_offset __ro_after_init;
4346 */
4447static bool direct_mode_enabled ;
4548
46- static int stimer0_irq ;
47- static int stimer0_vector ;
49+ static int stimer0_irq = -1 ;
4850static int stimer0_message_sint ;
51+ static DEFINE_PER_CPU (long , stimer0_evt );
4952
5053/*
51- * ISR for when stimer0 is operating in Direct Mode. Direct Mode
52- * does not use VMbus or any VMbus messages, so process here and not
53- * in the VMbus driver code.
54+ * Common code for stimer0 interrupts coming via Direct Mode or
55+ * as a VMbus message.
5456 */
5557void hv_stimer0_isr (void )
5658{
@@ -61,6 +63,16 @@ void hv_stimer0_isr(void)
6163}
6264EXPORT_SYMBOL_GPL (hv_stimer0_isr );
6365
66+ /*
67+ * stimer0 interrupt handler for architectures that support
68+ * per-cpu interrupts, which also implies Direct Mode.
69+ */
70+ static irqreturn_t hv_stimer0_percpu_isr (int irq , void * dev_id )
71+ {
72+ hv_stimer0_isr ();
73+ return IRQ_HANDLED ;
74+ }
75+
6476static int hv_ce_set_next_event (unsigned long delta ,
6577 struct clock_event_device * evt )
6678{
@@ -76,8 +88,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
7688{
7789 hv_set_register (HV_REGISTER_STIMER0_COUNT , 0 );
7890 hv_set_register (HV_REGISTER_STIMER0_CONFIG , 0 );
79- if (direct_mode_enabled )
80- hv_disable_stimer0_percpu_irq (stimer0_irq );
91+ if (direct_mode_enabled && stimer0_irq >= 0 )
92+ disable_percpu_irq (stimer0_irq );
8193
8294 return 0 ;
8395}
@@ -95,8 +107,9 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
95107 * on the specified hardware vector/IRQ.
96108 */
97109 timer_cfg .direct_mode = 1 ;
98- timer_cfg .apic_vector = stimer0_vector ;
99- hv_enable_stimer0_percpu_irq (stimer0_irq );
110+ timer_cfg .apic_vector = HYPERV_STIMER0_VECTOR ;
111+ if (stimer0_irq >= 0 )
112+ enable_percpu_irq (stimer0_irq , IRQ_TYPE_NONE );
100113 } else {
101114 /*
102115 * When it expires, the timer will generate a VMbus message,
@@ -169,10 +182,58 @@ int hv_stimer_cleanup(unsigned int cpu)
169182}
170183EXPORT_SYMBOL_GPL (hv_stimer_cleanup );
171184
185+ /*
186+ * These placeholders are overridden by arch specific code on
187+ * architectures that need special setup of the stimer0 IRQ because
188+ * they don't support per-cpu IRQs (such as x86/x64).
189+ */
190+ void __weak hv_setup_stimer0_handler (void (* handler )(void ))
191+ {
192+ };
193+
194+ void __weak hv_remove_stimer0_handler (void )
195+ {
196+ };
197+
198+ /* Called only on architectures with per-cpu IRQs (i.e., not x86/x64) */
199+ static int hv_setup_stimer0_irq (void )
200+ {
201+ int ret ;
202+
203+ ret = acpi_register_gsi (NULL , HYPERV_STIMER0_VECTOR ,
204+ ACPI_EDGE_SENSITIVE , ACPI_ACTIVE_HIGH );
205+ if (ret < 0 ) {
206+ pr_err ("Can't register Hyper-V stimer0 GSI. Error %d" , ret );
207+ return ret ;
208+ }
209+ stimer0_irq = ret ;
210+
211+ ret = request_percpu_irq (stimer0_irq , hv_stimer0_percpu_isr ,
212+ "Hyper-V stimer0" , & stimer0_evt );
213+ if (ret ) {
214+ pr_err ("Can't request Hyper-V stimer0 IRQ %d. Error %d" ,
215+ stimer0_irq , ret );
216+ acpi_unregister_gsi (stimer0_irq );
217+ stimer0_irq = -1 ;
218+ }
219+ return ret ;
220+ }
221+
222+ static void hv_remove_stimer0_irq (void )
223+ {
224+ if (stimer0_irq == -1 ) {
225+ hv_remove_stimer0_handler ();
226+ } else {
227+ free_percpu_irq (stimer0_irq , & stimer0_evt );
228+ acpi_unregister_gsi (stimer0_irq );
229+ stimer0_irq = -1 ;
230+ }
231+ }
232+
172233/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
173- int hv_stimer_alloc (void )
234+ int hv_stimer_alloc (bool have_percpu_irqs )
174235{
175- int ret = 0 ;
236+ int ret ;
176237
177238 /*
178239 * Synthetic timers are always available except on old versions of
@@ -188,29 +249,37 @@ int hv_stimer_alloc(void)
188249
189250 direct_mode_enabled = ms_hyperv .misc_features &
190251 HV_STIMER_DIRECT_MODE_AVAILABLE ;
191- if (direct_mode_enabled ) {
192- ret = hv_setup_stimer0_irq (& stimer0_irq , & stimer0_vector ,
193- hv_stimer0_isr );
252+
253+ /*
254+ * If Direct Mode isn't enabled, the remainder of the initialization
255+ * is done later by hv_stimer_legacy_init()
256+ */
257+ if (!direct_mode_enabled )
258+ return 0 ;
259+
260+ if (have_percpu_irqs ) {
261+ ret = hv_setup_stimer0_irq ();
194262 if (ret )
195- goto free_percpu ;
263+ goto free_clock_event ;
264+ } else {
265+ hv_setup_stimer0_handler (hv_stimer0_isr );
266+ }
196267
197- /*
198- * Since we are in Direct Mode, stimer initialization
199- * can be done now with a CPUHP value in the same range
200- * as other clockevent devices.
201- */
202- ret = cpuhp_setup_state (CPUHP_AP_HYPERV_TIMER_STARTING ,
203- "clockevents/hyperv/stimer:starting" ,
204- hv_stimer_init , hv_stimer_cleanup );
205- if (ret < 0 )
206- goto free_stimer0_irq ;
268+ /*
269+ * Since we are in Direct Mode, stimer initialization
270+ * can be done now with a CPUHP value in the same range
271+ * as other clockevent devices.
272+ */
273+ ret = cpuhp_setup_state (CPUHP_AP_HYPERV_TIMER_STARTING ,
274+ "clockevents/hyperv/stimer:starting" ,
275+ hv_stimer_init , hv_stimer_cleanup );
276+ if (ret < 0 ) {
277+ hv_remove_stimer0_irq ();
278+ goto free_clock_event ;
207279 }
208280 return ret ;
209281
210- free_stimer0_irq :
211- hv_remove_stimer0_irq (stimer0_irq );
212- stimer0_irq = 0 ;
213- free_percpu :
282+ free_clock_event :
214283 free_percpu (hv_clock_event );
215284 hv_clock_event = NULL ;
216285 return ret ;
@@ -254,23 +323,6 @@ void hv_stimer_legacy_cleanup(unsigned int cpu)
254323}
255324EXPORT_SYMBOL_GPL (hv_stimer_legacy_cleanup );
256325
257-
258- /* hv_stimer_free - Free global resources allocated by hv_stimer_alloc() */
259- void hv_stimer_free (void )
260- {
261- if (!hv_clock_event )
262- return ;
263-
264- if (direct_mode_enabled ) {
265- cpuhp_remove_state (CPUHP_AP_HYPERV_TIMER_STARTING );
266- hv_remove_stimer0_irq (stimer0_irq );
267- stimer0_irq = 0 ;
268- }
269- free_percpu (hv_clock_event );
270- hv_clock_event = NULL ;
271- }
272- EXPORT_SYMBOL_GPL (hv_stimer_free );
273-
274326/*
275327 * Do a global cleanup of clockevents for the cases of kexec and
276328 * vmbus exit
@@ -287,12 +339,17 @@ void hv_stimer_global_cleanup(void)
287339 hv_stimer_legacy_cleanup (cpu );
288340 }
289341
290- /*
291- * If Direct Mode is enabled, the cpuhp teardown callback
292- * (hv_stimer_cleanup) will be run on all CPUs to stop the
293- * stimers.
294- */
295- hv_stimer_free ();
342+ if (!hv_clock_event )
343+ return ;
344+
345+ if (direct_mode_enabled ) {
346+ cpuhp_remove_state (CPUHP_AP_HYPERV_TIMER_STARTING );
347+ hv_remove_stimer0_irq ();
348+ stimer0_irq = -1 ;
349+ }
350+ free_percpu (hv_clock_event );
351+ hv_clock_event = NULL ;
352+
296353}
297354EXPORT_SYMBOL_GPL (hv_stimer_global_cleanup );
298355
@@ -454,9 +511,14 @@ static bool __init hv_init_tsc_clocksource(void)
454511 * Hyper-V Reference TSC rating, causing the generic TSC to be used.
455512 * TSC_INVARIANT is not offered on ARM64, so the Hyper-V Reference
456513 * TSC will be preferred over the virtualized ARM64 arch counter.
514+ * While the Hyper-V MSR clocksource won't be used since the
515+ * Reference TSC clocksource is present, change its rating as
516+ * well for consistency.
457517 */
458- if (ms_hyperv .features & HV_ACCESS_TSC_INVARIANT )
518+ if (ms_hyperv .features & HV_ACCESS_TSC_INVARIANT ) {
459519 hyperv_cs_tsc .rating = 250 ;
520+ hyperv_cs_msr .rating = 250 ;
521+ }
460522
461523 hv_read_reference_counter = read_hv_clock_tsc ;
462524 phys_addr = virt_to_phys (hv_get_tsc_page ());
0 commit comments