patch-proposal: extended adjtime()

From: Ulrich Windl (Ulrich.Windl@rz.uni-regensburg.de)
Date: Tue Apr 24 2001 - 07:48:39 EST


Hello,

someone found out that in Linux adjtime()'s correction is limited to
something like 2000s (signed 32bit microseconds for i386). This is not
a true problem, but for those who desperately need/want it, I have a
patch proposal (incomplete, but essential) to implement the full range
(maybe even more). The patch tries to keep binary compatibility, too.

Opinions?

Regards,
Ulrich


--- kernel/243time.c Mon Apr 16 20:14:27 2001
+++ kernel/xxxtime.c Mon Apr 16 20:41:15 2001
@@ -100,7 +100,8 @@
         write_lock_irq(&xtime_lock);
         xtime.tv_sec = value;
         xtime.tv_usec = 0;
- time_adjust = 0; /* stop active adjtime() */
+ time_adjust.tv_sec = time_adjust.tv_usec = 0;
+ /* stop active adjtime() */
         time_status |= STA_UNSYNC;
         time_maxerror = NTP_PHASE_LIMIT;
         time_esterror = NTP_PHASE_LIMIT;
@@ -225,7 +226,8 @@
  */
 int do_adjtimex(struct timex *txc)
 {
- long ltemp, mtemp, save_adjust;
+ long ltemp, mtemp;
+ struct timeval save_adjust;
         int result;
 
         /* In order to modify anything, you gotta be super-user! */
@@ -295,7 +297,31 @@
             if (txc->modes & ADJ_OFFSET) { /* values checked earlier */
                 if (txc->modes == ADJ_OFFSET_SINGLESHOT) {
                     /* adjtime() is independent from ntp_adjtime() */
- time_adjust = txc->offset;
+
+ /* Try to extend the range for plain old adjtime()
+ * to multiple seconds without breaking binary
+ * compatibility. A perfect solution is not
+ * possible, but this one has a high probability
+ * for success. The true solution is a syscall of
+ * its own.
+ * The offset for ADJ_OFFSET_SINGLESHOT is stored in
+ * txc->time (struct timeval) now. To avoid using
+ * garbage vaues, it's required to copy
+ * `txc->time.tv_usec' also into `txc->offset'. Just
+ * to be sure, we also require the magic word
+ * EXTENDED_ADJTIME_MAGIC to be written to `txc->status'
+ * (it's a value not possible before, and it's
+ * overwritten after each call).
+ */
+#define EXTENDED_ADJTIME_MAGIC (0x0000ffff + ('U' << 24) + ('W' << 16))
+ /* old compatible interface */
+ time_adjust.tv_usec = txc->offset;
+
+ if (txc->offset == txc->time.tv_usec &&
+ txc->status == EXTENDED_ADJTIME_MAGIC) {
+ /* extended part */
+ time_adjust.tv_sec = txc->time.tv_sec;
+ }
                 }
                 else if ( time_status & (STA_PLL | STA_PPSTIME) ) {
                     ltemp = (time_status & (STA_PPSTIME | STA_PPSSIGNAL)) ==
@@ -375,9 +401,11 @@
             /* p. 24, (d) */
                 result = TIME_ERROR;
         
- if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
- txc->offset = save_adjust;
- else {
+ if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT) {
+ txc->offset = save_adjust.tv_usec;
+ if (txc->status == EXTENDED_ADJTIME_MAGIC)
+ txc->time = save_adjust;
+ } else {
             if (time_offset < 0)
                 txc->offset = -(-time_offset >> SHIFT_UPDATE);
             else
--- kernel/243timer.c Mon Apr 16 20:34:29 2001
+++ kernel/xxxtimer.c Mon Apr 16 21:01:29 2001
@@ -58,8 +58,7 @@
 long time_adj; /* tick adjust (scaled 1 / HZ) */
 long time_reftime; /* time at last adjustment (s) */
 
-long time_adjust;
-long time_adjust_step;
+struct timeval time_adjust; /* remaining time adjustment */
 
 unsigned long event;
 
@@ -461,8 +460,26 @@
 /* in the NTP reference this is called "hardclock()" */
 static void update_wall_time_one_tick(void)
 {
- if ( (time_adjust_step = time_adjust) != 0 ) {
- /* We are doing an adjtime thing.
+ long time_adjust_step;
+
+ if ((time_adjust.tv_sec | time_adjust.tv_usec) != 0) {
+ time_adjust_step = time_adjust.tv_usec;
+ if (time_adjust_step > 0) {
+ /* if we run out of microseconds, but have more seconds,
+ * borrow another second
+ */
+ if (time_adjust_step < tickadj && time_adjust.tv_sec > 0) {
+ time_adjust_step = time_adjust.tv_usec += 1000000;
+ --time_adjust.tv_sec;
+ }
+ } else {
+ if (time_adjust_step > -tickadj && time_adjust.tv_sec < 0) {
+ time_adjust_step = time_adjust.tv_usec -= 1000000;
+ ++time_adjust.tv_sec;
+ }
+ }
+
+ /* We gave to complete the adjtime() thing.
              *
              * Prepare time_adjust_step to be within bounds.
              * Note that a positive time_adjust means we want the clock
@@ -471,15 +488,16 @@
              * Limit the amount of the step to be in the range
              * -tickadj .. +tickadj
              */
- if (time_adjust > tickadj)
+ if (time_adjust_step > tickadj)
                 time_adjust_step = tickadj;
- else if (time_adjust < -tickadj)
+ else if (time_adjust_step < -tickadj)
                 time_adjust_step = -tickadj;
              
- /* Reduce by this step the amount of time left */
- time_adjust -= time_adjust_step;
+ /* Reduce remaining correction by this step. Can't overflow */
+ time_adjust.tv_usec -= time_adjust_step;
+ xtime.tv_usec += time_adjust_step;
         }
- xtime.tv_usec += tick + time_adjust_step;
+ xtime.tv_usec += tick;
         /*
          * Advance the phase, once it gets to one microsecond, then
          * advance the tick more.

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/



This archive was generated by hypermail 2b29 : Mon Apr 30 2001 - 21:00:10 EST