25
25
#include <linux/init.h>
26
26
#include <linux/export.h>
27
27
#include <linux/clocksource.h>
28
+ #include <linux/cpu.h>
29
+ #include <linux/reboot.h>
28
30
#include <asm/div64.h>
29
31
#include <asm/x86_init.h>
30
32
#include <asm/hypervisor.h>
47
49
#define VMWARE_CMD_GETVCPU_INFO 68
48
50
#define VMWARE_CMD_LEGACY_X2APIC 3
49
51
#define VMWARE_CMD_VCPU_RESERVED 31
52
+ #define VMWARE_CMD_STEALCLOCK 91
53
+
54
+ #define STEALCLOCK_NOT_AVAILABLE (-1)
55
+ #define STEALCLOCK_DISABLED 0
56
+ #define STEALCLOCK_ENABLED 1
50
57
51
58
#define VMWARE_PORT (cmd , eax , ebx , ecx , edx ) \
52
59
__asm__("inl (%%dx), %%eax" : \
86
93
} \
87
94
} while (0)
88
95
96
+ struct vmware_steal_time {
97
+ union {
98
+ uint64_t clock ; /* stolen time counter in units of vtsc */
99
+ struct {
100
+ /* only for little-endian */
101
+ uint32_t clock_low ;
102
+ uint32_t clock_high ;
103
+ };
104
+ };
105
+ uint64_t reserved [7 ];
106
+ };
107
+
89
108
static unsigned long vmware_tsc_khz __ro_after_init ;
90
109
static u8 vmware_hypercall_mode __ro_after_init ;
91
110
@@ -103,15 +122,25 @@ static unsigned long vmware_get_tsc_khz(void)
103
122
104
123
#ifdef CONFIG_PARAVIRT
105
124
static struct cyc2ns_data vmware_cyc2ns __ro_after_init ;
106
- static int vmw_sched_clock __initdata = 1 ;
125
+ static bool vmw_sched_clock __initdata = true;
126
+ static DEFINE_PER_CPU_DECRYPTED (struct vmware_steal_time , vmw_steal_time ) __aligned (64 );
127
+ static bool has_steal_clock ;
128
+ static bool steal_acc __initdata = true; /* steal time accounting */
107
129
108
130
static __init int setup_vmw_sched_clock (char * s )
109
131
{
110
- vmw_sched_clock = 0 ;
132
+ vmw_sched_clock = false ;
111
133
return 0 ;
112
134
}
113
135
early_param ("no-vmw-sched-clock" , setup_vmw_sched_clock );
114
136
137
+ static __init int parse_no_stealacc (char * arg )
138
+ {
139
+ steal_acc = false;
140
+ return 0 ;
141
+ }
142
+ early_param ("no-steal-acc" , parse_no_stealacc );
143
+
115
144
static unsigned long long notrace vmware_sched_clock (void )
116
145
{
117
146
unsigned long long ns ;
@@ -122,7 +151,7 @@ static unsigned long long notrace vmware_sched_clock(void)
122
151
return ns ;
123
152
}
124
153
125
- static void __init vmware_sched_clock_setup (void )
154
+ static void __init vmware_cyc2ns_setup (void )
126
155
{
127
156
struct cyc2ns_data * d = & vmware_cyc2ns ;
128
157
unsigned long long tsc_now = rdtsc ();
@@ -132,17 +161,201 @@ static void __init vmware_sched_clock_setup(void)
132
161
d -> cyc2ns_offset = mul_u64_u32_shr (tsc_now , d -> cyc2ns_mul ,
133
162
d -> cyc2ns_shift );
134
163
135
- pv_ops .time .sched_clock = vmware_sched_clock ;
136
- pr_info ("using sched offset of %llu ns\n" , d -> cyc2ns_offset );
164
+ pr_info ("using clock offset of %llu ns\n" , d -> cyc2ns_offset );
165
+ }
166
+
167
+ static int vmware_cmd_stealclock (uint32_t arg1 , uint32_t arg2 )
168
+ {
169
+ uint32_t result , info ;
170
+
171
+ asm volatile (VMWARE_HYPERCALL :
172
+ "=a" (result ),
173
+ "=c" (info ) :
174
+ "a" (VMWARE_HYPERVISOR_MAGIC ),
175
+ "b" (0 ),
176
+ "c" (VMWARE_CMD_STEALCLOCK ),
177
+ "d" (0 ),
178
+ "S" (arg1 ),
179
+ "D" (arg2 ) :
180
+ "memory" );
181
+ return result ;
182
+ }
183
+
184
+ static bool stealclock_enable (phys_addr_t pa )
185
+ {
186
+ return vmware_cmd_stealclock (upper_32_bits (pa ),
187
+ lower_32_bits (pa )) == STEALCLOCK_ENABLED ;
188
+ }
189
+
190
+ static int __stealclock_disable (void )
191
+ {
192
+ return vmware_cmd_stealclock (0 , 1 );
193
+ }
194
+
195
+ static void stealclock_disable (void )
196
+ {
197
+ __stealclock_disable ();
198
+ }
199
+
200
+ static bool vmware_is_stealclock_available (void )
201
+ {
202
+ return __stealclock_disable () != STEALCLOCK_NOT_AVAILABLE ;
203
+ }
204
+
205
+ /**
206
+ * vmware_steal_clock() - read the per-cpu steal clock
207
+ * @cpu: the cpu number whose steal clock we want to read
208
+ *
209
+ * The function reads the steal clock if we are on a 64-bit system, otherwise
210
+ * reads it in parts, checking that the high part didn't change in the
211
+ * meantime.
212
+ *
213
+ * Return:
214
+ * The steal clock reading in ns.
215
+ */
216
+ static uint64_t vmware_steal_clock (int cpu )
217
+ {
218
+ struct vmware_steal_time * steal = & per_cpu (vmw_steal_time , cpu );
219
+ uint64_t clock ;
220
+
221
+ if (IS_ENABLED (CONFIG_64BIT ))
222
+ clock = READ_ONCE (steal -> clock );
223
+ else {
224
+ uint32_t initial_high , low , high ;
225
+
226
+ do {
227
+ initial_high = READ_ONCE (steal -> clock_high );
228
+ /* Do not reorder initial_high and high readings */
229
+ virt_rmb ();
230
+ low = READ_ONCE (steal -> clock_low );
231
+ /* Keep low reading in between */
232
+ virt_rmb ();
233
+ high = READ_ONCE (steal -> clock_high );
234
+ } while (initial_high != high );
235
+
236
+ clock = ((uint64_t )high << 32 ) | low ;
237
+ }
238
+
239
+ return mul_u64_u32_shr (clock , vmware_cyc2ns .cyc2ns_mul ,
240
+ vmware_cyc2ns .cyc2ns_shift );
241
+ }
242
+
243
+ static void vmware_register_steal_time (void )
244
+ {
245
+ int cpu = smp_processor_id ();
246
+ struct vmware_steal_time * st = & per_cpu (vmw_steal_time , cpu );
247
+
248
+ if (!has_steal_clock )
249
+ return ;
250
+
251
+ if (!stealclock_enable (slow_virt_to_phys (st ))) {
252
+ has_steal_clock = false;
253
+ return ;
254
+ }
255
+
256
+ pr_info ("vmware-stealtime: cpu %d, pa %llx\n" ,
257
+ cpu , (unsigned long long ) slow_virt_to_phys (st ));
137
258
}
138
259
260
+ static void vmware_disable_steal_time (void )
261
+ {
262
+ if (!has_steal_clock )
263
+ return ;
264
+
265
+ stealclock_disable ();
266
+ }
267
+
268
+ static void vmware_guest_cpu_init (void )
269
+ {
270
+ if (has_steal_clock )
271
+ vmware_register_steal_time ();
272
+ }
273
+
274
+ static void vmware_pv_guest_cpu_reboot (void * unused )
275
+ {
276
+ vmware_disable_steal_time ();
277
+ }
278
+
279
+ static int vmware_pv_reboot_notify (struct notifier_block * nb ,
280
+ unsigned long code , void * unused )
281
+ {
282
+ if (code == SYS_RESTART )
283
+ on_each_cpu (vmware_pv_guest_cpu_reboot , NULL , 1 );
284
+ return NOTIFY_DONE ;
285
+ }
286
+
287
+ static struct notifier_block vmware_pv_reboot_nb = {
288
+ .notifier_call = vmware_pv_reboot_notify ,
289
+ };
290
+
291
+ #ifdef CONFIG_SMP
292
+ static void __init vmware_smp_prepare_boot_cpu (void )
293
+ {
294
+ vmware_guest_cpu_init ();
295
+ native_smp_prepare_boot_cpu ();
296
+ }
297
+
298
+ static int vmware_cpu_online (unsigned int cpu )
299
+ {
300
+ local_irq_disable ();
301
+ vmware_guest_cpu_init ();
302
+ local_irq_enable ();
303
+ return 0 ;
304
+ }
305
+
306
+ static int vmware_cpu_down_prepare (unsigned int cpu )
307
+ {
308
+ local_irq_disable ();
309
+ vmware_disable_steal_time ();
310
+ local_irq_enable ();
311
+ return 0 ;
312
+ }
313
+ #endif
314
+
315
+ static __init int activate_jump_labels (void )
316
+ {
317
+ if (has_steal_clock ) {
318
+ static_key_slow_inc (& paravirt_steal_enabled );
319
+ if (steal_acc )
320
+ static_key_slow_inc (& paravirt_steal_rq_enabled );
321
+ }
322
+
323
+ return 0 ;
324
+ }
325
+ arch_initcall (activate_jump_labels );
326
+
139
327
static void __init vmware_paravirt_ops_setup (void )
140
328
{
141
329
pv_info .name = "VMware hypervisor" ;
142
330
pv_ops .cpu .io_delay = paravirt_nop ;
143
331
144
- if (vmware_tsc_khz && vmw_sched_clock )
145
- vmware_sched_clock_setup ();
332
+ if (vmware_tsc_khz == 0 )
333
+ return ;
334
+
335
+ vmware_cyc2ns_setup ();
336
+
337
+ if (vmw_sched_clock )
338
+ pv_ops .time .sched_clock = vmware_sched_clock ;
339
+
340
+ if (vmware_is_stealclock_available ()) {
341
+ has_steal_clock = true;
342
+ pv_ops .time .steal_clock = vmware_steal_clock ;
343
+
344
+ /* We use reboot notifier only to disable steal clock */
345
+ register_reboot_notifier (& vmware_pv_reboot_nb );
346
+
347
+ #ifdef CONFIG_SMP
348
+ smp_ops .smp_prepare_boot_cpu =
349
+ vmware_smp_prepare_boot_cpu ;
350
+ if (cpuhp_setup_state_nocalls (CPUHP_AP_ONLINE_DYN ,
351
+ "x86/vmware:online" ,
352
+ vmware_cpu_online ,
353
+ vmware_cpu_down_prepare ) < 0 )
354
+ pr_err ("vmware_guest: Failed to install cpu hotplug callbacks\n" );
355
+ #else
356
+ vmware_guest_cpu_init ();
357
+ #endif
358
+ }
146
359
}
147
360
#else
148
361
#define vmware_paravirt_ops_setup () do {} while (0)
@@ -213,7 +426,7 @@ static void __init vmware_platform_setup(void)
213
426
vmware_set_capabilities ();
214
427
}
215
428
216
- static u8 vmware_select_hypercall (void )
429
+ static u8 __init vmware_select_hypercall (void )
217
430
{
218
431
int eax , ebx , ecx , edx ;
219
432
0 commit comments