17
17
#include <linux/clocksource.h>
18
18
#include <linux/sched_clock.h>
19
19
#include <linux/mm.h>
20
+ #include <linux/cpuhotplug.h>
20
21
#include <clocksource/hyperv_timer.h>
21
22
#include <asm/hyperv-tlfs.h>
22
23
#include <asm/mshyperv.h>
@@ -30,6 +31,15 @@ static u64 hv_sched_clock_offset __ro_after_init;
30
31
* mechanism is used when running on older versions of Hyper-V
31
32
* that don't support Direct Mode. While Hyper-V provides
32
33
* four stimer's per CPU, Linux uses only stimer0.
34
+ *
35
+ * Because Direct Mode does not require processing a VMbus
36
+ * message, stimer interrupts can be enabled earlier in the
37
+ * process of booting a CPU, and consistent with when timer
38
+ * interrupts are enabled for other clocksource drivers.
39
+ * However, for legacy versions of Hyper-V when Direct Mode
40
+ * is not enabled, setting up stimer interrupts must be
41
+ * delayed until VMbus is initialized and can process the
42
+ * interrupt message.
33
43
*/
34
44
static bool direct_mode_enabled ;
35
45
@@ -102,17 +112,12 @@ static int hv_ce_set_oneshot(struct clock_event_device *evt)
102
112
/*
103
113
* hv_stimer_init - Per-cpu initialization of the clockevent
104
114
*/
105
- void hv_stimer_init (unsigned int cpu )
115
+ static int hv_stimer_init (unsigned int cpu )
106
116
{
107
117
struct clock_event_device * ce ;
108
118
109
- /*
110
- * Synthetic timers are always available except on old versions of
111
- * Hyper-V on x86. In that case, just return as Linux will use a
112
- * clocksource based on emulated PIT or LAPIC timer hardware.
113
- */
114
- if (!(ms_hyperv .features & HV_MSR_SYNTIMER_AVAILABLE ))
115
- return ;
119
+ if (!hv_clock_event )
120
+ return 0 ;
116
121
117
122
ce = per_cpu_ptr (hv_clock_event , cpu );
118
123
ce -> name = "Hyper-V clockevent" ;
@@ -127,28 +132,55 @@ void hv_stimer_init(unsigned int cpu)
127
132
HV_CLOCK_HZ ,
128
133
HV_MIN_DELTA_TICKS ,
129
134
HV_MAX_MAX_DELTA_TICKS );
135
+ return 0 ;
130
136
}
131
- EXPORT_SYMBOL_GPL (hv_stimer_init );
132
137
133
138
/*
134
139
* hv_stimer_cleanup - Per-cpu cleanup of the clockevent
135
140
*/
136
- void hv_stimer_cleanup (unsigned int cpu )
141
+ int hv_stimer_cleanup (unsigned int cpu )
137
142
{
138
143
struct clock_event_device * ce ;
139
144
140
- /* Turn off clockevent device */
141
- if (ms_hyperv .features & HV_MSR_SYNTIMER_AVAILABLE ) {
142
- ce = per_cpu_ptr (hv_clock_event , cpu );
145
+ if (!hv_clock_event )
146
+ return 0 ;
147
+
148
+ /*
149
+ * In the legacy case where Direct Mode is not enabled
150
+ * (which can only be on x86/64), stimer cleanup happens
151
+ * relatively early in the CPU offlining process. We
152
+ * must unbind the stimer-based clockevent device so
153
+ * that the LAPIC timer can take over until clockevents
154
+ * are no longer needed in the offlining process. Note
155
+ * that clockevents_unbind_device() eventually calls
156
+ * hv_ce_shutdown().
157
+ *
158
+ * The unbind should not be done when Direct Mode is
159
+ * enabled because we may be on an architecture where
160
+ * there are no other clockevent devices to fallback to.
161
+ */
162
+ ce = per_cpu_ptr (hv_clock_event , cpu );
163
+ if (direct_mode_enabled )
143
164
hv_ce_shutdown (ce );
144
- }
165
+ else
166
+ clockevents_unbind_device (ce , cpu );
167
+
168
+ return 0 ;
145
169
}
146
170
EXPORT_SYMBOL_GPL (hv_stimer_cleanup );
147
171
148
172
/* hv_stimer_alloc - Global initialization of the clockevent and stimer0 */
149
- int hv_stimer_alloc (int sint )
173
+ int hv_stimer_alloc (void )
150
174
{
151
- int ret ;
175
+ int ret = 0 ;
176
+
177
+ /*
178
+ * Synthetic timers are always available except on old versions of
179
+ * Hyper-V on x86. In that case, return as error as Linux will use a
180
+ * clockevent based on emulated LAPIC timer hardware.
181
+ */
182
+ if (!(ms_hyperv .features & HV_MSR_SYNTIMER_AVAILABLE ))
183
+ return - EINVAL ;
152
184
153
185
hv_clock_event = alloc_percpu (struct clock_event_device );
154
186
if (!hv_clock_event )
@@ -159,22 +191,78 @@ int hv_stimer_alloc(int sint)
159
191
if (direct_mode_enabled ) {
160
192
ret = hv_setup_stimer0_irq (& stimer0_irq , & stimer0_vector ,
161
193
hv_stimer0_isr );
162
- if (ret ) {
163
- free_percpu (hv_clock_event );
164
- hv_clock_event = NULL ;
165
- return ret ;
166
- }
194
+ if (ret )
195
+ goto free_percpu ;
196
+
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 ;
167
207
}
208
+ return ret ;
168
209
169
- stimer0_message_sint = sint ;
170
- return 0 ;
210
+ free_stimer0_irq :
211
+ hv_remove_stimer0_irq (stimer0_irq );
212
+ stimer0_irq = 0 ;
213
+ free_percpu :
214
+ free_percpu (hv_clock_event );
215
+ hv_clock_event = NULL ;
216
+ return ret ;
171
217
}
172
218
EXPORT_SYMBOL_GPL (hv_stimer_alloc );
173
219
220
+ /*
221
+ * hv_stimer_legacy_init -- Called from the VMbus driver to handle
222
+ * the case when Direct Mode is not enabled, and the stimer
223
+ * must be initialized late in the CPU onlining process.
224
+ *
225
+ */
226
+ void hv_stimer_legacy_init (unsigned int cpu , int sint )
227
+ {
228
+ if (direct_mode_enabled )
229
+ return ;
230
+
231
+ /*
232
+ * This function gets called by each vCPU, so setting the
233
+ * global stimer_message_sint value each time is conceptually
234
+ * not ideal, but the value passed in is always the same and
235
+ * it avoids introducing yet another interface into this
236
+ * clocksource driver just to set the sint in the legacy case.
237
+ */
238
+ stimer0_message_sint = sint ;
239
+ (void )hv_stimer_init (cpu );
240
+ }
241
+ EXPORT_SYMBOL_GPL (hv_stimer_legacy_init );
242
+
243
+ /*
244
+ * hv_stimer_legacy_cleanup -- Called from the VMbus driver to
245
+ * handle the case when Direct Mode is not enabled, and the
246
+ * stimer must be cleaned up early in the CPU offlining
247
+ * process.
248
+ */
249
+ void hv_stimer_legacy_cleanup (unsigned int cpu )
250
+ {
251
+ if (direct_mode_enabled )
252
+ return ;
253
+ (void )hv_stimer_cleanup (cpu );
254
+ }
255
+ EXPORT_SYMBOL_GPL (hv_stimer_legacy_cleanup );
256
+
257
+
174
258
/* hv_stimer_free - Free global resources allocated by hv_stimer_alloc() */
175
259
void hv_stimer_free (void )
176
260
{
177
- if (direct_mode_enabled && (stimer0_irq != 0 )) {
261
+ if (!hv_clock_event )
262
+ return ;
263
+
264
+ if (direct_mode_enabled ) {
265
+ cpuhp_remove_state (CPUHP_AP_HYPERV_TIMER_STARTING );
178
266
hv_remove_stimer0_irq (stimer0_irq );
179
267
stimer0_irq = 0 ;
180
268
}
@@ -190,14 +278,20 @@ EXPORT_SYMBOL_GPL(hv_stimer_free);
190
278
void hv_stimer_global_cleanup (void )
191
279
{
192
280
int cpu ;
193
- struct clock_event_device * ce ;
194
281
195
- if (ms_hyperv .features & HV_MSR_SYNTIMER_AVAILABLE ) {
196
- for_each_present_cpu (cpu ) {
197
- ce = per_cpu_ptr (hv_clock_event , cpu );
198
- clockevents_unbind_device (ce , cpu );
199
- }
282
+ /*
283
+ * hv_stime_legacy_cleanup() will stop the stimer if Direct
284
+ * Mode is not enabled, and fallback to the LAPIC timer.
285
+ */
286
+ for_each_present_cpu (cpu ) {
287
+ hv_stimer_legacy_cleanup (cpu );
200
288
}
289
+
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
+ */
201
295
hv_stimer_free ();
202
296
}
203
297
EXPORT_SYMBOL_GPL (hv_stimer_global_cleanup );
0 commit comments