From b2ce2307e8547e420fcce9fbad46119814f7f810 Mon Sep 17 00:00:00 2001 From: Alexey Makhalov <amakhalov@vmware.com> Date: Tue, 9 May 2017 12:39:57 -0700 Subject: [PATCH] x86/vmware: pv-ops boot_clock --- arch/x86/include/asm/paravirt.h | 5 +++++ arch/x86/include/asm/paravirt_types.h | 5 +++++ arch/x86/kernel/cpu/vmware.c | 24 +++++++++++++++++++++++- arch/x86/kernel/head_64.S | 8 ++++++++ arch/x86/kernel/paravirt.c | 7 +++++++ arch/x86/kernel/setup.c | 9 +++++++++ 6 files changed, 57 insertions(+), 1 deletion(-) diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h index d49bbf4..e0e5b8e 100644 --- a/arch/x86/include/asm/paravirt.h +++ b/arch/x86/include/asm/paravirt.h @@ -184,6 +184,11 @@ static inline u64 paravirt_steal_clock(int cpu) return PVOP_CALL1(u64, pv_time_ops.steal_clock, cpu); } +static inline void paravirt_read_boot_clock64(struct timespec64 *ts) +{ + PVOP_VCALL1(pv_time_ops.read_boot_clock64, ts); +} + static inline unsigned long long paravirt_read_pmc(int counter) { return PVOP_CALL1(u64, pv_cpu_ops.read_pmc, counter); diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h index 180bc0b..b6280a9 100644 --- a/arch/x86/include/asm/paravirt_types.h +++ b/arch/x86/include/asm/paravirt_types.h @@ -53,7 +53,8 @@ struct mm_struct; struct desc_struct; struct task_struct; struct cpumask; +struct timespec64; struct flush_tlb_info; struct mmu_gather; /* @@ -99,6 +100,7 @@ struct pv_lazy_ops { struct pv_time_ops { unsigned long long (*sched_clock)(void); unsigned long long (*steal_clock)(int cpu); + void (*read_boot_clock64)(struct timespec64 *ts); } __no_randomize_layout; struct pv_cpu_ops { diff --git a/arch/x86/kernel/cpu/vmware.c b/arch/x86/kernel/cpu/vmware.c index 20509ee..e299357 100644 --- a/arch/x86/kernel/cpu/vmware.c +++ b/arch/x86/kernel/cpu/vmware.c @@ -101,6 +101,7 @@ static struct cyc2ns_data vmware_cyc2ns __ro_after_init; static int vmw_sched_clock __initdata = 1; static DEFINE_PER_CPU_DECRYPTED(struct vmware_steal_time, steal_time) __aligned(64); static int has_steal_clock = 0; +uint64_t __initdata tsc_at_head; static u64 vmware_clock_get_cycles(struct clocksource *cs) { @@ -123,6 +124,26 @@ struct clocksource * __init clocksource_default_clock(void) return &clocksource_vmware; } +/* Function to read the exact time the system has been started. It will be + used as zero time for monotonic clock */ +static void vmware_read_boot_clock64(struct timespec64 *ts) +{ + struct timespec64 now; + u64 delta_nsec; + u32 rem; + + read_persistent_clock64(&now); + delta_nsec = mul_u64_u32_shr(rdtsc(), vmware_cyc2ns.cyc2ns_mul, + vmware_cyc2ns.cyc2ns_shift); + delta_nsec -= vmware_cyc2ns.cyc2ns_offset; + ts->tv_sec = now.tv_sec - div_s64_rem(delta_nsec, NSEC_PER_SEC, &rem); + ts->tv_nsec = now.tv_nsec - rem; + while (unlikely(ts->tv_nsec < 0)) { + ts->tv_sec--; + ts->tv_nsec += NSEC_PER_SEC; + } +} + static __init int setup_vmw_sched_clock(char *s) { vmw_sched_clock = 0; @@ -143,7 +164,7 @@ static unsigned long long vmware_sched_clock(void) static void __init vmware_cyc2ns_setup(void) { struct cyc2ns_data *d = &vmware_cyc2ns; - unsigned long long tsc_now = rdtsc(); + unsigned long long tsc_now = tsc_at_head; clocks_calc_mult_shift(&d->cyc2ns_mul, &d->cyc2ns_shift, vmware_tsc_khz, NSEC_PER_MSEC, 0); @@ -347,6 +368,7 @@ static void __init vmware_paravirt_ops_setup(void) #endif } clocksource_register_khz(&clocksource_vmware, vmware_tsc_khz); + pv_time_ops.read_boot_clock64 = vmware_read_boot_clock64; } #else #define vmware_paravirt_ops_setup() do {} while (0) diff --git a/arch/x86/kernel/head_64.S b/arch/x86/kernel/head_64.S index 8344dd2..60ad9ac 100644 --- a/arch/x86/kernel/head_64.S +++ b/arch/x86/kernel/head_64.S @@ -107,6 +107,14 @@ ENTRY(secondary_startup_64) * after the boot processor executes this code. */ + /* + * Read a TSC value first + */ + rdtsc + shl $0x20, %rdx + or %rax, %rdx + mov %rdx, tsc_at_head(%rip) + /* Sanitize CPU configuration */ call verify_cpu diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c index 930c883..f6b3799 100644 --- a/arch/x86/kernel/paravirt.c +++ b/arch/x86/kernel/paravirt.c @@ -219,6 +219,12 @@ static u64 native_steal_clock(int cpu) return 0; } +static void native_read_boot_clock64(struct timespec64 *ts) +{ + ts->tv_sec = 0; + ts->tv_nsec = 0; +} + /* These are in entry.S */ extern void native_iret(void); extern void native_usergs_sysret64(void); @@ -326,6 +332,7 @@ struct pv_init_ops pv_init_ops = { struct pv_time_ops pv_time_ops = { .sched_clock = native_sched_clock, .steal_clock = native_steal_clock, + .read_boot_clock64 = native_read_boot_clock64, }; __visible struct pv_irq_ops pv_irq_ops = { diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index 74b4472..3a6ab94 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1318,3 +1318,13 @@ static int __init register_kernel_offset_dumper(void) return 0; } __initcall(register_kernel_offset_dumper); + + +/* We need to define a real function for read_boot_clock64, to override the + weak default version */ +#ifdef CONFIG_PARAVIRT +void read_boot_clock64(struct timespec64 *ts) +{ + paravirt_read_boot_clock64(ts); +} +#endif