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