diff options
-rw-r--r-- | arch/x86/entry/entry_32.S | 3 | ||||
-rw-r--r-- | arch/x86/entry/entry_64.S | 3 | ||||
-rw-r--r-- | arch/x86/include/asm/hardirq.h | 1 | ||||
-rw-r--r-- | arch/x86/include/asm/irq_vectors.h | 3 | ||||
-rw-r--r-- | arch/x86/include/asm/mshyperv.h | 13 | ||||
-rw-r--r-- | arch/x86/include/uapi/asm/hyperv.h | 3 | ||||
-rw-r--r-- | arch/x86/kernel/cpu/mshyperv.c | 40 | ||||
-rw-r--r-- | arch/x86/kernel/irq.c | 7 | ||||
-rw-r--r-- | drivers/hv/hv.c | 59 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 4 |
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, | |||
902 | BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR, | 902 | BUILD_INTERRUPT3(hyperv_reenlightenment_vector, HYPERV_REENLIGHTENMENT_VECTOR, |
903 | hyperv_reenlightenment_intr) | 903 | hyperv_reenlightenment_intr) |
904 | 904 | ||
905 | BUILD_INTERRUPT3(hv_stimer0_callback_vector, HYPERV_STIMER0_VECTOR, | ||
906 | hv_stimer0_vector_handler) | ||
907 | |||
905 | #endif /* CONFIG_HYPERV */ | 908 | #endif /* CONFIG_HYPERV */ |
906 | 909 | ||
907 | ENTRY(page_fault) | 910 | ENTRY(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 | ||
1136 | apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \ | 1136 | apicinterrupt3 HYPERV_REENLIGHTENMENT_VECTOR \ |
1137 | hyperv_reenlightenment_vector hyperv_reenlightenment_intr | 1137 | hyperv_reenlightenment_vector hyperv_reenlightenment_intr |
1138 | |||
1139 | apicinterrupt3 HYPERV_STIMER0_VECTOR \ | ||
1140 | hv_stimer0_callback_vector hv_stimer0_vector_handler | ||
1138 | #endif /* CONFIG_HYPERV */ | 1141 | #endif /* CONFIG_HYPERV */ |
1139 | 1142 | ||
1140 | idtentry debug do_debug has_error_code=0 paranoid=1 shift_ist=DEBUG_STACK | 1143 | idtentry 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); | |||
173 | void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); | 173 | void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); |
174 | void hv_remove_crash_handler(void); | 174 | void 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 | */ | ||
180 | void hv_stimer0_vector_handler(struct pt_regs *regs); | ||
181 | void hv_stimer0_callback_vector(void); | ||
182 | int hv_setup_stimer0_irq(int *irq, int *vector, void (*handler)(void)); | ||
183 | void hv_remove_stimer0_irq(int irq); | ||
184 | |||
185 | static inline void hv_enable_stimer0_percpu_irq(int irq) {} | ||
186 | static inline void hv_disable_stimer0_percpu_irq(int irq) {} | ||
187 | |||
188 | |||
176 | #if IS_ENABLED(CONFIG_HYPERV) | 189 | #if IS_ENABLED(CONFIG_HYPERV) |
177 | extern struct clocksource *hyperv_cs; | 190 | extern struct clocksource *hyperv_cs; |
178 | extern void *hv_hypercall_pg; | 191 | extern 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) |
39 | static void (*vmbus_handler)(void); | 39 | static void (*vmbus_handler)(void); |
40 | static void (*hv_stimer0_handler)(void); | ||
40 | static void (*hv_kexec_handler)(void); | 41 | static void (*hv_kexec_handler)(void); |
41 | static void (*hv_crash_handler)(struct pt_regs *regs); | 42 | static void (*hv_crash_handler)(struct pt_regs *regs); |
42 | 43 | ||
@@ -69,6 +70,41 @@ void hv_remove_vmbus_irq(void) | |||
69 | EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); | 70 | EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); |
70 | EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); | 71 | EXPORT_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 | |||
92 | int 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 | } | ||
99 | EXPORT_SYMBOL_GPL(hv_setup_stimer0_irq); | ||
100 | |||
101 | void hv_remove_stimer0_irq(int irq) | ||
102 | { | ||
103 | /* We have no way to deallocate the interrupt gate */ | ||
104 | hv_stimer0_handler = NULL; | ||
105 | } | ||
106 | EXPORT_SYMBOL_GPL(hv_remove_stimer0_irq); | ||
107 | |||
72 | void hv_setup_kexec_handler(void (*handler)(void)) | 108 | void 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 | */ | ||
48 | static bool direct_mode_enabled; | ||
49 | static int stimer0_irq; | ||
50 | static 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 | |||
113 | static 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 | |||
94 | static int hv_ce_set_next_event(unsigned long delta, | 122 | static 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; |
195 | err: | 245 | err: |
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 | }; |