TSC calibration is modified by the vmware hypervisor and paravirt by separate means. Moorestown wants to add its own calibration routine as well. So make calibrate_tsc a proper platform function and override it by paravirt or by the early setup of the vmware hypervisor. Signed-off-by: Thomas Gleixner --- arch/x86/include/asm/hypervisor.h | 2 +- arch/x86/include/asm/paravirt.h | 2 -- arch/x86/include/asm/platform.h | 2 ++ arch/x86/include/asm/timer.h | 5 ----- arch/x86/include/asm/tsc.h | 3 ++- arch/x86/include/asm/vmware.h | 2 +- arch/x86/kernel/cpu/hypervisor.c | 14 +++++++------- arch/x86/kernel/cpu/vmware.c | 35 +++++++++++++++++++---------------- arch/x86/kernel/kvmclock.c | 2 +- arch/x86/kernel/paravirt.c | 1 - arch/x86/kernel/platform_setup.c | 2 ++ arch/x86/kernel/setup.c | 2 +- arch/x86/kernel/tsc.c | 10 ++-------- arch/x86/kernel/vmi_32.c | 2 +- arch/x86/kernel/vmiclock_32.c | 2 +- arch/x86/lguest/boot.c | 2 +- arch/x86/xen/enlighten.c | 2 +- 17 files changed, 42 insertions(+), 48 deletions(-) Index: linux-2.6/arch/x86/include/asm/hypervisor.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/hypervisor.h +++ linux-2.6/arch/x86/include/asm/hypervisor.h @@ -20,7 +20,7 @@ #ifndef ASM_X86__HYPERVISOR_H #define ASM_X86__HYPERVISOR_H -extern unsigned long get_hypervisor_tsc_freq(void); extern void init_hypervisor(struct cpuinfo_x86 *c); +extern void init_hypervisor_platform(void); #endif Index: linux-2.6/arch/x86/include/asm/paravirt.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/paravirt.h +++ linux-2.6/arch/x86/include/asm/paravirt.h @@ -103,7 +103,6 @@ struct pv_time_ops { int (*set_wallclock)(unsigned long); unsigned long long (*sched_clock)(void); - unsigned long (*get_tsc_khz)(void); }; struct pv_cpu_ops { @@ -867,7 +866,6 @@ static inline unsigned long long paravir { return PVOP_CALL0(unsigned long long, pv_time_ops.sched_clock); } -#define calibrate_tsc() (pv_time_ops.get_tsc_khz()) static inline unsigned long long paravirt_read_pmc(int counter) { Index: linux-2.6/arch/x86/include/asm/platform.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/platform.h +++ linux-2.6/arch/x86/include/asm/platform.h @@ -90,11 +90,13 @@ struct platform_setup_paging { * boot cpu * @tsc_pre_init: platform function called before TSC init * @timer_init: initialize the platform timer (default PIT/HPET) + * @calibrate_tsc: calibrate TSC */ struct platform_setup_timers { void (*setup_percpu_clockev)(void); void (*tsc_pre_init)(void); void (*timer_init)(void); + unsigned long (*calibrate_tsc)(void); }; /** Index: linux-2.6/arch/x86/include/asm/timer.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/timer.h +++ linux-2.6/arch/x86/include/asm/timer.h @@ -8,7 +8,6 @@ #define TICK_SIZE (tick_nsec / 1000) unsigned long long native_sched_clock(void); -unsigned long native_calibrate_tsc(void); extern int recalibrate_cpu_khz(void); #if defined(CONFIG_X86_32) && defined(CONFIG_X86_IO_APIC) @@ -19,10 +18,6 @@ extern int timer_ack; extern int no_timer_check; -#ifndef CONFIG_PARAVIRT -#define calibrate_tsc() native_calibrate_tsc() -#endif - /* Accelerators for sched_clock() * convert from cycles(64bits) => nanoseconds (64bits) * basic equation: Index: linux-2.6/arch/x86/include/asm/tsc.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/tsc.h +++ linux-2.6/arch/x86/include/asm/tsc.h @@ -48,7 +48,8 @@ static __always_inline cycles_t vget_cyc extern void tsc_init(void); extern void mark_tsc_unstable(char *reason); extern int unsynchronized_tsc(void); -int check_tsc_unstable(void); +extern int check_tsc_unstable(void); +extern unsigned long native_calibrate_tsc(void); /* * Boot-time check whether the TSCs are synchronized across Index: linux-2.6/arch/x86/include/asm/vmware.h =================================================================== --- linux-2.6.orig/arch/x86/include/asm/vmware.h +++ linux-2.6/arch/x86/include/asm/vmware.h @@ -20,7 +20,7 @@ #ifndef ASM_X86__VMWARE_H #define ASM_X86__VMWARE_H -extern unsigned long vmware_get_tsc_khz(void); +extern void vmware_platform_setup(void); extern int vmware_platform(void); extern void vmware_set_feature_bits(struct cpuinfo_x86 *c); Index: linux-2.6/arch/x86/kernel/cpu/hypervisor.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/cpu/hypervisor.c +++ linux-2.6/arch/x86/kernel/cpu/hypervisor.c @@ -35,13 +35,6 @@ detect_hypervisor_vendor(struct cpuinfo_ } } -unsigned long get_hypervisor_tsc_freq(void) -{ - if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE) - return vmware_get_tsc_khz(); - return 0; -} - static inline void __cpuinit hypervisor_set_feature_bits(struct cpuinfo_x86 *c) { @@ -56,3 +49,10 @@ void __cpuinit init_hypervisor(struct cp detect_hypervisor_vendor(c); hypervisor_set_feature_bits(c); } + +void __init init_hypervisor_platform(void) +{ + init_hypervisor(&boot_cpu_data); + if (boot_cpu_data.x86_hyper_vendor == X86_HYPER_VENDOR_VMWARE) + vmware_platform_setup(); +} Index: linux-2.6/arch/x86/kernel/cpu/vmware.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/cpu/vmware.c +++ linux-2.6/arch/x86/kernel/cpu/vmware.c @@ -24,6 +24,7 @@ #include #include #include +#include #define CPUID_VMWARE_INFO_LEAF 0x40000000 #define VMWARE_HYPERVISOR_MAGIC 0x564D5868 @@ -47,19 +48,27 @@ static inline int __vmware_platform(void return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC; } -static unsigned long __vmware_get_tsc_khz(void) +static unsigned long vmware_get_tsc_khz(void) { - uint64_t tsc_hz; - uint32_t eax, ebx, ecx, edx; + uint64_t tsc_hz; + uint32_t eax, ebx, ecx, edx; + + VMWARE_PORT(GETHZ, eax, ebx, ecx, edx); + + tsc_hz = eax | (((uint64_t)ebx) << 32); + do_div(tsc_hz, 1000); + BUG_ON(tsc_hz >> 32); + return tsc_hz; +} + +void __init vmware_platform_setup(void) +{ + uint32_t eax, ebx, ecx, edx; - VMWARE_PORT(GETHZ, eax, ebx, ecx, edx); + VMWARE_PORT(GETHZ, eax, ebx, ecx, edx); - if (ebx == UINT_MAX) - return 0; - tsc_hz = eax | (((uint64_t)ebx) << 32); - do_div(tsc_hz, 1000); - BUG_ON(tsc_hz >> 32); - return tsc_hz; + if (ebx != UINT_MAX) + platform_setup.timers.calibrate_tsc = vmware_get_tsc_khz; } /* @@ -87,12 +96,6 @@ int vmware_platform(void) return 0; } -unsigned long vmware_get_tsc_khz(void) -{ - BUG_ON(!vmware_platform()); - return __vmware_get_tsc_khz(); -} - /* * VMware hypervisor takes care of exporting a reliable TSC to the guest. * Still, due to timing difference when running on virtual cpus, the TSC can Index: linux-2.6/arch/x86/kernel/kvmclock.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/kvmclock.c +++ linux-2.6/arch/x86/kernel/kvmclock.c @@ -187,7 +187,7 @@ void __init kvmclock_init(void) pv_time_ops.get_wallclock = kvm_get_wallclock; pv_time_ops.set_wallclock = kvm_set_wallclock; pv_time_ops.sched_clock = kvm_clock_read; - pv_time_ops.get_tsc_khz = kvm_get_tsc_khz; + platform_setup.timers.calibrate_tsc = kvm_get_tsc_khz; #ifdef CONFIG_X86_LOCAL_APIC platform_cpuhotplug_setup.setup_percpu_clockev = kvm_setup_secondary_clock; Index: linux-2.6/arch/x86/kernel/paravirt.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/paravirt.c +++ linux-2.6/arch/x86/kernel/paravirt.c @@ -309,7 +309,6 @@ struct pv_time_ops pv_time_ops = { .get_wallclock = native_get_wallclock, .set_wallclock = native_set_wallclock, .sched_clock = native_sched_clock, - .get_tsc_khz = native_calibrate_tsc, }; struct pv_irq_ops pv_irq_ops = { Index: linux-2.6/arch/x86/kernel/platform_setup.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/platform_setup.c +++ linux-2.6/arch/x86/kernel/platform_setup.c @@ -12,6 +12,7 @@ #include #include #include +#include void __cpuinit platform_setup_noop(void) { } void __init platform_setup_uint_noop(unsigned int unused) { } @@ -59,6 +60,7 @@ struct __initdata platform_setup_ops pla .setup_percpu_clockev = setup_boot_APIC_clock, .tsc_pre_init = platform_setup_noop, .timer_init = hpet_time_init, + .calibrate_tsc = native_calibrate_tsc, }, .quirks = { Index: linux-2.6/arch/x86/kernel/setup.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/setup.c +++ linux-2.6/arch/x86/kernel/setup.c @@ -818,7 +818,7 @@ void __init setup_arch(char **cmdline_p) * VMware detection requires dmi to be available, so this * needs to be done after dmi_scan_machine, for the BP. */ - init_hypervisor(&boot_cpu_data); + init_hypervisor_platform(); platform_setup.resources.probe_roms(); Index: linux-2.6/arch/x86/kernel/tsc.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/tsc.c +++ linux-2.6/arch/x86/kernel/tsc.c @@ -401,15 +401,9 @@ unsigned long native_calibrate_tsc(void) { u64 tsc1, tsc2, delta, ref1, ref2; unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX; - unsigned long flags, latch, ms, fast_calibrate, hv_tsc_khz; + unsigned long flags, latch, ms, fast_calibrate; int hpet = is_hpet_enabled(), i, loopmin; - hv_tsc_khz = get_hypervisor_tsc_freq(); - if (hv_tsc_khz) { - printk(KERN_INFO "TSC: Frequency read from the hypervisor\n"); - return hv_tsc_khz; - } - local_irq_save(flags); fast_calibrate = quick_pit_calibrate(); local_irq_restore(flags); @@ -917,7 +911,7 @@ void __init tsc_init(void) if (!cpu_has_tsc) return; - tsc_khz = calibrate_tsc(); + tsc_khz = platform_setup.timers.calibrate_tsc(); cpu_khz = tsc_khz; if (!tsc_khz) { Index: linux-2.6/arch/x86/kernel/vmi_32.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/vmi_32.c +++ linux-2.6/arch/x86/kernel/vmi_32.c @@ -826,7 +826,7 @@ static inline int __init activate_vmi(vo vmi_time_ap_init; #endif pv_time_ops.sched_clock = vmi_sched_clock; - pv_time_ops.get_tsc_khz = vmi_tsc_khz; + platform_setup.timers.calibrate_tsc = vmi_tsc_khz; /* We have true wallclock functions; disable CMOS clock sync */ no_sync_cmos_clock = 1; Index: linux-2.6/arch/x86/kernel/vmiclock_32.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/vmiclock_32.c +++ linux-2.6/arch/x86/kernel/vmiclock_32.c @@ -68,7 +68,7 @@ unsigned long long vmi_sched_clock(void) return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE)); } -/* paravirt_ops.get_tsc_khz = vmi_tsc_khz */ +/* platform_setup.calibrate_tsc = vmi_tsc_khz */ unsigned long vmi_tsc_khz(void) { unsigned long long khz; Index: linux-2.6/arch/x86/lguest/boot.c =================================================================== --- linux-2.6.orig/arch/x86/lguest/boot.c +++ linux-2.6/arch/x86/lguest/boot.c @@ -1320,11 +1320,11 @@ __init void lguest_init(void) /* Time operations */ pv_time_ops.get_wallclock = lguest_get_wallclock; - pv_time_ops.get_tsc_khz = lguest_tsc_khz; platform_setup.resources.memory_setup = lguest_memory_setup; platform_setup.irqs.intr_init = lguest_init_IRQ; platform_setup.timers.timer_init = lguest_time_init; + platform_setup.timers.calibrate_tsc = lguest_tsc_khz; /* * Now is a good time to look at the implementations of these functions Index: linux-2.6/arch/x86/xen/enlighten.c =================================================================== --- linux-2.6.orig/arch/x86/xen/enlighten.c +++ linux-2.6/arch/x86/xen/enlighten.c @@ -844,7 +844,6 @@ static const struct pv_init_ops xen_init static const struct pv_time_ops xen_time_ops __initdata = { .set_wallclock = xen_set_wallclock, .get_wallclock = xen_get_wallclock, - .get_tsc_khz = xen_tsc_khz, .sched_clock = xen_sched_clock, }; @@ -981,6 +980,7 @@ asmlinkage void __init xen_start_kernel( platform_setup.oem.banner = xen_banner; platform_setup.timers.timer_init = xen_time_init; + platform_setup.timers.calibrate_tsc = xen_tsc_khz; /* Override the default per cpu clockevents setup functions */ platform_setup.timers.setup_percpu_clockev = platform_setup_noop; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/