| From khlebnikov@openvz.org Tue Aug 2 14:42:16 2011 |
| From: Konstantin Khlebnikov <khlebnikov@openvz.org> |
| Date: Fri, 29 Jul 2011 14:07:44 +0400 |
| Subject: x86: HPET: Chose a paranoid safe value for the ETIME check |
| To: <stable@kernel.org> |
| Message-ID: <20110729100744.15668.23742.stgit@localhost6> |
| |
| |
| From: Thomas Gleixner <tglx@linutronix.de> |
| |
| (imported from commit v2.6.37-rc5-64-gf1c1807) |
| |
| commit 995bd3bb5 (x86: Hpet: Avoid the comparator readback penalty) |
| chose 8 HPET cycles as a safe value for the ETIME check, as we had the |
| confirmation that the posted write to the comparator register is |
| delayed by two HPET clock cycles on Intel chipsets which showed |
| readback problems. |
| |
| After that patch hit mainline we got reports from machines with newer |
| AMD chipsets which seem to have an even longer delay. See |
| http://thread.gmane.org/gmane.linux.kernel/1054283 and |
| http://thread.gmane.org/gmane.linux.kernel/1069458 for further |
| information. |
| |
| Boris tried to come up with an ACPI based selection of the minimum |
| HPET cycles, but this failed on a couple of test machines. And of |
| course we did not get any useful information from the hardware folks. |
| |
| For now our only option is to chose a paranoid high and safe value for |
| the minimum HPET cycles used by the ETIME check. Adjust the minimum ns |
| value for the HPET clockevent accordingly. |
| |
| Reported-Bistected-and-Tested-by: Markus Trippelsdorf <markus@trippelsdorf.de> |
| Signed-off-by: Thomas Gleixner <tglx@linutronix.de> |
| LKML-Reference: <alpine.LFD.2.00.1012131222420.2653@localhost6.localdomain6> |
| Cc: Simon Kirby <sim@hostway.ca> |
| Cc: Borislav Petkov <bp@alien8.de> |
| Cc: Andreas Herrmann <Andreas.Herrmann3@amd.com> |
| Cc: John Stultz <johnstul@us.ibm.com> |
| Signed-off-by: Konstantin Khlebnikov <khlebnikov@openvz.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> |
| --- |
| arch/x86/kernel/hpet.c | 26 ++++++++++++++++---------- |
| 1 file changed, 16 insertions(+), 10 deletions(-) |
| |
| --- a/arch/x86/kernel/hpet.c |
| +++ b/arch/x86/kernel/hpet.c |
| @@ -27,6 +27,9 @@ |
| #define HPET_DEV_FSB_CAP 0x1000 |
| #define HPET_DEV_PERI_CAP 0x2000 |
| |
| +#define HPET_MIN_CYCLES 128 |
| +#define HPET_MIN_PROG_DELTA (HPET_MIN_CYCLES + (HPET_MIN_CYCLES >> 1)) |
| + |
| #define EVT_TO_HPET_DEV(evt) container_of(evt, struct hpet_dev, evt) |
| |
| /* |
| @@ -298,8 +301,9 @@ static void hpet_legacy_clockevent_regis |
| /* Calculate the min / max delta */ |
| hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF, |
| &hpet_clockevent); |
| - /* 5 usec minimum reprogramming delta. */ |
| - hpet_clockevent.min_delta_ns = 5000; |
| + /* Setup minimum reprogramming delta. */ |
| + hpet_clockevent.min_delta_ns = clockevent_delta2ns(HPET_MIN_PROG_DELTA, |
| + &hpet_clockevent); |
| |
| /* |
| * Start hpet with the boot cpu mask and make it |
| @@ -392,22 +396,24 @@ static int hpet_next_event(unsigned long |
| * the wraparound into account) nor a simple count down event |
| * mode. Further the write to the comparator register is |
| * delayed internally up to two HPET clock cycles in certain |
| - * chipsets (ATI, ICH9,10). We worked around that by reading |
| - * back the compare register, but that required another |
| - * workaround for ICH9,10 chips where the first readout after |
| - * write can return the old stale value. We already have a |
| - * minimum delta of 5us enforced, but a NMI or SMI hitting |
| + * chipsets (ATI, ICH9,10). Some newer AMD chipsets have even |
| + * longer delays. We worked around that by reading back the |
| + * compare register, but that required another workaround for |
| + * ICH9,10 chips where the first readout after write can |
| + * return the old stale value. We already had a minimum |
| + * programming delta of 5us enforced, but a NMI or SMI hitting |
| * between the counter readout and the comparator write can |
| * move us behind that point easily. Now instead of reading |
| * the compare register back several times, we make the ETIME |
| * decision based on the following: Return ETIME if the |
| - * counter value after the write is less than 8 HPET cycles |
| + * counter value after the write is less than HPET_MIN_CYCLES |
| * away from the event or if the counter is already ahead of |
| - * the event. |
| + * the event. The minimum programming delta for the generic |
| + * clockevents code is set to 1.5 * HPET_MIN_CYCLES. |
| */ |
| res = (s32)(cnt - (u32)hpet_readl(HPET_COUNTER)); |
| |
| - return res < 8 ? -ETIME : 0; |
| + return res < HPET_MIN_CYCLES ? -ETIME : 0; |
| } |
| |
| static void hpet_legacy_set_mode(enum clock_event_mode mode, |