aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKeir Fraser <keir@xen.org>2011-04-25 13:45:06 +0100
committerKeir Fraser <keir@xen.org>2011-04-25 13:45:06 +0100
commita6a3bf803986847f8a1161fcd72200dfb5d6fd81 (patch)
tree92ffbcb6eff175ce5f59f419cd88c60c207373a1
parentb1c1c767777178bf8c24564973a82332072c37a9 (diff)
downloadxen-a6a3bf803986847f8a1161fcd72200dfb5d6fd81.tar.gz
xen-a6a3bf803986847f8a1161fcd72200dfb5d6fd81.tar.bz2
xen-a6a3bf803986847f8a1161fcd72200dfb5d6fd81.zip
x86: don't write_tsc() non-zero values on CPUs updating only the lower 32 bits
This means suppressing the uses in time_calibration_tsc_rendezvous(), cstate_restore_tsc(), and synchronize_tsc_slave(), and fixes a boot hang of Linux Dom0 when loading processor.ko on such systems that have support for C states above C1. Signed-off-by: Jan Beulich <jbeulich@novell.com> Signed-off-by: Keir Fraser <keir@xen.org> xen-unstable changeset: 23228:1329d99b4f16 xen-unstable date: Fri Apr 15 08:52:08 2011 +0100
-rw-r--r--xen/arch/x86/acpi/cpu_idle.c4
-rw-r--r--xen/arch/x86/smpboot.c13
-rw-r--r--xen/arch/x86/time.c68
-rw-r--r--xen/include/asm-x86/time.h2
-rw-r--r--xen/include/xen/cpuidle.h3
5 files changed, 88 insertions, 2 deletions
diff --git a/xen/arch/x86/acpi/cpu_idle.c b/xen/arch/x86/acpi/cpu_idle.c
index 4e35e40d0d..ce5cfa3b27 100644
--- a/xen/arch/x86/acpi/cpu_idle.c
+++ b/xen/arch/x86/acpi/cpu_idle.c
@@ -941,3 +941,7 @@ void cpuidle_disable_deep_cstate(void)
hpet_disable_legacy_broadcast();
}
+bool_t cpuidle_using_deep_cstate(void)
+{
+ return xen_cpuidle && max_cstate > (local_apic_timer_c2_ok ? 2 : 1);
+}
diff --git a/xen/arch/x86/smpboot.c b/xen/arch/x86/smpboot.c
index a806fa7e4d..35f3c153b7 100644
--- a/xen/arch/x86/smpboot.c
+++ b/xen/arch/x86/smpboot.c
@@ -52,6 +52,7 @@
#include <asm/flushtlb.h>
#include <asm/msr.h>
#include <asm/mtrr.h>
+#include <asm/time.h>
#include <mach_apic.h>
#include <mach_wakecpu.h>
#include <smpboot_hooks.h>
@@ -171,6 +172,12 @@ valid_k7:
* then we print a warning if not, and always resync.
*/
+/*
+ * TSC's upper 32 bits can't be written in earlier CPUs (before
+ * Prescott), there is no way to resync one AP against BP.
+ */
+bool_t disable_tsc_sync;
+
static atomic_t tsc_start_flag = ATOMIC_INIT(0);
static atomic_t tsc_count_start = ATOMIC_INIT(0);
static atomic_t tsc_count_stop = ATOMIC_INIT(0);
@@ -187,6 +194,9 @@ static void __init synchronize_tsc_bp (void)
unsigned int one_usec;
int buggy = 0;
+ if ( disable_tsc_sync )
+ return;
+
if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE)) {
printk("TSC is reliable, synchronization unnecessary\n");
return;
@@ -284,6 +294,9 @@ static void __init synchronize_tsc_ap (void)
{
int i;
+ if ( disable_tsc_sync )
+ return;
+
if (boot_cpu_has(X86_FEATURE_TSC_RELIABLE))
return;
diff --git a/xen/arch/x86/time.c b/xen/arch/x86/time.c
index 3814d51b59..6451dd3f8b 100644
--- a/xen/arch/x86/time.c
+++ b/xen/arch/x86/time.c
@@ -21,6 +21,7 @@
#include <xen/smp.h>
#include <xen/irq.h>
#include <xen/softirq.h>
+#include <xen/cpuidle.h>
#include <xen/keyhandler.h>
#include <xen/guest_access.h>
#include <asm/io.h>
@@ -175,7 +176,6 @@ static inline struct time_scale scale_reciprocal(struct time_scale scale)
* cpu_mask that denotes the CPUs that needs timer interrupt coming in as
* IPIs in place of local APIC timers
*/
-extern int xen_cpuidle;
static cpumask_t pit_broadcast_mask;
static void smp_send_timer_broadcast_ipi(void)
@@ -724,6 +724,8 @@ void cstate_restore_tsc(void)
new_tsc = t->local_tsc_stamp + scale_delta(stime_delta, &sys_to_tsc);
+ ASSERT(boot_cpu_has(X86_FEATURE_TSC_RELIABLE));
+
write_tsc(new_tsc);
}
@@ -1416,6 +1418,66 @@ void init_percpu_time(void)
}
}
+/*
+ * On certain older Intel CPUs writing the TSC MSR clears the upper 32 bits.
+ * Obviously we must not use write_tsc() on such CPUs.
+ *
+ * Additionally, AMD specifies that being able to write the TSC MSR is not an
+ * architectural feature (but, other than their manual says, also cannot be
+ * determined from CPUID bits).
+ */
+static void __init tsc_check_writability(void)
+{
+ const char *what = NULL;
+ uint64_t tsc;
+
+ /*
+ * If all CPUs are reported as synchronised and in sync, we never write
+ * the TSCs (except unavoidably, when a CPU is physically hot-plugged).
+ * Hence testing for writability is pointless and even harmful.
+ */
+ if ( boot_cpu_has(X86_FEATURE_TSC_RELIABLE) )
+ return;
+
+ rdtscll(tsc);
+ if ( wrmsr_safe(MSR_IA32_TSC, 0, 0) == 0 )
+ {
+ uint64_t tmp, tmp2;
+ rdtscll(tmp2);
+ write_tsc(tsc | (1ULL << 32));
+ rdtscll(tmp);
+ if ( ABS((s64)tmp - (s64)tmp2) < (1LL << 31) )
+ what = "only partially";
+ }
+ else
+ {
+ what = "not";
+ }
+
+ /* Nothing to do if the TSC is fully writable. */
+ if ( !what )
+ {
+ /*
+ * Paranoia - write back original TSC value. However, APs get synced
+ * with BSP as they are brought up, so this doesn't much matter.
+ */
+ write_tsc(tsc);
+ return;
+ }
+
+ printk(XENLOG_WARNING "TSC %s writable\n", what);
+
+ /* time_calibration_tsc_rendezvous() must not be used */
+ setup_clear_cpu_cap(X86_FEATURE_CONSTANT_TSC);
+
+ /* cstate_restore_tsc() must not be used (or do nothing) */
+ if ( !boot_cpu_has(X86_FEATURE_NONSTOP_TSC) )
+ cpuidle_disable_deep_cstate();
+
+ /* synchronize_tsc_slave() must do nothing */
+ disable_tsc_sync = 1;
+}
+
/* Late init function (after all CPUs are booted). */
int __init init_xen_time(void)
{
@@ -1432,6 +1494,8 @@ int __init init_xen_time(void)
setup_clear_cpu_cap(X86_FEATURE_TSC_RELIABLE);
}
+ tsc_check_writability();
+
/* If we have constant-rate TSCs then scale factor can be shared. */
if ( boot_cpu_has(X86_FEATURE_CONSTANT_TSC) )
{
@@ -1486,7 +1550,7 @@ static int disable_pit_irq(void)
* XXX dom0 may rely on RTC interrupt delivery, so only enable
* hpet_broadcast if FSB mode available or if force_hpet_broadcast.
*/
- if ( xen_cpuidle && !boot_cpu_has(X86_FEATURE_ARAT) )
+ if ( cpuidle_using_deep_cstate() && !boot_cpu_has(X86_FEATURE_ARAT) )
{
hpet_broadcast_init();
if ( !hpet_broadcast_is_available() )
diff --git a/xen/include/asm-x86/time.h b/xen/include/asm-x86/time.h
index 724fc4a93c..6e9410aedd 100644
--- a/xen/include/asm-x86/time.h
+++ b/xen/include/asm-x86/time.h
@@ -27,6 +27,8 @@ void calibrate_tsc_ap(void);
typedef u64 cycles_t;
+extern bool_t disable_tsc_sync;
+
static inline cycles_t get_cycles(void)
{
cycles_t c;
diff --git a/xen/include/xen/cpuidle.h b/xen/include/xen/cpuidle.h
index 4f4a2995ca..689682c6d9 100644
--- a/xen/include/xen/cpuidle.h
+++ b/xen/include/xen/cpuidle.h
@@ -83,7 +83,10 @@ struct cpuidle_governor
void (*reflect) (struct acpi_processor_power *dev);
};
+extern s8 xen_cpuidle;
extern struct cpuidle_governor *cpuidle_current_governor;
+
+bool_t cpuidle_using_deep_cstate(void);
void cpuidle_disable_deep_cstate(void);
#define CPUIDLE_DRIVER_STATE_START 1