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
--Message-Boundary-28924
Content-type: text/plain; charset=US-ASCII
Content-transfer-encoding: 7BIT
Content-description: Text from file 'newadjtime.diff'
--- 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.
--Message-Boundary-28924--
-
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/