patch-proposal: extended adjtime()
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
xxxtime.c renamed from 243time.c +34 -6
@@ -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
xxxtimer.c renamed from 243timer.c +27 -9
@@ -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.