[RFC PATCH v4 05/12] x86/ioapic: Refactor the delay logic in timer_irq_works()

From: Dou Liyang
Date: Fri May 26 2017 - 04:13:49 EST


Kernel use timer_irq_works() to detects the timer IRQs. It calls
mdelay(10) to delay ten ticks and checks whether the timer IRQs work
or not. The mdelay() depends on the loops_per_jiffy which is set up
in calibrate_delay(). But in "notsc" case, calibrating delay also
should make sure the timer IRQs work well. There need each other.

Current kernel defaults the IRQs is available when it calibrates delay.
But it is wrong in the dump-capture kernel with 'notsc' option inherited
from 1st kernel option. The correct design is making the interrupt mode
setup and checking IRQs works in advance of calibrate_delay(). That results
in the mdelay() being unusable in timer_irq_works().

Refactor the delay logic by waiting for some cycles. In the system with
X86_FEATURE_TSC feature, Use rdtsc(), others will call __delay() directly.

Note: regard 4G as the max CPU frequence of current single CPU.

Signed-off-by: Dou Liyang <douly.fnst@xxxxxxxxxxxxxx>
---

V3 --> V4:
-Rewrite the changelog
-Delete a blank line
v2 --> v3:
-Find a new way to for waiting.
-Reference to the realization of hpet_clocksource_register() by Thomas.

arch/x86/kernel/apic/io_apic.c | 45 ++++++++++++++++++++++++++++++++++++++++--
1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/apic/io_apic.c b/arch/x86/kernel/apic/io_apic.c
index 347bb9f..f710077 100644
--- a/arch/x86/kernel/apic/io_apic.c
+++ b/arch/x86/kernel/apic/io_apic.c
@@ -1607,6 +1607,43 @@ static int __init notimercheck(char *s)
}
__setup("no_timer_check", notimercheck);

+static void __init delay_with_tsc(void)
+{
+ unsigned long long start, now;
+ unsigned long ticks = jiffies;
+
+ start = rdtsc();
+
+ /*
+ * We don't know the TSC frequency yet, but waiting for
+ * 40000000000/HZ TSC cycles is safe:
+ * 4 GHz == 10 jiffies
+ * 1 GHz == 40 jiffies
+ */
+ do {
+ rep_nop();
+ now = rdtsc();
+ } while ((now - start) < 40000000000UL / HZ &&
+ time_before_eq(jiffies, ticks + 4));
+}
+
+static void __init delay_without_tsc(void)
+{
+ int band = 1;
+ unsigned long ticks = jiffies;
+
+ /*
+ * We don't know any frequency yet, but waiting for
+ * 40940000000/HZ cycles is safe:
+ * 4 GHz == 10 jiffies
+ * 1 GHz == 40 jiffies
+ * 1 << 1 + 1 << 2 +...+ 1 << 11 = 4094
+ */
+ do {
+ __delay(((1 << band++) * 10000000UL) / HZ);
+ } while (band < 12 && time_before_eq(jiffies, ticks + 4));
+}
+
/*
* There is a nasty bug in some older SMP boards, their mptable lies
* about the timer IRQ. We do the following to work around the situation:
@@ -1625,8 +1662,12 @@ static int __init timer_irq_works(void)

local_save_flags(flags);
local_irq_enable();
- /* Let ten ticks pass... */
- mdelay((10 * 1000) / HZ);
+
+ if (boot_cpu_has(X86_FEATURE_TSC))
+ delay_with_tsc();
+ else
+ delay_without_tsc();
+
local_irq_restore(flags);

/*
--
2.5.5