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

clocksource/drivers: Make Hyper-V clocksource ISA agnostic

Hyper-V clock/timer code and data structures are currently mixed
in with other code in the ISA independent drivers/hv directory as
well as the ISA dependent Hyper-V code under arch/x86.

Consolidate this code and data structures into a Hyper-V clocksource driver
to better follow the Linux model. In doing so, separate out the ISA
dependent portions so the new clocksource driver works for x86 and for the
in-process Hyper-V on ARM64 code.

To start, move the existing clockevents code to create the new clocksource
driver. Update the VMbus driver to call initialization and cleanup routines
since the Hyper-V synthetic timers are not independently enumerated in
ACPI.

No behavior is changed and no new functionality is added.

Suggested-by: Marc Zyngier <marc.zyngier@arm.com>
Signed-off-by: Michael Kelley <mikelley@microsoft.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Reviewed-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: "bp@alien8.de" <bp@alien8.de>
Cc: "will.deacon@arm.com" <will.deacon@arm.com>
Cc: "catalin.marinas@arm.com" <catalin.marinas@arm.com>
Cc: "mark.rutland@arm.com" <mark.rutland@arm.com>
Cc: "linux-arm-kernel@lists.infradead.org" <linux-arm-kernel@lists.infradead.org>
Cc: "gregkh@linuxfoundation.org" <gregkh@linuxfoundation.org>
Cc: "linux-hyperv@vger.kernel.org" <linux-hyperv@vger.kernel.org>
Cc: "olaf@aepfle.de" <olaf@aepfle.de>
Cc: "apw@canonical.com" <apw@canonical.com>
Cc: "jasowang@redhat.com" <jasowang@redhat.com>
Cc: "marcelo.cerri@canonical.com" <marcelo.cerri@canonical.com>
Cc: Sunil Muthuswamy <sunilmut@microsoft.com>
Cc: KY Srinivasan <kys@microsoft.com>
Cc: "sashal@kernel.org" <sashal@kernel.org>
Cc: "vincenzo.frascino@arm.com" <vincenzo.frascino@arm.com>
Cc: "linux-arch@vger.kernel.org" <linux-arch@vger.kernel.org>
Cc: "linux-mips@vger.kernel.org" <linux-mips@vger.kernel.org>
Cc: "linux-kselftest@vger.kernel.org" <linux-kselftest@vger.kernel.org>
Cc: "arnd@arndb.de" <arnd@arndb.de>
Cc: "linux@armlinux.org.uk" <linux@armlinux.org.uk>
Cc: "ralf@linux-mips.org" <ralf@linux-mips.org>
Cc: "paul.burton@mips.com" <paul.burton@mips.com>
Cc: "daniel.lezcano@linaro.org" <daniel.lezcano@linaro.org>
Cc: "salyzyn@android.com" <salyzyn@android.com>
Cc: "pcc@google.com" <pcc@google.com>
Cc: "shuah@kernel.org" <shuah@kernel.org>
Cc: "0x7f454c46@gmail.com" <0x7f454c46@gmail.com>
Cc: "linux@rasmusvillemoes.dk" <linux@rasmusvillemoes.dk>
Cc: "huw@codeweavers.com" <huw@codeweavers.com>
Cc: "sfr@canb.auug.org.au" <sfr@canb.auug.org.au>
Cc: "pbonzini@redhat.com" <pbonzini@redhat.com>
Cc: "rkrcmar@redhat.com" <rkrcmar@redhat.com>
Cc: "kvm@vger.kernel.org" <kvm@vger.kernel.org>
Link: https://lkml.kernel.org/r/1561955054-1838-2-git-send-email-mikelley@microsoft.com

authored by

Michael Kelley and committed by
Thomas Gleixner
fd1fea68 34192404

+268 -176
+2
MAINTAINERS
··· 7313 7313 F: arch/x86/include/asm/hyperv-tlfs.h 7314 7314 F: arch/x86/kernel/cpu/mshyperv.c 7315 7315 F: arch/x86/hyperv 7316 + F: drivers/clocksource/hyperv_timer.c 7316 7317 F: drivers/hid/hid-hyperv.c 7317 7318 F: drivers/hv/ 7318 7319 F: drivers/input/serio/hyperv-keyboard.c ··· 7324 7323 F: drivers/video/fbdev/hyperv_fb.c 7325 7324 F: drivers/iommu/hyperv_iommu.c 7326 7325 F: net/vmw_vsock/hyperv_transport.c 7326 + F: include/clocksource/hyperv_timer.h 7327 7327 F: include/linux/hyperv.h 7328 7328 F: include/uapi/linux/hyperv.h 7329 7329 F: tools/hv/
+6
arch/x86/include/asm/hyperv-tlfs.h
··· 401 401 #define HV_STATUS_INVALID_CONNECTION_ID 18 402 402 #define HV_STATUS_INSUFFICIENT_BUFFERS 19 403 403 404 + /* 405 + * The Hyper-V TimeRefCount register and the TSC 406 + * page provide a guest VM clock with 100ns tick rate 407 + */ 408 + #define HV_CLOCK_HZ (NSEC_PER_SEC/100) 409 + 404 410 typedef struct _HV_REFERENCE_TSC_PAGE { 405 411 __u32 tsc_sequence; 406 412 __u32 res1;
+3 -1
arch/x86/kernel/cpu/mshyperv.c
··· 17 17 #include <linux/irq.h> 18 18 #include <linux/kexec.h> 19 19 #include <linux/i8253.h> 20 + #include <linux/random.h> 20 21 #include <asm/processor.h> 21 22 #include <asm/hypervisor.h> 22 23 #include <asm/hyperv-tlfs.h> ··· 81 80 inc_irq_stat(hyperv_stimer0_count); 82 81 if (hv_stimer0_handler) 83 82 hv_stimer0_handler(); 83 + add_interrupt_randomness(HYPERV_STIMER0_VECTOR, 0); 84 84 ack_APIC_irq(); 85 85 86 86 exiting_irq(); ··· 91 89 int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)) 92 90 { 93 91 *vector = HYPERV_STIMER0_VECTOR; 94 - *irq = 0; /* Unused on x86/x64 */ 92 + *irq = -1; /* Unused on x86/x64 */ 95 93 hv_stimer0_handler = handler; 96 94 return 0; 97 95 }
+1
drivers/clocksource/Makefile
··· 86 86 obj-$(CONFIG_RISCV_TIMER) += timer-riscv.o 87 87 obj-$(CONFIG_CSKY_MP_TIMER) += timer-mp-csky.o 88 88 obj-$(CONFIG_GX6605S_TIMER) += timer-gx6605s.o 89 + obj-$(CONFIG_HYPERV_TIMER) += hyperv_timer.o
+200
drivers/clocksource/hyperv_timer.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + /* 4 + * Clocksource driver for the synthetic counter and timers 5 + * provided by the Hyper-V hypervisor to guest VMs, as described 6 + * in the Hyper-V Top Level Functional Spec (TLFS). This driver 7 + * is instruction set architecture independent. 8 + * 9 + * Copyright (C) 2019, Microsoft, Inc. 10 + * 11 + * Author: Michael Kelley <mikelley@microsoft.com> 12 + */ 13 + 14 + #include <linux/percpu.h> 15 + #include <linux/cpumask.h> 16 + #include <linux/clockchips.h> 17 + #include <linux/mm.h> 18 + #include <clocksource/hyperv_timer.h> 19 + #include <asm/hyperv-tlfs.h> 20 + #include <asm/mshyperv.h> 21 + 22 + static struct clock_event_device __percpu *hv_clock_event; 23 + 24 + /* 25 + * If false, we're using the old mechanism for stimer0 interrupts 26 + * where it sends a VMbus message when it expires. The old 27 + * mechanism is used when running on older versions of Hyper-V 28 + * that don't support Direct Mode. While Hyper-V provides 29 + * four stimer's per CPU, Linux uses only stimer0. 30 + */ 31 + static bool direct_mode_enabled; 32 + 33 + static int stimer0_irq; 34 + static int stimer0_vector; 35 + static int stimer0_message_sint; 36 + 37 + /* 38 + * ISR for when stimer0 is operating in Direct Mode. Direct Mode 39 + * does not use VMbus or any VMbus messages, so process here and not 40 + * in the VMbus driver code. 41 + */ 42 + void hv_stimer0_isr(void) 43 + { 44 + struct clock_event_device *ce; 45 + 46 + ce = this_cpu_ptr(hv_clock_event); 47 + ce->event_handler(ce); 48 + } 49 + EXPORT_SYMBOL_GPL(hv_stimer0_isr); 50 + 51 + static int hv_ce_set_next_event(unsigned long delta, 52 + struct clock_event_device *evt) 53 + { 54 + u64 current_tick; 55 + 56 + current_tick = hyperv_cs->read(NULL); 57 + current_tick += delta; 58 + hv_init_timer(0, current_tick); 59 + return 0; 60 + } 61 + 62 + static int hv_ce_shutdown(struct clock_event_device *evt) 63 + { 64 + hv_init_timer(0, 0); 65 + hv_init_timer_config(0, 0); 66 + if (direct_mode_enabled) 67 + hv_disable_stimer0_percpu_irq(stimer0_irq); 68 + 69 + return 0; 70 + } 71 + 72 + static int hv_ce_set_oneshot(struct clock_event_device *evt) 73 + { 74 + union hv_stimer_config timer_cfg; 75 + 76 + timer_cfg.as_uint64 = 0; 77 + timer_cfg.enable = 1; 78 + timer_cfg.auto_enable = 1; 79 + if (direct_mode_enabled) { 80 + /* 81 + * When it expires, the timer will directly interrupt 82 + * on the specified hardware vector/IRQ. 83 + */ 84 + timer_cfg.direct_mode = 1; 85 + timer_cfg.apic_vector = stimer0_vector; 86 + hv_enable_stimer0_percpu_irq(stimer0_irq); 87 + } else { 88 + /* 89 + * When it expires, the timer will generate a VMbus message, 90 + * to be handled by the normal VMbus interrupt handler. 91 + */ 92 + timer_cfg.direct_mode = 0; 93 + timer_cfg.sintx = stimer0_message_sint; 94 + } 95 + hv_init_timer_config(0, timer_cfg.as_uint64); 96 + return 0; 97 + } 98 + 99 + /* 100 + * hv_stimer_init - Per-cpu initialization of the clockevent 101 + */ 102 + void hv_stimer_init(unsigned int cpu) 103 + { 104 + struct clock_event_device *ce; 105 + 106 + /* 107 + * Synthetic timers are always available except on old versions of 108 + * Hyper-V on x86. In that case, just return as Linux will use a 109 + * clocksource based on emulated PIT or LAPIC timer hardware. 110 + */ 111 + if (!(ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE)) 112 + return; 113 + 114 + ce = per_cpu_ptr(hv_clock_event, cpu); 115 + ce->name = "Hyper-V clockevent"; 116 + ce->features = CLOCK_EVT_FEAT_ONESHOT; 117 + ce->cpumask = cpumask_of(cpu); 118 + ce->rating = 1000; 119 + ce->set_state_shutdown = hv_ce_shutdown; 120 + ce->set_state_oneshot = hv_ce_set_oneshot; 121 + ce->set_next_event = hv_ce_set_next_event; 122 + 123 + clockevents_config_and_register(ce, 124 + HV_CLOCK_HZ, 125 + HV_MIN_DELTA_TICKS, 126 + HV_MAX_MAX_DELTA_TICKS); 127 + } 128 + EXPORT_SYMBOL_GPL(hv_stimer_init); 129 + 130 + /* 131 + * hv_stimer_cleanup - Per-cpu cleanup of the clockevent 132 + */ 133 + void hv_stimer_cleanup(unsigned int cpu) 134 + { 135 + struct clock_event_device *ce; 136 + 137 + /* Turn off clockevent device */ 138 + if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) { 139 + ce = per_cpu_ptr(hv_clock_event, cpu); 140 + hv_ce_shutdown(ce); 141 + } 142 + } 143 + EXPORT_SYMBOL_GPL(hv_stimer_cleanup); 144 + 145 + /* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */ 146 + int hv_stimer_alloc(int sint) 147 + { 148 + int ret; 149 + 150 + hv_clock_event = alloc_percpu(struct clock_event_device); 151 + if (!hv_clock_event) 152 + return -ENOMEM; 153 + 154 + direct_mode_enabled = ms_hyperv.misc_features & 155 + HV_STIMER_DIRECT_MODE_AVAILABLE; 156 + if (direct_mode_enabled) { 157 + ret = hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector, 158 + hv_stimer0_isr); 159 + if (ret) { 160 + free_percpu(hv_clock_event); 161 + hv_clock_event = NULL; 162 + return ret; 163 + } 164 + } 165 + 166 + stimer0_message_sint = sint; 167 + return 0; 168 + } 169 + EXPORT_SYMBOL_GPL(hv_stimer_alloc); 170 + 171 + /* hv_stimer_free - Free global resources allocated by hv_stimer_alloc() */ 172 + void hv_stimer_free(void) 173 + { 174 + if (direct_mode_enabled && (stimer0_irq != 0)) { 175 + hv_remove_stimer0_irq(stimer0_irq); 176 + stimer0_irq = 0; 177 + } 178 + free_percpu(hv_clock_event); 179 + hv_clock_event = NULL; 180 + } 181 + EXPORT_SYMBOL_GPL(hv_stimer_free); 182 + 183 + /* 184 + * Do a global cleanup of clockevents for the cases of kexec and 185 + * vmbus exit 186 + */ 187 + void hv_stimer_global_cleanup(void) 188 + { 189 + int cpu; 190 + struct clock_event_device *ce; 191 + 192 + if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) { 193 + for_each_present_cpu(cpu) { 194 + ce = per_cpu_ptr(hv_clock_event, cpu); 195 + clockevents_unbind_device(ce, cpu); 196 + } 197 + } 198 + hv_stimer_free(); 199 + } 200 + EXPORT_SYMBOL_GPL(hv_stimer_global_cleanup);
+3
drivers/hv/Kconfig
··· 10 10 Select this option to run Linux as a Hyper-V client operating 11 11 system. 12 12 13 + config HYPERV_TIMER 14 + def_bool HYPERV 15 + 13 16 config HYPERV_TSCPAGE 14 17 def_bool HYPERV && X86_64 15 18
+4 -152
drivers/hv/hv.c
··· 16 16 #include <linux/version.h> 17 17 #include <linux/random.h> 18 18 #include <linux/clockchips.h> 19 + #include <clocksource/hyperv_timer.h> 19 20 #include <asm/mshyperv.h> 20 21 #include "hyperv_vmbus.h" 21 22 22 23 /* The one and only */ 23 24 struct hv_context hv_context; 24 - 25 - /* 26 - * If false, we're using the old mechanism for stimer0 interrupts 27 - * where it sends a VMbus message when it expires. The old 28 - * mechanism is used when running on older versions of Hyper-V 29 - * that don't support Direct Mode. While Hyper-V provides 30 - * four stimer's per CPU, Linux uses only stimer0. 31 - */ 32 - static bool direct_mode_enabled; 33 - static int stimer0_irq; 34 - static int stimer0_vector; 35 - 36 - #define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ 37 - #define HV_MAX_MAX_DELTA_TICKS 0xffffffff 38 - #define HV_MIN_DELTA_TICKS 1 39 25 40 26 /* 41 27 * hv_init - Main initialization routine. ··· 33 47 hv_context.cpu_context = alloc_percpu(struct hv_per_cpu_context); 34 48 if (!hv_context.cpu_context) 35 49 return -ENOMEM; 36 - 37 - direct_mode_enabled = ms_hyperv.misc_features & 38 - HV_STIMER_DIRECT_MODE_AVAILABLE; 39 50 return 0; 40 51 } 41 52 ··· 71 88 return status & 0xFFFF; 72 89 } 73 90 74 - /* 75 - * ISR for when stimer0 is operating in Direct Mode. Direct Mode 76 - * does not use VMbus or any VMbus messages, so process here and not 77 - * in the VMbus driver code. 78 - */ 79 - 80 - static void hv_stimer0_isr(void) 81 - { 82 - struct hv_per_cpu_context *hv_cpu; 83 - 84 - hv_cpu = this_cpu_ptr(hv_context.cpu_context); 85 - hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt); 86 - add_interrupt_randomness(stimer0_vector, 0); 87 - } 88 - 89 - static int hv_ce_set_next_event(unsigned long delta, 90 - struct clock_event_device *evt) 91 - { 92 - u64 current_tick; 93 - 94 - WARN_ON(!clockevent_state_oneshot(evt)); 95 - 96 - current_tick = hyperv_cs->read(NULL); 97 - current_tick += delta; 98 - hv_init_timer(0, current_tick); 99 - return 0; 100 - } 101 - 102 - static int hv_ce_shutdown(struct clock_event_device *evt) 103 - { 104 - hv_init_timer(0, 0); 105 - hv_init_timer_config(0, 0); 106 - if (direct_mode_enabled) 107 - hv_disable_stimer0_percpu_irq(stimer0_irq); 108 - 109 - return 0; 110 - } 111 - 112 - static int hv_ce_set_oneshot(struct clock_event_device *evt) 113 - { 114 - union hv_stimer_config timer_cfg; 115 - 116 - timer_cfg.as_uint64 = 0; 117 - timer_cfg.enable = 1; 118 - timer_cfg.auto_enable = 1; 119 - if (direct_mode_enabled) { 120 - /* 121 - * When it expires, the timer will directly interrupt 122 - * on the specified hardware vector/IRQ. 123 - */ 124 - timer_cfg.direct_mode = 1; 125 - timer_cfg.apic_vector = stimer0_vector; 126 - hv_enable_stimer0_percpu_irq(stimer0_irq); 127 - } else { 128 - /* 129 - * When it expires, the timer will generate a VMbus message, 130 - * to be handled by the normal VMbus interrupt handler. 131 - */ 132 - timer_cfg.direct_mode = 0; 133 - timer_cfg.sintx = VMBUS_MESSAGE_SINT; 134 - } 135 - hv_init_timer_config(0, timer_cfg.as_uint64); 136 - return 0; 137 - } 138 - 139 - static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) 140 - { 141 - dev->name = "Hyper-V clockevent"; 142 - dev->features = CLOCK_EVT_FEAT_ONESHOT; 143 - dev->cpumask = cpumask_of(cpu); 144 - dev->rating = 1000; 145 - /* 146 - * Avoid settint dev->owner = THIS_MODULE deliberately as doing so will 147 - * result in clockevents_config_and_register() taking additional 148 - * references to the hv_vmbus module making it impossible to unload. 149 - */ 150 - 151 - dev->set_state_shutdown = hv_ce_shutdown; 152 - dev->set_state_oneshot = hv_ce_set_oneshot; 153 - dev->set_next_event = hv_ce_set_next_event; 154 - } 155 - 156 - 157 91 int hv_synic_alloc(void) 158 92 { 159 93 int cpu; ··· 99 199 tasklet_init(&hv_cpu->msg_dpc, 100 200 vmbus_on_msg_dpc, (unsigned long) hv_cpu); 101 201 102 - hv_cpu->clk_evt = kzalloc(sizeof(struct clock_event_device), 103 - GFP_KERNEL); 104 - if (hv_cpu->clk_evt == NULL) { 105 - pr_err("Unable to allocate clock event device\n"); 106 - goto err; 107 - } 108 - hv_init_clockevent_device(hv_cpu->clk_evt, cpu); 109 - 110 202 hv_cpu->synic_message_page = 111 203 (void *)get_zeroed_page(GFP_ATOMIC); 112 204 if (hv_cpu->synic_message_page == NULL) { ··· 121 229 INIT_LIST_HEAD(&hv_cpu->chan_list); 122 230 } 123 231 124 - if (direct_mode_enabled && 125 - hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector, 126 - hv_stimer0_isr)) 127 - goto err; 128 - 129 232 return 0; 130 233 err: 131 234 /* ··· 139 252 struct hv_per_cpu_context *hv_cpu 140 253 = per_cpu_ptr(hv_context.cpu_context, cpu); 141 254 142 - kfree(hv_cpu->clk_evt); 143 255 free_page((unsigned long)hv_cpu->synic_event_page); 144 256 free_page((unsigned long)hv_cpu->synic_message_page); 145 257 free_page((unsigned long)hv_cpu->post_msg_page); ··· 197 311 198 312 hv_set_synic_state(sctrl.as_uint64); 199 313 200 - /* 201 - * Register the per-cpu clockevent source. 202 - */ 203 - if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) 204 - clockevents_config_and_register(hv_cpu->clk_evt, 205 - HV_TIMER_FREQUENCY, 206 - HV_MIN_DELTA_TICKS, 207 - HV_MAX_MAX_DELTA_TICKS); 314 + hv_stimer_init(cpu); 315 + 208 316 return 0; 209 - } 210 - 211 - /* 212 - * hv_synic_clockevents_cleanup - Cleanup clockevent devices 213 - */ 214 - void hv_synic_clockevents_cleanup(void) 215 - { 216 - int cpu; 217 - 218 - if (!(ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE)) 219 - return; 220 - 221 - if (direct_mode_enabled) 222 - hv_remove_stimer0_irq(stimer0_irq); 223 - 224 - for_each_present_cpu(cpu) { 225 - struct hv_per_cpu_context *hv_cpu 226 - = per_cpu_ptr(hv_context.cpu_context, cpu); 227 - 228 - clockevents_unbind_device(hv_cpu->clk_evt, cpu); 229 - } 230 317 } 231 318 232 319 /* ··· 247 388 if (channel_found && vmbus_connection.conn_state == CONNECTED) 248 389 return -EBUSY; 249 390 250 - /* Turn off clockevent device */ 251 - if (ms_hyperv.features & HV_MSR_SYNTIMER_AVAILABLE) { 252 - struct hv_per_cpu_context *hv_cpu 253 - = this_cpu_ptr(hv_context.cpu_context); 254 - 255 - clockevents_unbind_device(hv_cpu->clk_evt, cpu); 256 - hv_ce_shutdown(hv_cpu->clk_evt); 257 - } 391 + hv_stimer_cleanup(cpu); 258 392 259 393 hv_get_synint_state(VMBUS_MESSAGE_SINT, shared_sint.as_uint64); 260 394
-3
drivers/hv/hyperv_vmbus.h
··· 138 138 * per-cpu list of the channels based on their CPU affinity. 139 139 */ 140 140 struct list_head chan_list; 141 - struct clock_event_device *clk_evt; 142 141 }; 143 142 144 143 struct hv_context { ··· 174 175 extern int hv_synic_init(unsigned int cpu); 175 176 176 177 extern int hv_synic_cleanup(unsigned int cpu); 177 - 178 - extern void hv_synic_clockevents_cleanup(void); 179 178 180 179 /* Interface */ 181 180
+22 -20
drivers/hv/vmbus_drv.c
··· 30 30 #include <linux/kdebug.h> 31 31 #include <linux/efi.h> 32 32 #include <linux/random.h> 33 + #include <clocksource/hyperv_timer.h> 33 34 #include "hyperv_vmbus.h" 34 35 35 36 struct vmbus_dynid { ··· 956 955 kfree(ctx); 957 956 } 958 957 959 - static void hv_process_timer_expiration(struct hv_message *msg, 960 - struct hv_per_cpu_context *hv_cpu) 961 - { 962 - struct clock_event_device *dev = hv_cpu->clk_evt; 963 - 964 - if (dev->event_handler) 965 - dev->event_handler(dev); 966 - 967 - vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 968 - } 969 - 970 958 void vmbus_on_msg_dpc(unsigned long data) 971 959 { 972 960 struct hv_per_cpu_context *hv_cpu = (void *)data; ··· 1149 1159 1150 1160 /* Check if there are actual msgs to be processed */ 1151 1161 if (msg->header.message_type != HVMSG_NONE) { 1152 - if (msg->header.message_type == HVMSG_TIMER_EXPIRED) 1153 - hv_process_timer_expiration(msg, hv_cpu); 1154 - else 1162 + if (msg->header.message_type == HVMSG_TIMER_EXPIRED) { 1163 + hv_stimer0_isr(); 1164 + vmbus_signal_eom(msg, HVMSG_TIMER_EXPIRED); 1165 + } else 1155 1166 tasklet_schedule(&hv_cpu->msg_dpc); 1156 1167 } 1157 1168 ··· 1254 1263 ret = hv_synic_alloc(); 1255 1264 if (ret) 1256 1265 goto err_alloc; 1266 + 1267 + ret = hv_stimer_alloc(VMBUS_MESSAGE_SINT); 1268 + if (ret < 0) 1269 + goto err_alloc; 1270 + 1257 1271 /* 1258 - * Initialize the per-cpu interrupt state and 1259 - * connect to the host. 1272 + * Initialize the per-cpu interrupt state and stimer state. 1273 + * Then connect to the host. 1260 1274 */ 1261 1275 ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hyperv/vmbus:online", 1262 1276 hv_synic_init, hv_synic_cleanup); 1263 1277 if (ret < 0) 1264 - goto err_alloc; 1278 + goto err_cpuhp; 1265 1279 hyperv_cpuhp_online = ret; 1266 1280 1267 1281 ret = vmbus_connect(); ··· 1314 1318 1315 1319 err_connect: 1316 1320 cpuhp_remove_state(hyperv_cpuhp_online); 1321 + err_cpuhp: 1322 + hv_stimer_free(); 1317 1323 err_alloc: 1318 1324 hv_synic_free(); 1319 1325 hv_remove_vmbus_irq(); ··· 2062 2064 2063 2065 static void hv_kexec_handler(void) 2064 2066 { 2065 - hv_synic_clockevents_cleanup(); 2067 + hv_stimer_global_cleanup(); 2066 2068 vmbus_initiate_unload(false); 2067 2069 vmbus_connection.conn_state = DISCONNECTED; 2068 2070 /* Make sure conn_state is set as hv_synic_cleanup checks for it */ ··· 2073 2075 2074 2076 static void hv_crash_handler(struct pt_regs *regs) 2075 2077 { 2078 + int cpu; 2079 + 2076 2080 vmbus_initiate_unload(true); 2077 2081 /* 2078 2082 * In crash handler we can't schedule synic cleanup for all CPUs, ··· 2082 2082 * for kdump. 2083 2083 */ 2084 2084 vmbus_connection.conn_state = DISCONNECTED; 2085 - hv_synic_cleanup(smp_processor_id()); 2085 + cpu = smp_processor_id(); 2086 + hv_stimer_cleanup(cpu); 2087 + hv_synic_cleanup(cpu); 2086 2088 hyperv_cleanup(); 2087 2089 }; 2088 2090 ··· 2133 2131 hv_remove_kexec_handler(); 2134 2132 hv_remove_crash_handler(); 2135 2133 vmbus_connection.conn_state = DISCONNECTED; 2136 - hv_synic_clockevents_cleanup(); 2134 + hv_stimer_global_cleanup(); 2137 2135 vmbus_disconnect(); 2138 2136 hv_remove_vmbus_irq(); 2139 2137 for_each_online_cpu(cpu) {
+27
include/clocksource/hyperv_timer.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + 3 + /* 4 + * Definitions for the clocksource provided by the Hyper-V 5 + * hypervisor to guest VMs, as described in the Hyper-V Top 6 + * Level Functional Spec (TLFS). 7 + * 8 + * Copyright (C) 2019, Microsoft, Inc. 9 + * 10 + * Author: Michael Kelley <mikelley@microsoft.com> 11 + */ 12 + 13 + #ifndef __CLKSOURCE_HYPERV_TIMER_H 14 + #define __CLKSOURCE_HYPERV_TIMER_H 15 + 16 + #define HV_MAX_MAX_DELTA_TICKS 0xffffffff 17 + #define HV_MIN_DELTA_TICKS 1 18 + 19 + /* Routines called by the VMbus driver */ 20 + extern int hv_stimer_alloc(int sint); 21 + extern void hv_stimer_free(void); 22 + extern void hv_stimer_init(unsigned int cpu); 23 + extern void hv_stimer_cleanup(unsigned int cpu); 24 + extern void hv_stimer_global_cleanup(void); 25 + extern void hv_stimer0_isr(void); 26 + 27 + #endif