aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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 };