From: Keir Fraser Date: Thu, 1 May 2008 09:50:09 +0000 (+0100) Subject: x86: Add TSC stop support for Deep C state X-Git-Tag: 3.3.0-rc1~238^2~46 X-Git-Url: http://xenbits.xensource.com/gitweb?a=commitdiff_plain;h=fe30f626cca32330175dbd68a3a072aa5bf358e8;p=xen.git x86: Add TSC stop support for Deep C state TSC may stop at deep C state (C3/C4...) entry/exit. this patch add the logic that save and restore TSC during deep C state entry/exit, by using platform timer (PIT/HPET) Signed-off-by: Yu Ke Signed-off-by: Tian Kevin Signed-off-by: Wei Gang --- diff --git a/xen/arch/x86/acpi/cpu_idle.c b/xen/arch/x86/acpi/cpu_idle.c index 73413dccc1..2dc5fa7256 100644 --- a/xen/arch/x86/acpi/cpu_idle.c +++ b/xen/arch/x86/acpi/cpu_idle.c @@ -442,8 +442,8 @@ static void acpi_processor_idle(void) * stopped by H/W. Without carefully handling of TSC/APIC stop issues, * deep C state can't work correctly. */ - /* placeholder for preparing TSC stop */ - + /* preparing TSC stop */ + cstate_save_tsc(); /* placeholder for preparing APIC stop */ /* Invoke C3 */ @@ -451,7 +451,8 @@ static void acpi_processor_idle(void) /* placeholder for recovering APIC */ - /* placeholder for recovering TSC */ + /* recovering TSC */ + cstate_restore_tsc(); /* Get end time (ticks) */ t2 = inl(pmtmr_ioport); diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c index 6bec5cb1c6..44f1118748 100644 --- a/xen/arch/x86/time.c +++ b/xen/arch/x86/time.c @@ -51,9 +51,11 @@ struct time_scale { struct cpu_time { u64 local_tsc_stamp; + u64 cstate_tsc_stamp; s_time_t stime_local_stamp; s_time_t stime_master_stamp; struct time_scale tsc_scale; + u32 cstate_plt_count_stamp; struct timer calibration_timer; }; @@ -66,6 +68,8 @@ struct platform_timesource { static DEFINE_PER_CPU(struct cpu_time, cpu_time); +static u8 tsc_invariant=0; /* TSC is invariant upon C state entry */ + /* * We simulate a 32-bit platform timer from the 16-bit PIT ch2 counter. * Otherwise overflow happens too quickly (~50ms) for us to guarantee that @@ -594,6 +598,36 @@ static void init_platform_timer(void) freq_string(pts->frequency), pts->name); } +void cstate_save_tsc(void) +{ + struct cpu_time *t = &this_cpu(cpu_time); + + if (!tsc_invariant){ + t->cstate_plt_count_stamp = plt_src.read_counter(); + rdtscll(t->cstate_tsc_stamp); + } +} + +void cstate_restore_tsc(void) +{ + struct cpu_time *t; + u32 plt_count_delta; + u64 tsc_delta; + + if (!tsc_invariant){ + t = &this_cpu(cpu_time); + + /* if platform counter overflow happens, interrupt will bring CPU from + C state to working state, so the platform counter won't wrap the + cstate_plt_count_stamp, and the 32 bit unsigned platform counter + is enough for delta calculation + */ + plt_count_delta = + (plt_src.read_counter() - t->cstate_plt_count_stamp) & plt_mask; + tsc_delta = scale_delta(plt_count_delta, &plt_scale)*cpu_khz/1000000UL; + wrmsrl(MSR_IA32_TSC, t->cstate_tsc_stamp + tsc_delta); + } +} /*************************************************************************** * CMOS Timer functions @@ -973,6 +1007,11 @@ int __init init_xen_time(void) stime_platform_stamp = 0; init_platform_timer(); + /* check if TSC is invariant during deep C state + this is a new feature introduced by Nehalem*/ + if ( cpuid_edx(0x80000007) & (1U<<8) ) + tsc_invariant = 1; + local_irq_enable(); return 0; diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h index e70ad5350e..c7c13909a0 100644 --- a/xen/include/xen/time.h +++ b/xen/include/xen/time.h @@ -13,6 +13,8 @@ #include extern int init_xen_time(void); +extern void cstate_save_tsc(void); +extern void cstate_restore_tsc(void); extern unsigned long cpu_khz;