x86: Move tsc_calibration to x86_init_ops
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 x86_init_ops function and override it by paravirt or by the early setup of the vmware hypervisor. Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
This commit is contained in:
@ -20,7 +20,7 @@
|
|||||||
#ifndef ASM_X86__HYPERVISOR_H
|
#ifndef ASM_X86__HYPERVISOR_H
|
||||||
#define 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(struct cpuinfo_x86 *c);
|
||||||
|
extern void init_hypervisor_platform(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -210,7 +210,6 @@ static inline unsigned long long paravirt_sched_clock(void)
|
|||||||
{
|
{
|
||||||
return PVOP_CALL0(unsigned long long, pv_time_ops.sched_clock);
|
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)
|
static inline unsigned long long paravirt_read_pmc(int counter)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#define TICK_SIZE (tick_nsec / 1000)
|
#define TICK_SIZE (tick_nsec / 1000)
|
||||||
|
|
||||||
unsigned long long native_sched_clock(void);
|
unsigned long long native_sched_clock(void);
|
||||||
unsigned long native_calibrate_tsc(void);
|
|
||||||
extern int recalibrate_cpu_khz(void);
|
extern int recalibrate_cpu_khz(void);
|
||||||
|
|
||||||
#if defined(CONFIG_X86_32) && defined(CONFIG_X86_IO_APIC)
|
#if defined(CONFIG_X86_32) && defined(CONFIG_X86_IO_APIC)
|
||||||
@ -19,10 +18,6 @@ extern int timer_ack;
|
|||||||
|
|
||||||
extern int no_timer_check;
|
extern int no_timer_check;
|
||||||
|
|
||||||
#ifndef CONFIG_PARAVIRT
|
|
||||||
#define calibrate_tsc() native_calibrate_tsc()
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Accelerators for sched_clock()
|
/* Accelerators for sched_clock()
|
||||||
* convert from cycles(64bits) => nanoseconds (64bits)
|
* convert from cycles(64bits) => nanoseconds (64bits)
|
||||||
* basic equation:
|
* basic equation:
|
||||||
|
@ -48,7 +48,8 @@ static __always_inline cycles_t vget_cycles(void)
|
|||||||
extern void tsc_init(void);
|
extern void tsc_init(void);
|
||||||
extern void mark_tsc_unstable(char *reason);
|
extern void mark_tsc_unstable(char *reason);
|
||||||
extern int unsynchronized_tsc(void);
|
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
|
* Boot-time check whether the TSCs are synchronized across
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#ifndef ASM_X86__VMWARE_H
|
#ifndef ASM_X86__VMWARE_H
|
||||||
#define 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 int vmware_platform(void);
|
||||||
extern void vmware_set_feature_bits(struct cpuinfo_x86 *c);
|
extern void vmware_set_feature_bits(struct cpuinfo_x86 *c);
|
||||||
|
|
||||||
|
@ -112,8 +112,17 @@ struct x86_cpuinit_ops {
|
|||||||
void (*setup_percpu_clockev)(void);
|
void (*setup_percpu_clockev)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct x86_platform_ops - platform specific runtime functions
|
||||||
|
* @calibrate_tsc: calibrate TSC
|
||||||
|
*/
|
||||||
|
struct x86_platform_ops {
|
||||||
|
unsigned long (*calibrate_tsc)(void);
|
||||||
|
};
|
||||||
|
|
||||||
extern struct x86_init_ops x86_init;
|
extern struct x86_init_ops x86_init;
|
||||||
extern struct x86_cpuinit_ops x86_cpuinit;
|
extern struct x86_cpuinit_ops x86_cpuinit;
|
||||||
|
extern struct x86_platform_ops x86_platform;
|
||||||
|
|
||||||
extern void x86_init_noop(void);
|
extern void x86_init_noop(void);
|
||||||
extern void x86_init_uint_noop(unsigned int unused);
|
extern void x86_init_uint_noop(unsigned int unused);
|
||||||
|
@ -34,13 +34,6 @@ detect_hypervisor_vendor(struct cpuinfo_x86 *c)
|
|||||||
c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
|
c->x86_hyper_vendor = X86_HYPER_VENDOR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
static inline void __cpuinit
|
||||||
hypervisor_set_feature_bits(struct cpuinfo_x86 *c)
|
hypervisor_set_feature_bits(struct cpuinfo_x86 *c)
|
||||||
{
|
{
|
||||||
@ -55,3 +48,10 @@ void __cpuinit init_hypervisor(struct cpuinfo_x86 *c)
|
|||||||
detect_hypervisor_vendor(c);
|
detect_hypervisor_vendor(c);
|
||||||
hypervisor_set_feature_bits(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();
|
||||||
|
}
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <linux/dmi.h>
|
#include <linux/dmi.h>
|
||||||
#include <asm/div64.h>
|
#include <asm/div64.h>
|
||||||
#include <asm/vmware.h>
|
#include <asm/vmware.h>
|
||||||
|
#include <asm/x86_init.h>
|
||||||
|
|
||||||
#define CPUID_VMWARE_INFO_LEAF 0x40000000
|
#define CPUID_VMWARE_INFO_LEAF 0x40000000
|
||||||
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
|
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
|
||||||
@ -47,21 +48,29 @@ static inline int __vmware_platform(void)
|
|||||||
return eax != (uint32_t)-1 && ebx == VMWARE_HYPERVISOR_MAGIC;
|
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;
|
uint64_t tsc_hz;
|
||||||
uint32_t eax, ebx, ecx, edx;
|
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);
|
tsc_hz = eax | (((uint64_t)ebx) << 32);
|
||||||
do_div(tsc_hz, 1000);
|
do_div(tsc_hz, 1000);
|
||||||
BUG_ON(tsc_hz >> 32);
|
BUG_ON(tsc_hz >> 32);
|
||||||
return tsc_hz;
|
return tsc_hz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void __init vmware_platform_setup(void)
|
||||||
|
{
|
||||||
|
uint32_t eax, ebx, ecx, edx;
|
||||||
|
|
||||||
|
VMWARE_PORT(GETHZ, eax, ebx, ecx, edx);
|
||||||
|
|
||||||
|
if (ebx != UINT_MAX)
|
||||||
|
x86_platform.calibrate_tsc = vmware_get_tsc_khz;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* While checking the dmi string infomation, just checking the product
|
* While checking the dmi string infomation, just checking the product
|
||||||
* serial key should be enough, as this will always have a VMware
|
* serial key should be enough, as this will always have a VMware
|
||||||
@ -87,12 +96,6 @@ int vmware_platform(void)
|
|||||||
return 0;
|
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.
|
* 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
|
* Still, due to timing difference when running on virtual cpus, the TSC can
|
||||||
|
@ -187,7 +187,7 @@ void __init kvmclock_init(void)
|
|||||||
pv_time_ops.get_wallclock = kvm_get_wallclock;
|
pv_time_ops.get_wallclock = kvm_get_wallclock;
|
||||||
pv_time_ops.set_wallclock = kvm_set_wallclock;
|
pv_time_ops.set_wallclock = kvm_set_wallclock;
|
||||||
pv_time_ops.sched_clock = kvm_clock_read;
|
pv_time_ops.sched_clock = kvm_clock_read;
|
||||||
pv_time_ops.get_tsc_khz = kvm_get_tsc_khz;
|
x86_platform.calibrate_tsc = kvm_get_tsc_khz;
|
||||||
#ifdef CONFIG_X86_LOCAL_APIC
|
#ifdef CONFIG_X86_LOCAL_APIC
|
||||||
x86_cpuinit.setup_percpu_clockev =
|
x86_cpuinit.setup_percpu_clockev =
|
||||||
kvm_setup_secondary_clock;
|
kvm_setup_secondary_clock;
|
||||||
|
@ -309,7 +309,6 @@ struct pv_time_ops pv_time_ops = {
|
|||||||
.get_wallclock = native_get_wallclock,
|
.get_wallclock = native_get_wallclock,
|
||||||
.set_wallclock = native_set_wallclock,
|
.set_wallclock = native_set_wallclock,
|
||||||
.sched_clock = native_sched_clock,
|
.sched_clock = native_sched_clock,
|
||||||
.get_tsc_khz = native_calibrate_tsc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pv_irq_ops pv_irq_ops = {
|
struct pv_irq_ops pv_irq_ops = {
|
||||||
|
@ -818,7 +818,7 @@ void __init setup_arch(char **cmdline_p)
|
|||||||
* VMware detection requires dmi to be available, so this
|
* VMware detection requires dmi to be available, so this
|
||||||
* needs to be done after dmi_scan_machine, for the BP.
|
* needs to be done after dmi_scan_machine, for the BP.
|
||||||
*/
|
*/
|
||||||
init_hypervisor(&boot_cpu_data);
|
init_hypervisor_platform();
|
||||||
|
|
||||||
x86_init.resources.probe_roms();
|
x86_init.resources.probe_roms();
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <asm/delay.h>
|
#include <asm/delay.h>
|
||||||
#include <asm/hypervisor.h>
|
#include <asm/hypervisor.h>
|
||||||
#include <asm/nmi.h>
|
#include <asm/nmi.h>
|
||||||
|
#include <asm/x86_init.h>
|
||||||
|
|
||||||
unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
|
unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
|
||||||
EXPORT_SYMBOL(cpu_khz);
|
EXPORT_SYMBOL(cpu_khz);
|
||||||
@ -401,15 +402,9 @@ unsigned long native_calibrate_tsc(void)
|
|||||||
{
|
{
|
||||||
u64 tsc1, tsc2, delta, ref1, ref2;
|
u64 tsc1, tsc2, delta, ref1, ref2;
|
||||||
unsigned long tsc_pit_min = ULONG_MAX, tsc_ref_min = ULONG_MAX;
|
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;
|
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);
|
local_irq_save(flags);
|
||||||
fast_calibrate = quick_pit_calibrate();
|
fast_calibrate = quick_pit_calibrate();
|
||||||
local_irq_restore(flags);
|
local_irq_restore(flags);
|
||||||
@ -567,7 +562,7 @@ int recalibrate_cpu_khz(void)
|
|||||||
unsigned long cpu_khz_old = cpu_khz;
|
unsigned long cpu_khz_old = cpu_khz;
|
||||||
|
|
||||||
if (cpu_has_tsc) {
|
if (cpu_has_tsc) {
|
||||||
tsc_khz = calibrate_tsc();
|
tsc_khz = x86_platform.calibrate_tsc();
|
||||||
cpu_khz = tsc_khz;
|
cpu_khz = tsc_khz;
|
||||||
cpu_data(0).loops_per_jiffy =
|
cpu_data(0).loops_per_jiffy =
|
||||||
cpufreq_scale(cpu_data(0).loops_per_jiffy,
|
cpufreq_scale(cpu_data(0).loops_per_jiffy,
|
||||||
@ -917,7 +912,7 @@ void __init tsc_init(void)
|
|||||||
if (!cpu_has_tsc)
|
if (!cpu_has_tsc)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
tsc_khz = calibrate_tsc();
|
tsc_khz = x86_platform.calibrate_tsc();
|
||||||
cpu_khz = tsc_khz;
|
cpu_khz = tsc_khz;
|
||||||
|
|
||||||
if (!tsc_khz) {
|
if (!tsc_khz) {
|
||||||
|
@ -825,7 +825,7 @@ static inline int __init activate_vmi(void)
|
|||||||
x86_cpuinit.setup_percpu_clockev = vmi_time_ap_init;
|
x86_cpuinit.setup_percpu_clockev = vmi_time_ap_init;
|
||||||
#endif
|
#endif
|
||||||
pv_time_ops.sched_clock = vmi_sched_clock;
|
pv_time_ops.sched_clock = vmi_sched_clock;
|
||||||
pv_time_ops.get_tsc_khz = vmi_tsc_khz;
|
x86_platform.calibrate_tsc = vmi_tsc_khz;
|
||||||
|
|
||||||
/* We have true wallclock functions; disable CMOS clock sync */
|
/* We have true wallclock functions; disable CMOS clock sync */
|
||||||
no_sync_cmos_clock = 1;
|
no_sync_cmos_clock = 1;
|
||||||
|
@ -68,7 +68,7 @@ unsigned long long vmi_sched_clock(void)
|
|||||||
return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE));
|
return cycles_2_ns(vmi_timer_ops.get_cycle_counter(VMI_CYCLES_AVAILABLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* paravirt_ops.get_tsc_khz = vmi_tsc_khz */
|
/* x86_platform.calibrate_tsc = vmi_tsc_khz */
|
||||||
unsigned long vmi_tsc_khz(void)
|
unsigned long vmi_tsc_khz(void)
|
||||||
{
|
{
|
||||||
unsigned long long khz;
|
unsigned long long khz;
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <asm/e820.h>
|
#include <asm/e820.h>
|
||||||
#include <asm/time.h>
|
#include <asm/time.h>
|
||||||
#include <asm/irq.h>
|
#include <asm/irq.h>
|
||||||
|
#include <asm/tsc.h>
|
||||||
|
|
||||||
void __cpuinit x86_init_noop(void) { }
|
void __cpuinit x86_init_noop(void) { }
|
||||||
void __init x86_init_uint_noop(unsigned int unused) { }
|
void __init x86_init_uint_noop(unsigned int unused) { }
|
||||||
@ -67,3 +68,7 @@ struct __initdata x86_init_ops x86_init = {
|
|||||||
__cpuinitdata struct x86_cpuinit_ops x86_cpuinit = {
|
__cpuinitdata struct x86_cpuinit_ops x86_cpuinit = {
|
||||||
.setup_percpu_clockev = setup_secondary_APIC_clock,
|
.setup_percpu_clockev = setup_secondary_APIC_clock,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct x86_platform_ops x86_platform = {
|
||||||
|
.calibrate_tsc = native_calibrate_tsc,
|
||||||
|
};
|
||||||
|
@ -1320,11 +1320,11 @@ __init void lguest_init(void)
|
|||||||
|
|
||||||
/* Time operations */
|
/* Time operations */
|
||||||
pv_time_ops.get_wallclock = lguest_get_wallclock;
|
pv_time_ops.get_wallclock = lguest_get_wallclock;
|
||||||
pv_time_ops.get_tsc_khz = lguest_tsc_khz;
|
|
||||||
|
|
||||||
x86_init.resources.memory_setup = lguest_memory_setup;
|
x86_init.resources.memory_setup = lguest_memory_setup;
|
||||||
x86_init.irqs.intr_init = lguest_init_IRQ;
|
x86_init.irqs.intr_init = lguest_init_IRQ;
|
||||||
x86_init.timers.timer_init = lguest_time_init;
|
x86_init.timers.timer_init = lguest_time_init;
|
||||||
|
x86_platform.calibrate_tsc = lguest_tsc_khz;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Now is a good time to look at the implementations of these functions
|
* Now is a good time to look at the implementations of these functions
|
||||||
|
@ -844,7 +844,6 @@ static const struct pv_init_ops xen_init_ops __initdata = {
|
|||||||
static const struct pv_time_ops xen_time_ops __initdata = {
|
static const struct pv_time_ops xen_time_ops __initdata = {
|
||||||
.set_wallclock = xen_set_wallclock,
|
.set_wallclock = xen_set_wallclock,
|
||||||
.get_wallclock = xen_get_wallclock,
|
.get_wallclock = xen_get_wallclock,
|
||||||
.get_tsc_khz = xen_tsc_khz,
|
|
||||||
.sched_clock = xen_sched_clock,
|
.sched_clock = xen_sched_clock,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -980,6 +979,8 @@ asmlinkage void __init xen_start_kernel(void)
|
|||||||
x86_init.timers.setup_percpu_clockev = x86_init_noop;
|
x86_init.timers.setup_percpu_clockev = x86_init_noop;
|
||||||
x86_cpuinit.setup_percpu_clockev = x86_init_noop;
|
x86_cpuinit.setup_percpu_clockev = x86_init_noop;
|
||||||
|
|
||||||
|
x86_platform.calibrate_tsc = xen_tsc_khz;
|
||||||
|
|
||||||
#ifdef CONFIG_X86_64
|
#ifdef CONFIG_X86_64
|
||||||
/*
|
/*
|
||||||
* Setup percpu state. We only need to do this for 64-bit
|
* Setup percpu state. We only need to do this for 64-bit
|
||||||
|
Reference in New Issue
Block a user