Skip to content

Commit 42595ce

Browse files
committed
Merge branch 'x86-vmware-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 vmware updates from Ingo Molnar: "The main change in this tree is the addition of 'steal time clock support' for VMware guests" * 'x86-vmware-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/vmware: Use bool type for vmw_sched_clock x86/vmware: Enable steal time accounting x86/vmware: Add steal time clock support for VMware guests x86/vmware: Remove vmware_sched_clock_setup() x86/vmware: Make vmware_select_hypercall() __init
2 parents d9d7677 + 8fefe9d commit 42595ce

File tree

2 files changed

+222
-9
lines changed

2 files changed

+222
-9
lines changed

Documentation/admin-guide/kernel-parameters.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3201,7 +3201,7 @@
32013201
[X86,PV_OPS] Disable paravirtualized VMware scheduler
32023202
clock and use the default one.
32033203

3204-
no-steal-acc [X86,KVM,ARM64] Disable paravirtualized steal time
3204+
no-steal-acc [X86,PV_OPS,ARM64] Disable paravirtualized steal time
32053205
accounting. steal time is computed, but won't
32063206
influence scheduler behaviour
32073207

arch/x86/kernel/cpu/vmware.c

Lines changed: 221 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
#include <linux/init.h>
2626
#include <linux/export.h>
2727
#include <linux/clocksource.h>
28+
#include <linux/cpu.h>
29+
#include <linux/reboot.h>
2830
#include <asm/div64.h>
2931
#include <asm/x86_init.h>
3032
#include <asm/hypervisor.h>
@@ -47,6 +49,11 @@
4749
#define VMWARE_CMD_GETVCPU_INFO 68
4850
#define VMWARE_CMD_LEGACY_X2APIC 3
4951
#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
5057

5158
#define VMWARE_PORT(cmd, eax, ebx, ecx, edx) \
5259
__asm__("inl (%%dx), %%eax" : \
@@ -86,6 +93,18 @@
8693
} \
8794
} while (0)
8895

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+
89108
static unsigned long vmware_tsc_khz __ro_after_init;
90109
static u8 vmware_hypercall_mode __ro_after_init;
91110

@@ -103,15 +122,25 @@ static unsigned long vmware_get_tsc_khz(void)
103122

104123
#ifdef CONFIG_PARAVIRT
105124
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 */
107129

108130
static __init int setup_vmw_sched_clock(char *s)
109131
{
110-
vmw_sched_clock = 0;
132+
vmw_sched_clock = false;
111133
return 0;
112134
}
113135
early_param("no-vmw-sched-clock", setup_vmw_sched_clock);
114136

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+
115144
static unsigned long long notrace vmware_sched_clock(void)
116145
{
117146
unsigned long long ns;
@@ -122,7 +151,7 @@ static unsigned long long notrace vmware_sched_clock(void)
122151
return ns;
123152
}
124153

125-
static void __init vmware_sched_clock_setup(void)
154+
static void __init vmware_cyc2ns_setup(void)
126155
{
127156
struct cyc2ns_data *d = &vmware_cyc2ns;
128157
unsigned long long tsc_now = rdtsc();
@@ -132,17 +161,201 @@ static void __init vmware_sched_clock_setup(void)
132161
d->cyc2ns_offset = mul_u64_u32_shr(tsc_now, d->cyc2ns_mul,
133162
d->cyc2ns_shift);
134163

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));
137258
}
138259

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+
139327
static void __init vmware_paravirt_ops_setup(void)
140328
{
141329
pv_info.name = "VMware hypervisor";
142330
pv_ops.cpu.io_delay = paravirt_nop;
143331

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+
}
146359
}
147360
#else
148361
#define vmware_paravirt_ops_setup() do {} while (0)
@@ -213,7 +426,7 @@ static void __init vmware_platform_setup(void)
213426
vmware_set_capabilities();
214427
}
215428

216-
static u8 vmware_select_hypercall(void)
429+
static u8 __init vmware_select_hypercall(void)
217430
{
218431
int eax, ebx, ecx, edx;
219432

0 commit comments

Comments
 (0)