diff mbox

[RFC,2/2] x86: Prefer TSC Deadline Timer over LAPIC timer

Message ID alpine.LFD.2.00.1007280035480.28038@localhost.localdomain (mailing list archive)
State New, archived
Headers show

Commit Message

Len Brown July 28, 2010, 4:37 a.m. UTC
None
diff mbox

Patch

diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 2b2407d..73ec308 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -2596,6 +2596,9 @@  and is between 256 and 4096 characters. It is defined in the file
 
 	tdfx=		[HW,DRM]
 
+	tdt_off		[APIC,X86]
+			Disable TSC Deadline Timer, default back to LAPIC timer.
+
 	test_suspend=	[SUSPEND]
 			Specify "mem" (for Suspend-to-RAM) or "standby" (for
 			standby suspend) as the system sleep state to briefly
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index a96489e..64069ae 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -53,6 +53,15 @@ 
 #include <asm/kvm_para.h>
 #include <asm/tsc.h>
 
+#define APIC_TIMER_MODE_ONESHOT		(0 << 17)
+#define APIC_TIMER_MODE_PERIODIC	(1 << 17)
+#define APIC_TIMER_MODE_TSC_DEADLINE	(2 << 17)
+#define APIC_TIMER_MODE_MASK		(3 << 17)
+
+static unsigned long tsc_per_apic_clock;
+static int tdt_enabled;
+static int tdt_disable;
+
 unsigned int num_processors;
 
 unsigned disabled_cpus __cpuinitdata;
@@ -355,6 +364,14 @@  static void __setup_APIC_LVTT(unsigned int clocks, int oneshot, int irqen)
 	if (!irqen)
 		lvtt_value |= APIC_LVT_MASKED;
 
+	if (oneshot && !tdt_disable &&
+	    boot_cpu_has(X86_FEATURE_TSC_DEADLINE)) {
+		printk_once(KERN_DEBUG "TSC deadline timer enabled\n");
+		tdt_enabled = 1;
+		lvtt_value &= (~APIC_TIMER_MODE_MASK);
+		lvtt_value |= APIC_TIMER_MODE_TSC_DEADLINE;
+	}
+
 	apic_write(APIC_LVTT, lvtt_value);
 
 	/*
@@ -409,7 +426,20 @@  EXPORT_SYMBOL_GPL(setup_APIC_eilvt_ibs);
 static int lapic_next_event(unsigned long delta,
 			    struct clock_event_device *evt)
 {
-	apic_write(APIC_TMICT, delta);
+	if (tdt_enabled) {
+		u64 tsc;
+		u64 delta_tsc;
+
+		delta_tsc = delta * tsc_per_apic_clock;
+		/* Just a safety check, should never get used */
+		if (delta_tsc < 2000000)
+			delta_tsc = 2000000;
+
+		rdtscll(tsc);
+		wrmsrl(MSR_IA32_TSC_DEADLINE, tsc + delta_tsc);
+	} else {
+		apic_write(APIC_TMICT, delta);
+	}
 	return 0;
 }
 
@@ -627,6 +657,11 @@  static int __init calibrate_APIC_clock(void)
 
 	deltatsc = (long)(lapic_cal_tsc2 - lapic_cal_tsc1);
 
+	tsc_per_apic_clock = (lapic_cal_tsc2 - lapic_cal_tsc1) /
+				(lapic_cal_t1 - lapic_cal_t2);
+	apic_printk(APIC_VERBOSE, "TSCs per APIC clocktick %lu\n",
+		tsc_per_apic_clock);
+
 	/* we trust the PM based calibration if possible */
 	pm_referenced = !calibrate_by_pmtimer(lapic_cal_pm2 - lapic_cal_pm1,
 					&delta, &deltatsc);
@@ -2314,6 +2349,13 @@  static int __init apic_set_verbosity(char *arg)
 }
 early_param("apic", apic_set_verbosity);
 
+static int __init tdt_off(char *str)
+{
+	tdt_disable = 1;
+	return 1;
+}
+__setup("tdt_off", tdt_off);
+
 static int __init lapic_insert_resource(void)
 {
 	if (!apic_phys)