aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Kelley <mhkelley@outlook.com>2018-03-05 00:17:18 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-03-06 12:57:17 -0500
commit248e742a396e7f00b283f1c56e14b1bef6e3ec56 (patch)
tree7c4976f72e563b771ac6b53094cab1e91fe3849c
parentce767047b1b731a1899a528338644f2bfdab8b36 (diff)
Drivers: hv: vmbus: Implement Direct Mode for stimer0
The 2016 version of Hyper-V offers the option to operate the guest VM per-vcpu stimer's in Direct Mode, which means the timer interupts on its own vector rather than queueing a VMbus message. Direct Mode reduces timer processing overhead in both the hypervisor and the guest, and avoids having timer interrupts pollute the VMbus interrupt stream for the synthetic NIC and storage. This patch enables Direct Mode by default on stimer0 when running on a version of Hyper-V that supports it. In prep for coming support of Hyper-V on ARM64, the arch independent portion of the code contains calls to routines that will be populated on ARM64 but are not needed and do nothing on x86. Signed-off-by: Michael Kelley <mikelley@microsoft.com> Signed-off-by: K. Y. Srinivasan <kys@microsoft.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--arch/x86/entry/entry_32.S3
-rw-r--r--arch/x86/entry/entry_64.S3
-rw-r--r--arch/x86/include/asm/hardirq.h1
-rw-r--r--arch/x86/include/asm/irq_vectors.h3
-rw-r--r--arch/x86/include/asm/mshyperv.h13
-rw-r--r--arch/x86/include/uapi/asm/hyperv.h3
-rw-r--r--arch/x86/kernel/cpu/mshyperv.c40
-rw-r--r--arch/x86/kernel/irq.c7
-rw-r--r--drivers/hv/hv.c59
-rw-r--r--drivers/hv/hyperv_vmbus.h4
10 files changed, 131 insertions, 5 deletions
diff --git a/arch/x86/entry/entry_32.S b/arch/x86/entry/entry_32.S
index 6ad064c8cf35..bef8e2b202a8 100644
--- a/arch/x86/entry/entry_32.S
+++ b/arch/x86/entry/entry_32.S
@@ -902,6 +902,9 @@ BUILD_INTERRUPT3(hyperv_callback_vector, HYPERVISOR_CALLBACK_VECTOR,
902BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR, 902BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR,
903 hyperv_reenlightenment_intr) 903 hyperv_reenlightenment_intr)
904 904
905BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR,
906 hv_stimer0_vector_handler)
907
905#endif /* CONFIG_HYPERV */ 908#endif /* CONFIG_HYPERV */
906 909
907ENTRY(page_fault) 910ENTRY(page_fault)
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S
index 805f52703ee3..5a11e324eacc 100644
--- a/arch/x86/entry/entry_64.S
+++ b/arch/x86/entry/entry_64.S
@@ -1135,6 +1135,9 @@ apicinterrupt3 HYPERVISOR_CALLBACK_VECTOR \
1135 1135
1136apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \ 1136apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \
1137 hyperv_reenlightenment_vector hyperv_reenlightenment_intr 1137 hyperv_reenlightenment_vector hyperv_reenlightenment_intr
1138
1139apicinterrupt3 HYPERV_STIMER0_VECTOR \
1140 hv_stimer0_callback_vector hv_stimer0_vector_handler
1138#endif /* CONFIG_HYPERV */ 1141#endif /* CONFIG_HYPERV */
1139 1142
1140idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK 1143idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK
diff --git a/arch/x86/include/asm/hardirq.h b/arch/x86/include/asm/hardirq.h
index 7c341a74ec8c..5ea2afd4c871 100644
--- a/arch/x86/include/asm/hardirq.h
+++ b/arch/x86/include/asm/hardirq.h
@@ -40,6 +40,7 @@ typedef struct {
40#endif 40#endif
41#if IS_ENABLED(CONFIG_HYPERV) 41#if IS_ENABLED(CONFIG_HYPERV)
42 unsigned int irq_hv_reenlightenment_count; 42 unsigned int irq_hv_reenlightenment_count;
43 unsigned int hyperv_stimer0_count;
43#endif 44#endif
44} ____cacheline_aligned irq_cpustat_t; 45} ____cacheline_aligned irq_cpustat_t;
45 46
diff --git a/arch/x86/include/asm/irq_vectors.h b/arch/x86/include/asm/irq_vectors.h
index e71c1120426b..404c5fdff859 100644
--- a/arch/x86/include/asm/irq_vectors.h
+++ b/arch/x86/include/asm/irq_vectors.h
@@ -106,9 +106,10 @@
106 106
107#if IS_ENABLED(CONFIG_HYPERV) 107#if IS_ENABLED(CONFIG_HYPERV)
108#define HYPERV_REENLIGHTENMENT_VECTOR 0xee 108#define HYPERV_REENLIGHTENMENT_VECTOR 0xee
109#define HYPERV_STIMER0_VECTOR 0xed
109#endif 110#endif
110 111
111#define LOCAL_TIMER_VECTOR 0xed 112#define LOCAL_TIMER_VECTOR 0xec
112 113
113#define NR_VECTORS 256 114#define NR_VECTORS 256
114 115
diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h
index 25283f7eb299..e73c4d0c06ad 100644
--- a/arch/x86/include/asm/mshyperv.h
+++ b/arch/x86/include/asm/mshyperv.h
@@ -173,6 +173,19 @@ void hv_remove_kexec_handler(void);
173void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); 173void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
174void hv_remove_crash_handler(void); 174void hv_remove_crash_handler(void);
175 175
176/*
177 * Routines for stimer0 Direct Mode handling.
178 * On x86/x64, there are no percpu actions to take.
179 */
180void hv_stimer0_vector_handler(struct pt_regs *regs);
181void hv_stimer0_callback_vector(void);
182int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void));
183void hv_remove_stimer0_irq(int irq);
184
185static inline void hv_enable_stimer0_percpu_irq(int irq) {}
186static inline void hv_disable_stimer0_percpu_irq(int irq) {}
187
188
176#if IS_ENABLED(CONFIG_HYPERV) 189#if IS_ENABLED(CONFIG_HYPERV)
177extern struct clocksource *hyperv_cs; 190extern struct clocksource *hyperv_cs;
178extern void *hv_hypercall_pg; 191extern void *hv_hypercall_pg;
diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h
index 099414345865..6c0c3a3b631c 100644
--- a/arch/x86/include/uapi/asm/hyperv.h
+++ b/arch/x86/include/uapi/asm/hyperv.h
@@ -77,6 +77,9 @@
77/* Crash MSR available */ 77/* Crash MSR available */
78#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10) 78#define HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE (1 << 10)
79 79
80/* stimer Direct Mode is available */
81#define HV_X64_STIMER_DIRECT_MODE_AVAILABLE (1 << 19)
82
80/* 83/*
81 * Feature identification: EBX indicates which flags were specified at 84 * Feature identification: EBX indicates which flags were specified at
82 * partition creation. The format is the same as the partition creation 85 * partition creation. The format is the same as the partition creation
diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c
index 9340f41ce8d3..4488cf0dd499 100644
--- a/arch/x86/kernel/cpu/mshyperv.c
+++ b/arch/x86/kernel/cpu/mshyperv.c
@@ -37,6 +37,7 @@ EXPORT_SYMBOL_GPL(ms_hyperv);
37 37
38#if IS_ENABLED(CONFIG_HYPERV) 38#if IS_ENABLED(CONFIG_HYPERV)
39static void (*vmbus_handler)(void); 39static void (*vmbus_handler)(void);
40static void (*hv_stimer0_handler)(void);
40static void (*hv_kexec_handler)(void); 41static void (*hv_kexec_handler)(void);
41static void (*hv_crash_handler)(struct pt_regs *regs); 42static void (*hv_crash_handler)(struct pt_regs *regs);
42 43
@@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void)
69EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); 70EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq);
70EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); 71EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq);
71 72
73/*
74 * Routines to do per-architecture handling of stimer0
75 * interrupts when in Direct Mode
76 */
77
78__visible void __irq_entry hv_stimer0_vector_handler(struct pt_regs *regs)
79{
80 struct pt_regs *old_regs = set_irq_regs(regs);
81
82 entering_irq();
83 inc_irq_stat(hyperv_stimer0_count);
84 if (hv_stimer0_handler)
85 hv_stimer0_handler();
86 ack_APIC_irq();
87
88 exiting_irq();
89 set_irq_regs(old_regs);
90}
91
92int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void))
93{
94 *vector = HYPERV_STIMER0_VECTOR;
95 *irq = 0; /* Unused on x86/x64 */
96 hv_stimer0_handler = handler;
97 return 0;
98}
99EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq);
100
101void hv_remove_stimer0_irq(int irq)
102{
103 /* We have no way to deallocate the interrupt gate */
104 hv_stimer0_handler = NULL;
105}
106EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq);
107
72void hv_setup_kexec_handler(void (*handler)(void)) 108void hv_setup_kexec_handler(void (*handler)(void))
73{ 109{
74 hv_kexec_handler = handler; 110 hv_kexec_handler = handler;
@@ -257,6 +293,10 @@ static void __init ms_hyperv_init_platform(void)
257 alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR, 293 alloc_intr_gate(HYPERV_REENLIGHTENMENT_VECTOR,
258 hyperv_reenlightenment_vector); 294 hyperv_reenlightenment_vector);
259 295
296 /* Setup the IDT for stimer0 */
297 if (ms_hyperv.misc_features & HV_X64_STIMER_DIRECT_MODE_AVAILABLE)
298 alloc_intr_gate(HYPERV_STIMER0_VECTOR,
299 hv_stimer0_callback_vector);
260#endif 300#endif
261} 301}
262 302
diff --git a/arch/x86/kernel/irq.c b/arch/x86/kernel/irq.c
index 45fb4d2565f8..328d027d829d 100644
--- a/arch/x86/kernel/irq.c
+++ b/arch/x86/kernel/irq.c
@@ -150,6 +150,13 @@ int arch_show_interrupts(struct seq_file *p, int prec)
150 irq_stats(j)->irq_hv_reenlightenment_count); 150 irq_stats(j)->irq_hv_reenlightenment_count);
151 seq_puts(p, " Hyper-V reenlightenment interrupts\n"); 151 seq_puts(p, " Hyper-V reenlightenment interrupts\n");
152 } 152 }
153 if (test_bit(HYPERV_STIMER0_VECTOR, system_vectors)) {
154 seq_printf(p, "%*s: ", prec, "HVS");
155 for_each_online_cpu(j)
156 seq_printf(p, "%10u ",
157 irq_stats(j)->hyperv_stimer0_count);
158 seq_puts(p, " Hyper-V stimer0 interrupts\n");
159 }
153#endif 160#endif
154 seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count)); 161 seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
155#if defined(CONFIG_X86_IO_APIC) 162#if defined(CONFIG_X86_IO_APIC)
diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c
index 31142b72f1b9..b1f6793acf4c 100644
--- a/drivers/hv/hv.c
+++ b/drivers/hv/hv.c
@@ -27,7 +27,7 @@
27#include <linux/vmalloc.h> 27#include <linux/vmalloc.h>
28#include <linux/hyperv.h> 28#include <linux/hyperv.h>
29#include <linux/version.h> 29#include <linux/version.h>
30#include <linux/interrupt.h> 30#include <linux/random.h>
31#include <linux/clockchips.h> 31#include <linux/clockchips.h>
32#include <asm/hyperv.h> 32#include <asm/hyperv.h>
33#include <asm/mshyperv.h> 33#include <asm/mshyperv.h>
@@ -38,6 +38,17 @@ struct hv_context hv_context = {
38 .synic_initialized = false, 38 .synic_initialized = false,
39}; 39};
40 40
41/*
42 * If false, we're using the old mechanism for stimer0 interrupts
43 * where it sends a VMbus message when it expires. The old
44 * mechanism is used when running on older versions of Hyper-V
45 * that don't support Direct Mode. While Hyper-V provides
46 * four stimer's per CPU, Linux uses only stimer0.
47 */
48static bool direct_mode_enabled;
49static int stimer0_irq;
50static int stimer0_vector;
51
41#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */ 52#define HV_TIMER_FREQUENCY (10 * 1000 * 1000) /* 100ns period */
42#define HV_MAX_MAX_DELTA_TICKS 0xffffffff 53#define HV_MAX_MAX_DELTA_TICKS 0xffffffff
43#define HV_MIN_DELTA_TICKS 1 54#define HV_MIN_DELTA_TICKS 1
@@ -53,6 +64,8 @@ int hv_init(void)
53 if (!hv_context.cpu_context) 64 if (!hv_context.cpu_context)
54 return -ENOMEM; 65 return -ENOMEM;
55 66
67 direct_mode_enabled = ms_hyperv.misc_features &
68 HV_X64_STIMER_DIRECT_MODE_AVAILABLE;
56 return 0; 69 return 0;
57} 70}
58 71
@@ -91,6 +104,21 @@ int hv_post_message(union hv_connection_id connection_id,
91 return status & 0xFFFF; 104 return status & 0xFFFF;
92} 105}
93 106
107/*
108 * ISR for when stimer0 is operating in Direct Mode. Direct Mode
109 * does not use VMbus or any VMbus messages, so process here and not
110 * in the VMbus driver code.
111 */
112
113static void hv_stimer0_isr(void)
114{
115 struct hv_per_cpu_context *hv_cpu;
116
117 hv_cpu = this_cpu_ptr(hv_context.cpu_context);
118 hv_cpu->clk_evt->event_handler(hv_cpu->clk_evt);
119 add_interrupt_randomness(stimer0_vector, 0);
120}
121
94static int hv_ce_set_next_event(unsigned long delta, 122static int hv_ce_set_next_event(unsigned long delta,
95 struct clock_event_device *evt) 123 struct clock_event_device *evt)
96{ 124{
@@ -108,6 +136,8 @@ static int hv_ce_shutdown(struct clock_event_device *evt)
108{ 136{
109 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0); 137 hv_init_timer(HV_X64_MSR_STIMER0_COUNT, 0);
110 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0); 138 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, 0);
139 if (direct_mode_enabled)
140 hv_disable_stimer0_percpu_irq(stimer0_irq);
111 141
112 return 0; 142 return 0;
113} 143}
@@ -116,11 +146,26 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
116{ 146{
117 union hv_timer_config timer_cfg; 147 union hv_timer_config timer_cfg;
118 148
149 timer_cfg.as_uint64 = 0;
119 timer_cfg.enable = 1; 150 timer_cfg.enable = 1;
120 timer_cfg.auto_enable = 1; 151 timer_cfg.auto_enable = 1;
121 timer_cfg.sintx = VMBUS_MESSAGE_SINT; 152 if (direct_mode_enabled) {
153 /*
154 * When it expires, the timer will directly interrupt
155 * on the specified hardware vector/IRQ.
156 */
157 timer_cfg.direct_mode = 1;
158 timer_cfg.apic_vector = stimer0_vector;
159 hv_enable_stimer0_percpu_irq(stimer0_irq);
160 } else {
161 /*
162 * When it expires, the timer will generate a VMbus message,
163 * to be handled by the normal VMbus interrupt handler.
164 */
165 timer_cfg.direct_mode = 0;
166 timer_cfg.sintx = VMBUS_MESSAGE_SINT;
167 }
122 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); 168 hv_init_timer_config(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64);
123
124 return 0; 169 return 0;
125} 170}
126 171
@@ -191,6 +236,11 @@ int hv_synic_alloc(void)
191 INIT_LIST_HEAD(&hv_cpu->chan_list); 236 INIT_LIST_HEAD(&hv_cpu->chan_list);
192 } 237 }
193 238
239 if (direct_mode_enabled &&
240 hv_setup_stimer0_irq(&stimer0_irq, &stimer0_vector,
241 hv_stimer0_isr))
242 goto err;
243
194 return 0; 244 return 0;
195err: 245err:
196 return -ENOMEM; 246 return -ENOMEM;
@@ -292,6 +342,9 @@ void hv_synic_clockevents_cleanup(void)
292 if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE)) 342 if (!(ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE))
293 return; 343 return;
294 344
345 if (direct_mode_enabled)
346 hv_remove_stimer0_irq(stimer0_irq);
347
295 for_each_present_cpu(cpu) { 348 for_each_present_cpu(cpu) {
296 struct hv_per_cpu_context *hv_cpu 349 struct hv_per_cpu_context *hv_cpu
297 = per_cpu_ptr(hv_context.cpu_context, cpu); 350 = per_cpu_ptr(hv_context.cpu_context, cpu);
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index 22300ec7b556..36d34fe3ccb3 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -57,7 +57,9 @@ union hv_timer_config {
57 u64 periodic:1; 57 u64 periodic:1;
58 u64 lazy:1; 58 u64 lazy:1;
59 u64 auto_enable:1; 59 u64 auto_enable:1;
60 u64 reserved_z0:12; 60 u64 apic_vector:8;
61 u64 direct_mode:1;
62 u64 reserved_z0:3;
61 u64 sintx:4; 63 u64 sintx:4;
62 u64 reserved_z1:44; 64 u64 reserved_z1:44;
63 }; 65 };