]> git.ipfire.org Git - thirdparty/chrony.git/commitdiff
Add support for ADJ_SETOFFSET mode
authorMiroslav Lichvar <mlichvar@redhat.com>
Thu, 9 Jun 2011 10:55:28 +0000 (12:55 +0200)
committerMiroslav Lichvar <mlichvar@redhat.com>
Thu, 1 Sep 2011 13:31:11 +0000 (15:31 +0200)
This adjtimex mode allows precise stepping of the system clock.

chrony_timex.h
sys_linux.c
wrap_adjtimex.c
wrap_adjtimex.h

index 870b5a9f212e6521cc23bccf9c9cf5f94e3a69b5..e262ff661ec7c513e969c8a24dc04d334aac2ecc 100644 (file)
@@ -40,6 +40,7 @@ struct timex {
 #define ADJ_MAXERROR           0x0004  /* maximum time error */
 #define ADJ_STATUS             0x0010  /* clock status */
 #define ADJ_TIMECONST          0x0020  /* pll time constant */
+#define ADJ_SETOFFSET          0x0100  /* add 'time' to current time */
 #define ADJ_NANO               0x2000  /* select nanosecond resolution */
 #define ADJ_TICK               0x4000  /* tick value */
 #define ADJ_OFFSET_SINGLESHOT  0x8001  /* old-fashioned adjtime */
index c26023ec1bf9bace113bd45411ad9f90084f39a2..07343615947ec9d9d815a2d2df9c2a699059cc5c 100644 (file)
@@ -115,6 +115,9 @@ static int have_readonly_adjtime;
    adjustments. */
 static int have_nanopll;
 
+/* Flag indicating whether adjtimex() can step the clock */
+static int have_setoffset;
+
 /* ================================================== */
 
 static void handle_end_of_slew(void *anything);
@@ -612,22 +615,28 @@ apply_step_offset(double offset)
     abort_slew();
   }
 
-  if (gettimeofday(&old_time, NULL) < 0) {
-    LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
-  }
+  if (have_setoffset) {
+    if (TMX_ApplyStepOffset(-offset) < 0) {
+      LOG_FATAL(LOGF_SysLinux, "adjtimex() failed");
+    }
+  } else {
+    if (gettimeofday(&old_time, NULL) < 0) {
+      LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
+    }
 
-  UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
+    UTI_AddDoubleToTimeval(&old_time, -offset, &new_time);
 
-  if (settimeofday(&new_time, NULL) < 0) {
-    LOG_FATAL(LOGF_SysLinux, "settimeofday() failed");
-  }
+    if (settimeofday(&new_time, NULL) < 0) {
+      LOG_FATAL(LOGF_SysLinux, "settimeofday() failed");
+    }
 
-  if (gettimeofday(&old_time, NULL) < 0) {
-    LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
-  }
+    if (gettimeofday(&old_time, NULL) < 0) {
+      LOG_FATAL(LOGF_SysLinux, "gettimeofday() failed");
+    }
 
-  UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
-  lcl_InvokeDispersionNotifyHandlers(fabs(err));
+    UTI_DiffTimevalsToDouble(&err, &old_time, &new_time);
+    lcl_InvokeDispersionNotifyHandlers(fabs(err));
+  }
 
   initiate_slew();
 
@@ -1010,6 +1019,13 @@ get_version_specific_details(void)
     have_nanopll = 1;
   }
 
+  /* ADJ_SETOFFSET support */
+  if (kernelvercmp(major, minor, patch, 2, 6, 39) < 0) {
+    have_setoffset = 0;
+  } else {
+    have_setoffset = 1;
+  }
+
   /* Override freq_scale if it appears in conf file */
   CNF_GetLinuxFreqScale(&set_config_freq_scale, &config_freq_scale);
   if (set_config_freq_scale) {
@@ -1049,6 +1065,11 @@ SYS_Linux_Initialise(void)
     have_nanopll = 0;
   }
 
+  if (have_setoffset && TMX_TestStepOffset() < 0) {
+    LOG(LOGS_INFO, LOGF_SysLinux, "adjtimex() doesn't support ADJ_SETOFFSET");
+    have_setoffset = 0;
+  }
+
   TMX_SetSync(CNF_GetRTCSync());
 
   /* Read current kernel frequency */
index 123cd3240850ddc003025bbbfc8fbcc6cb58b800..ed932808ae7affe2270e8b09d6e519f5cfe02c9b 100644 (file)
@@ -229,5 +229,53 @@ TMX_GetPLLOffsetLeft(long *offset)
   return result;
 }
 
+int
+TMX_TestStepOffset(void)
+{
+  struct timex txc;
+
+  /* Zero maxerror and check it's reset to a maximum after ADJ_SETOFFSET.
+     This seems to be the only way how to verify that the kernel really
+     supports the ADJ_SETOFFSET mode as it doesn't return an error on unknown
+     mode. */
+
+  txc.modes = ADJ_MAXERROR;
+  txc.maxerror = 0;
+  if (adjtimex(&txc) < 0 || txc.maxerror != 0)
+    return -1;
+
+  txc.modes = ADJ_SETOFFSET;
+  txc.time.tv_sec = 0;
+  txc.time.tv_usec = 0;
+  if (adjtimex(&txc) < 0 || txc.maxerror < 100000)
+    return -1;
+
+  return 0;
+}
+
+int
+TMX_ApplyStepOffset(double offset)
+{
+  struct timex txc;
+
+  txc.modes = ADJ_SETOFFSET;
+  if (offset >= 0) {
+    txc.time.tv_sec = offset;
+  } else {
+    txc.time.tv_sec = offset - 1;
+  }
+
+  /* ADJ_NANO changes the status even with ADJ_SETOFFSET, use it only when
+     STA_NANO is already enabled */
+  if (status & STA_PLL) {
+    txc.modes |= ADJ_NANO;
+    txc.time.tv_usec = 1e9 * (offset - txc.time.tv_sec);
+  } else {
+    txc.time.tv_usec = 1e6 * (offset - txc.time.tv_sec);
+  }
+
+  return adjtimex(&txc);
+}
+
 #endif
 
index 6736f5c3f3221755789b234d608642b6cf04cd08..e3b9cb0446ae64ad495b85b94d6ceaa1fc6d9de3 100644 (file)
@@ -75,6 +75,8 @@ int TMX_SetSync(int sync);
 int TMX_EnableNanoPLL(void);
 int TMX_ApplyPLLOffset(long offset);
 int TMX_GetPLLOffsetLeft(long *offset);
+int TMX_TestStepOffset(void);
+int TMX_ApplyStepOffset(double offset);
 
 #endif  /* GOT_WRAP_ADJTIMEX_H */