Skip to content

Commit

Permalink
x86: Move tsc_calibration to x86_init_ops
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Thomas Gleixner committed Aug 31, 2009
1 parent 4792621 commit 2d82640
Show file tree
Hide file tree
Showing 17 changed files with 48 additions and 41 deletions.
2 changes: 1 addition & 1 deletion arch/x86/include/asm/hypervisor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
1 change: 0 additions & 1 deletion arch/x86/include/asm/paravirt.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,6 @@ static inline unsigned long long paravirt_sched_clock(void)
{
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)
{
Expand Down
5 changes: 0 additions & 5 deletions arch/x86/include/asm/timer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/include/asm/tsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ static __always_inline cycles_t vget_cycles(void)
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
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/include/asm/vmware.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
9 changes: 9 additions & 0 deletions arch/x86/include/asm/x86_init.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,17 @@ struct x86_cpuinit_ops {
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_cpuinit_ops x86_cpuinit;
extern struct x86_platform_ops x86_platform;

extern void x86_init_noop(void);
extern void x86_init_uint_noop(unsigned int unused);
Expand Down
14 changes: 7 additions & 7 deletions arch/x86/kernel/cpu/hypervisor.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,6 @@ detect_hypervisor_vendor(struct cpuinfo_x86 *c)
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
hypervisor_set_feature_bits(struct cpuinfo_x86 *c)
{
Expand All @@ -55,3 +48,10 @@ void __cpuinit init_hypervisor(struct cpuinfo_x86 *c)
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();
}
21 changes: 12 additions & 9 deletions arch/x86/kernel/cpu/vmware.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <linux/dmi.h>
#include <asm/div64.h>
#include <asm/vmware.h>
#include <asm/x86_init.h>

#define CPUID_VMWARE_INFO_LEAF 0x40000000
#define VMWARE_HYPERVISOR_MAGIC 0x564D5868
Expand All @@ -47,21 +48,29 @@ 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;

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;
}

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
* serial key should be enough, as this will always have a VMware
Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/kvmclock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
x86_platform.calibrate_tsc = kvm_get_tsc_khz;
#ifdef CONFIG_X86_LOCAL_APIC
x86_cpuinit.setup_percpu_clockev =
kvm_setup_secondary_clock;
Expand Down
1 change: 0 additions & 1 deletion arch/x86/kernel/paravirt.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();

x86_init.resources.probe_roms();

Expand Down
13 changes: 4 additions & 9 deletions arch/x86/kernel/tsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <asm/delay.h>
#include <asm/hypervisor.h>
#include <asm/nmi.h>
#include <asm/x86_init.h>

unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz);
Expand Down Expand Up @@ -401,15 +402,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);
Expand Down Expand Up @@ -567,7 +562,7 @@ int recalibrate_cpu_khz(void)
unsigned long cpu_khz_old = cpu_khz;

if (cpu_has_tsc) {
tsc_khz = calibrate_tsc();
tsc_khz = x86_platform.calibrate_tsc();
cpu_khz = tsc_khz;
cpu_data(0).loops_per_jiffy =
cpufreq_scale(cpu_data(0).loops_per_jiffy,
Expand Down Expand Up @@ -917,7 +912,7 @@ void __init tsc_init(void)
if (!cpu_has_tsc)
return;

tsc_khz = calibrate_tsc();
tsc_khz = x86_platform.calibrate_tsc();
cpu_khz = tsc_khz;

if (!tsc_khz) {
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/vmi_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -825,7 +825,7 @@ static inline int __init activate_vmi(void)
x86_cpuinit.setup_percpu_clockev = vmi_time_ap_init;
#endif
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 */
no_sync_cmos_clock = 1;
Expand Down
2 changes: 1 addition & 1 deletion arch/x86/kernel/vmiclock_32.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
/* x86_platform.calibrate_tsc = vmi_tsc_khz */
unsigned long vmi_tsc_khz(void)
{
unsigned long long khz;
Expand Down
5 changes: 5 additions & 0 deletions arch/x86/kernel/x86_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <asm/e820.h>
#include <asm/time.h>
#include <asm/irq.h>
#include <asm/tsc.h>

void __cpuinit x86_init_noop(void) { }
void __init x86_init_uint_noop(unsigned int unused) { }
Expand Down Expand Up @@ -67,3 +68,7 @@ struct __initdata x86_init_ops x86_init = {
__cpuinitdata struct x86_cpuinit_ops x86_cpuinit = {
.setup_percpu_clockev = setup_secondary_APIC_clock,
};

struct x86_platform_ops x86_platform = {
.calibrate_tsc = native_calibrate_tsc,
};
2 changes: 1 addition & 1 deletion arch/x86/lguest/boot.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

x86_init.resources.memory_setup = lguest_memory_setup;
x86_init.irqs.intr_init = lguest_init_IRQ;
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
Expand Down
3 changes: 2 additions & 1 deletion arch/x86/xen/enlighten.c
Original file line number Diff line number Diff line change
Expand Up @@ -844,7 +844,6 @@ static const struct pv_init_ops xen_init_ops __initdata = {
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,
};

Expand Down Expand Up @@ -980,6 +979,8 @@ asmlinkage void __init xen_start_kernel(void)
x86_init.timers.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
/*
* Setup percpu state. We only need to do this for 64-bit
Expand Down

0 comments on commit 2d82640

Please sign in to comment.