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