patch-2.2.0-pre9 linux/kernel/time.c

Next file: linux/mm/filemap.c
Previous file: linux/kernel/sched.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.2.0-pre8/linux/kernel/time.c linux/kernel/time.c
@@ -16,6 +16,12 @@
  *      adjtime interface update and CMOS clock write code
  * 1995-08-13    Torsten Duwe
  *      kernel PLL updated to 1994-12-13 specs (rfc-1589)
+ * 1999-01-16    Ulrich Windl
+ *	Introduced error checking for many cases in adjtimex().
+ *	Updated NTP code according to technical memorandum Jan '96
+ *	"A Kernel Model for Precision Timekeeping" by Dave Mills
+ *	Allow time_constant larger than MAXTC(6) for NTP v4 (MAXTC == 10)
+ *	(Even though the technical memorandum forbids it)
  */
 
 #include <linux/mm.h>
@@ -88,9 +94,11 @@
 	cli();
 	xtime.tv_sec = value;
 	xtime.tv_usec = 0;
-	time_state = TIME_ERROR;
-	time_maxerror = MAXPHASE;
-	time_esterror = MAXPHASE;
+	time_adjust = 0;	/* stop active adjtime() */
+	time_status |= STA_UNSYNC;
+	time_state = TIME_ERROR;	/* p. 24, (a) */
+	time_maxerror = NTP_PHASE_LIMIT;
+	time_esterror = NTP_PHASE_LIMIT;
 	sti();
 	return 0;
 }
@@ -213,6 +221,7 @@
 int do_adjtimex(struct timex *txc)
 {
         long ltemp, mtemp, save_adjust;
+	int error = 0;
 
 	/* In order to modify anything, you gotta be super-user! */
 	if (txc->modes && !capable(CAP_SYS_TIME))
@@ -235,109 +244,153 @@
 	/* Save for later - semantics of adjtime is to return old value */
 	save_adjust = time_adjust;
 
+#if 0	/* STA_CLOCKERR is never set yet */
+	time_status &= ~STA_CLOCKERR;		/* reset STA_CLOCKERR */
+#endif
 	/* If there are input parameters, then process them */
 	if (txc->modes)
 	{
-	    if (time_state == TIME_BAD)
-		time_state = TIME_OK;
-
-	    if (txc->modes & ADJ_STATUS)
-		time_status = txc->status;
+	    if (time_state == TIME_ERROR)
+		time_state = TIME_OK;		/* reset error -- why? */
 
-	    if (txc->modes & ADJ_FREQUENCY)
-		time_freq = txc->freq;
+	    if (txc->modes & ADJ_STATUS)	/* only set allowed bits */
+		time_status =  (txc->status & ~STA_RONLY) |
+			      (time_status & STA_RONLY);
+
+	    if (txc->modes & ADJ_FREQUENCY) {	/* p. 22 */
+		if (txc->freq > MAXFREQ || txc->freq < -MAXFREQ) {
+		    error = -EINVAL;
+		    goto leave;
+		}
+		time_freq = txc->freq - pps_freq;
+	    }
 
-	    if (txc->modes & ADJ_MAXERROR)
+	    if (txc->modes & ADJ_MAXERROR) {
+		if (txc->maxerror < 0 || txc->maxerror >= NTP_PHASE_LIMIT) {
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_maxerror = txc->maxerror;
+	    }
 
-	    if (txc->modes & ADJ_ESTERROR)
+	    if (txc->modes & ADJ_ESTERROR) {
+		if (txc->esterror < 0 || txc->esterror >= NTP_PHASE_LIMIT) {
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_esterror = txc->esterror;
+	    }
 
-	    if (txc->modes & ADJ_TIMECONST)
+	    if (txc->modes & ADJ_TIMECONST) {	/* p. 24 */
+		if (txc->constant < 0) {	/* NTP v4 uses values > 6 */
+		    error = -EINVAL;
+		    goto leave;
+		}
 		time_constant = txc->constant;
+	    }
 
-	    if (txc->modes & ADJ_OFFSET) {
-	      if ((txc->modes == ADJ_OFFSET_SINGLESHOT)
-		  || !(time_status & STA_PLL))
-		{
-		  time_adjust = txc->offset;
+	    if (txc->modes & ADJ_OFFSET) {	/* values checked earlier */
+		if (txc->modes == ADJ_OFFSET_SINGLESHOT) {
+		    /* adjtime() is independent from ntp_adjtime() */
+		    time_adjust = txc->offset;
 		}
-	      else if ((time_status & STA_PLL)||(time_status & STA_PPSTIME))
-		{
-		  ltemp = (time_status & STA_PPSTIME &&
-			   time_status & STA_PPSSIGNAL) ?
-		    pps_offset : txc->offset;
-
-		  /*
-		   * Scale the phase adjustment and
-		   * clamp to the operating range.
-		   */
-		  if (ltemp > MAXPHASE)
-		    time_offset = MAXPHASE << SHIFT_UPDATE;
-		  else if (ltemp < -MAXPHASE)
-		    time_offset = -(MAXPHASE << SHIFT_UPDATE);
-		  else
-		    time_offset = ltemp << SHIFT_UPDATE;
-
-		  /*
-		   * Select whether the frequency is to be controlled and in which
-		   * mode (PLL or FLL). Clamp to the operating range. Ugly
-		   * multiply/divide should be replaced someday.
-		   */
-
-		  if (time_status & STA_FREQHOLD || time_reftime == 0)
+		else if ( time_status & (STA_PLL | STA_PPSTIME) ) {
+		    ltemp = (time_status & (STA_PPSTIME | STA_PPSSIGNAL)) ==
+		            (STA_PPSTIME | STA_PPSSIGNAL) ?
+		            pps_offset : txc->offset;
+
+		    /*
+		     * Scale the phase adjustment and
+		     * clamp to the operating range.
+		     */
+		    if (ltemp > MAXPHASE)
+		        time_offset = MAXPHASE << SHIFT_UPDATE;
+		    else if (ltemp < -MAXPHASE)
+			time_offset = -(MAXPHASE << SHIFT_UPDATE);
+		    else
+		        time_offset = ltemp << SHIFT_UPDATE;
+
+		    /*
+		     * Select whether the frequency is to be controlled
+		     * and in which mode (PLL or FLL). Clamp to the operating
+		     * range. Ugly multiply/divide should be replaced someday.
+		     */
+
+		    if (time_status & STA_FREQHOLD || time_reftime == 0)
+		        time_reftime = xtime.tv_sec;
+		    mtemp = xtime.tv_sec - time_reftime;
 		    time_reftime = xtime.tv_sec;
-		  mtemp = xtime.tv_sec - time_reftime;
-		  time_reftime = xtime.tv_sec;
-		  if (time_status & STA_FLL)
-		    {
-		      if (mtemp >= MINSEC)
-			{
-			  ltemp = ((time_offset / mtemp) << (SHIFT_USEC -
-							     SHIFT_UPDATE));
-			  if (ltemp < 0)
-			    time_freq -= -ltemp >> SHIFT_KH;
-			  else
-			    time_freq += ltemp >> SHIFT_KH;
-			}
-		    } 
-		  else 
-		    {
-		      if (mtemp < MAXSEC)
-			{
-			  ltemp *= mtemp;
-			  if (ltemp < 0)
-			    time_freq -= -ltemp >> (time_constant +
-						    time_constant + SHIFT_KF -
-						    SHIFT_USEC);
-			  else
-			    time_freq += ltemp >> (time_constant +
-						   time_constant + SHIFT_KF -
-						   SHIFT_USEC);
-			}
+		    if (time_status & STA_FLL) {
+		        if (mtemp >= MINSEC) {
+			    ltemp = (time_offset / mtemp) << (SHIFT_USEC -
+							      SHIFT_UPDATE);
+			    if (ltemp < 0)
+			        time_freq -= -ltemp >> SHIFT_KH;
+			    else
+			        time_freq += ltemp >> SHIFT_KH;
+			} else /* calibration interval too short (p. 12) */
+				time_state = TIME_ERROR;
+		    } else {	/* PLL mode */
+		        if (mtemp < MAXSEC) {
+			    ltemp *= mtemp;
+			    if (ltemp < 0)
+			        time_freq -= -ltemp >> (time_constant +
+							time_constant +
+							SHIFT_KF - SHIFT_USEC);
+			    else
+			        time_freq += ltemp >> (time_constant +
+						       time_constant +
+						       SHIFT_KF - SHIFT_USEC);
+			} else /* calibration interval too long (p. 12) */
+				time_state = TIME_ERROR;
 		    }
-		  if (time_freq > time_tolerance)
-		    time_freq = time_tolerance;
-		  else if (time_freq < -time_tolerance)
-		    time_freq = -time_tolerance;
+		    if (time_freq > time_tolerance)
+		        time_freq = time_tolerance;
+		    else if (time_freq < -time_tolerance)
+		        time_freq = -time_tolerance;
 		} /* STA_PLL || STA_PPSTIME */
+	    } /* txc->modes & ADJ_OFFSET */
+	    if (txc->modes & ADJ_TICK) {
+		/* if the quartz is off by more than 10% something is
+		   VERY wrong ! */
+		if (txc->tick < 900000/HZ || txc->tick > 1100000/HZ) {
+		    error = -EINVAL;
+		    goto leave;
+		}
+		tick = txc->tick;
 	    }
-	    if (txc->modes & ADJ_TICK)
-	      tick = txc->tick;
-
+	} /* txc->modes */
+leave:	if ((time_status & (STA_UNSYNC|STA_CLOCKERR)) != 0
+	    || ((time_status & (STA_PPSFREQ|STA_PPSTIME)) != 0
+		&& (time_status & STA_PPSSIGNAL) == 0)
+	    /* p. 24, (b) */
+	    || ((time_status & (STA_PPSTIME|STA_PPSJITTER))
+		== (STA_PPSTIME|STA_PPSJITTER))
+	    /* p. 24, (c) */
+	    || ((time_status & STA_PPSFREQ) != 0
+		&& (time_status & (STA_PPSWANDER|STA_PPSERROR)) != 0))
+	    /* p. 24, (d) */
+		time_state = TIME_ERROR;
+	
+	if ((txc->modes & ADJ_OFFSET_SINGLESHOT) == ADJ_OFFSET_SINGLESHOT)
+	    txc->offset	   = save_adjust;
+	else {
+	    if (time_offset < 0)
+		txc->offset = -(-time_offset >> SHIFT_UPDATE);
+	    else
+		txc->offset = time_offset >> SHIFT_UPDATE;
 	}
-	txc->offset	   = save_adjust;
-	txc->freq	   = time_freq;
+	txc->freq	   = time_freq + pps_freq;
 	txc->maxerror	   = time_maxerror;
 	txc->esterror	   = time_esterror;
 	txc->status	   = time_status;
 	txc->constant	   = time_constant;
 	txc->precision	   = time_precision;
 	txc->tolerance	   = time_tolerance;
-	txc->time	   = xtime;
+	do_gettimeofday(&txc->time);
 	txc->tick	   = tick;
 	txc->ppsfreq	   = pps_freq;
-	txc->jitter	   = pps_jitter;
+	txc->jitter	   = pps_jitter >> PPS_AVG;
 	txc->shift	   = pps_shift;
 	txc->stabil	   = pps_stabil;
 	txc->jitcnt	   = pps_jitcnt;
@@ -346,7 +399,7 @@
 	txc->stbcnt	   = pps_stbcnt;
 
 	sti();
-	return 0;
+	return(error < 0 ? error : time_state);
 }
 
 asmlinkage int sys_adjtimex(struct timex *txc_p)
@@ -360,8 +413,6 @@
 	 */
 	if(copy_from_user(&txc, txc_p, sizeof(struct timex)))
 		return -EFAULT;
-	if ((ret = do_adjtimex(&txc)))
-	  return ret;
-
-	return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : time_state;
+	ret = do_adjtimex(&txc);
+	return copy_to_user(txc_p, &txc, sizeof(struct timex)) ? -EFAULT : ret;
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov