On Sunday, February 3, 2008 at 23:44:18 +0100, Alain Guibert wrote:

>  -3) Rename busy_wait() to busywait_uip_fall() only for --directisa
> mode. Write a new busywait_second_change() function only for /dev/rtc
> mode, looping around ioctl(RTC_RD_TIME) until the time changes.

The attached no-interrupt-fallback-3.patch implements this proposal. It
comes as an option, in addition to the previous patch. First of all,
I abandon method #2. The choice is #1 or #3. The more I think to it, the
less I prefer this third method.

Method #1 (uip_fall) is dirty. It must be called by root, nobody else.
But it is top accurate on all kernels.

Method #3 (second_change) is clean, and usable by non-root users if they
have read rights on /dev/rtc. Accuracy is a little bit less good on good
kernels, catastrophic on bad kernels.

Note that you can experimentaly compare both methods by setting variable
noint_fallback to 0 (uip_fall), or to 1 (second_change). Also attached
no-interrupt-fallback-test.patch, a patch to simulate Klaus problem on
a machine having proper interrupts. Test-only, not to be commited.

I vote for method #1, because of top accuracy.


Alain.
no-interrupt-fallback-3.patch
Implements busywait_second_change()
When /dev/rtc interrupt is not available, fallbacks to loops around
ioctl(RTC_RD_TIME) until the RTC second changes.
Patch applies to adjtimex 1.23 above first no-interrupt-fallback.patch
fixes partially(?) Debian bug #460065 as by proposed solution #3
Signed-off-by: Alain Guibert <[EMAIL PROTECTED]>


diff -prud adjtimex-1.23.orig/adjtimex.c adjtimex-1.23/adjtimex.c
--- adjtimex-1.23.orig/adjtimex.c       Fri Feb  8 10:46:23 2008
+++ adjtimex-1.23/adjtimex.c    Fri Feb  8 13:47:03 2008
@@ -157,6 +157,7 @@ static void cmos_init_directisa ();
 static inline int cmos_read_bcd (int addr);
 static void cmos_read_time (time_t *cmos_timep, double *sysp);
 static void busywait_uip_fall(struct timeval *timestamp);
+static void busywait_second_change(struct tm *cmos, struct timeval *timestamp);
 static void compare(void);
 static void failntpdate();
 static void reset_time_status(void);
@@ -652,19 +653,24 @@ cmos_read_time (time_t *cmos_timep, doub
   static int sanity_checked=0;
   time_t cmos_time;
   struct timeval now;
+  int noint_fallback = 1;      /* detect tick by 0 => uip, 1 => time change */
 
   if (using_dev_rtc > 0)       /* access the CMOS clock thru /dev/rtc */
     {
       ioctl (cmos_fd, RTC_PIE_OFF, NULL); /* disable periodic interrupts */
       rc = ioctl (cmos_fd, RTC_UIE_ON, NULL); /* enable update
                                               complete interrupts */
-      if (rc == -1)    /* busywait for update-in-progress fall */
+      if (rc == -1)    /* no interrupts? fallback to busywait */
        {
          if (verbose)
            fprintf(stdout, 
                    "/dev/rtc doesn't allow user access to update interrupts\n"
                    " - using busy wait instead\n");
-         busywait_uip_fall(&now);
+
+         if (noint_fallback)
+           busywait_second_change(&tm, &now);
+         else
+           busywait_uip_fall(&now);
        }
       else             /* wait for update-ended interrupt */
        {
@@ -836,6 +842,39 @@ busywait_uip_fall(struct timeval *timest
       gettimeofday(timestamp, NULL);
       break;
     }
+}
+
+/*
+ * Busywait for a change in RTC time and timestamp this event.
+ * cmos_init() must have been called before, and the selected
+ * access method must be using_dev_rtc=1.
+ *
+ * Important note: an ioctl(RTC_RD_TIME) call that happens while the RTC
+ * is updating itself (UIP up, a 2 milliseconds long event) will block.
+ * Properly block until UIP release on recent Linux kernels since 2.6.16.
+ * However all older Linux kernels had a misfeature: they blocked much
+ * longer than necessary, up to 20 ms longer in the worst case.
+ * The method used here cannot detect precisely the CMOS clock tick on
+ * such older kernels. It would result in a random delay, the timestamp
+ * being between 8 and 18 ms late. Hell: that's 3 orders of magnitude
+ * worse than the accuracy expected from this function.
+ */
+static void
+busywait_second_change(struct tm *cmos, struct timeval *timestamp)
+{
+  struct tm begin;
+
+  if (verbose)
+    fprintf (stdout, "waiting for CMOS time change\n");
+
+  /* pick the time, then loop until it changes */
+  ioctl (cmos_fd, RTC_RD_TIME, &begin);
+  do
+    {
+      ioctl (cmos_fd, RTC_RD_TIME, cmos);
+    }
+  while (cmos->tm_sec == begin.tm_sec);
+  gettimeofday(timestamp, NULL);
 }
 
 static inline void 
simulate broken machine without RTC interrupts
patch to test no-interrupt-fallback.patch
DO NOT COMMIT


diff -prud adjtimex-1.23.orig/adjtimex.c adjtimex-1.23/adjtimex.c
--- adjtimex-1.23.orig/adjtimex.c       Fri Feb  8 10:46:23 2008
+++ adjtimex-1.23/adjtimex.c    Fri Feb  8 14:20:49 2008
@@ -659,1 +659,1 @@ cmos_read_time (time_t *cmos_timep, doub
-      rc = ioctl (cmos_fd, RTC_UIE_ON, NULL); /* enable update
+      rc = -1; /*ioctl (cmos_fd, RTC_UIE_ON, NULL);*/ /* enable update

Reply via email to