]> err.no Git - linux-2.6/commitdiff
rtc: fix deadlock
authorIngo Molnar <mingo@elte.hu>
Sat, 23 Aug 2008 15:59:07 +0000 (17:59 +0200)
committerIngo Molnar <mingo@elte.hu>
Sat, 23 Aug 2008 16:02:18 +0000 (18:02 +0200)
if get_rtc_time() is _ever_ called with IRQs off, we deadlock badly
in it, waiting for jiffies to increment.

So make the code more robust by doing an explicit mdelay(20).

This solves a very hard to reproduce/debug hard lockup reported
by Mikael Pettersson.

Reported-by: Mikael Pettersson <mikpe@it.uu.se>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
include/asm-generic/rtc.h

index be4af0029ac0b451cd89c91f239d3eca231a2e62..71ef3f0b9685f8adbfcc74a7717b8f7baf2cd496 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/mc146818rtc.h>
 #include <linux/rtc.h>
 #include <linux/bcd.h>
+#include <linux/delay.h>
 
 #define RTC_PIE 0x40           /* periodic interrupt enable */
 #define RTC_AIE 0x20           /* alarm interrupt enable */
@@ -43,7 +44,6 @@ static inline unsigned char rtc_is_updating(void)
 
 static inline unsigned int get_rtc_time(struct rtc_time *time)
 {
-       unsigned long uip_watchdog = jiffies;
        unsigned char ctrl;
        unsigned long flags;
 
@@ -53,19 +53,15 @@ static inline unsigned int get_rtc_time(struct rtc_time *time)
 
        /*
         * read RTC once any update in progress is done. The update
-        * can take just over 2ms. We wait 10 to 20ms. There is no need to
+        * can take just over 2ms. We wait 20ms. There is no need to
         * to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
         * If you need to know *exactly* when a second has started, enable
         * periodic update complete interrupts, (via ioctl) and then 
         * immediately read /dev/rtc which will block until you get the IRQ.
         * Once the read clears, read the RTC time (again via ioctl). Easy.
         */
-
-       if (rtc_is_updating() != 0)
-               while (jiffies - uip_watchdog < 2*HZ/100) {
-                       barrier();
-                       cpu_relax();
-               }
+       if (rtc_is_updating())
+               mdelay(20);
 
        /*
         * Only the values that we read from the RTC are set. We leave