File hwclock-adjust-and-hctosys.diff of Package util-linux

From: Kurt Garloff <garloff@suse.de>
Subject: Allow to combine --adjust and --hctosys
Reference: bnc441106

Waiting for a clock tick twice when calling --adjust and --hctosys
is wasteful. Having to do it once is bad enough.
This patch allows combining the two options.

It does two additional things:

The busy wait loop for set_hardware_clock_exact() is replaced by a
version that does some usleep()ing to save CPU cycles. It's broken
out in a separate function.

It also uses the newly introduced usec granularity of set_system_clock()
to avoid the up to 1s inaccuracy in --preadjust.

[Patch 4/4]

Index: util-linux-ng-2.14.1/hwclock/hwclock.c
===================================================================
--- util-linux-ng-2.14.1.orig/hwclock/hwclock.c
+++ util-linux-ng-2.14.1/hwclock/hwclock.c
@@ -489,19 +489,13 @@ set_hardware_clock(const time_t newtime,
     err = ur->set_hardware_clock(&new_broken_time);
   }
 }
 
+static int
+set_system_clock(const bool hclock_valid, const time_t newtime,
+                 const bool testing, const int usec);
 
-
-static void
-set_hardware_clock_exact(const time_t sethwtime,
-                         const struct timeval refsystime,
-                         const bool universal,
-                         const bool testing) {
 /*----------------------------------------------------------------------------
-  Set the Hardware Clock to the time "sethwtime", in local time zone or UTC,
-  according to "universal".
-
   Wait for a fraction of a second so that "sethwtime" is the value of
   the Hardware Clock as of system time "refsystime", which is in the past.
   For example, if "sethwtime" is 14:03:05 and "refsystime" is 12:10:04.5
   and the current system time is 12:10:06.0: Wait .5 seconds (to make
@@ -514,11 +508,16 @@ set_hardware_clock_exact(const time_t se
 
   This function ought to be able to accept set times as fractional times.
   Idea for future enhancement.
 -----------------------------------------------------------------------------*/
-
+static time_t
+time_busy_wait(const time_t sethwtime,
+               const struct timeval refsystime,
+	       float delta)
+{
   time_t newhwtime;
   struct timeval beginsystime, nowsystime;
+  float  towait;
 
  time_resync:
   gettimeofday(&beginsystime, NULL);
   newhwtime = sethwtime + (int) time_diff(beginsystime, refsystime) + 1;
@@ -538,15 +537,35 @@ set_hardware_clock_exact(const time_t se
 	  gettimeofday(&nowsystime, NULL);
 	  tdiff = time_diff(nowsystime, beginsystime);
 	  if (tdiff < 0)
 		  goto time_resync;	/* probably time was reset */
-  } while (time_diff(nowsystime, refsystime) - 0.5 < newhwtime - sethwtime);
+	  towait = newhwtime - sethwtime + delta - time_diff(nowsystime, refsystime);
+	  if (towait > 0.08) {
+		  usleep(1000000*(towait-0.08));
+		  towait = 0.08;
+	  }
+  } while (towait > 0);
+  return newhwtime;
+}
 
+static void
+set_hardware_clock_exact(const time_t sethwtime,
+                         const struct timeval refsystime,
+                         const bool universal,
+                         const bool testing,
+			 const bool hctosys) {
+/*----------------------------------------------------------------------------
+  Set the Hardware Clock to the time "sethwtime", in local time zone or UTC,
+  according to "universal".
+-----------------------------------------------------------------------------*/
+
+  time_t newhwtime = time_busy_wait(sethwtime, refsystime, 0.5);
+  if (hctosys)
+    set_system_clock(TRUE, newhwtime, testing, 500000);
   set_hardware_clock(newhwtime, universal, testing);
 }
 
 
-
 static void
 display_time(const bool hclock_valid, const time_t systime,
              const double sync_duration) {
 /*----------------------------------------------------------------------------
@@ -672,9 +691,9 @@ interpret_date_string(const char *date_o
 
 
 static int
 set_system_clock(const bool hclock_valid, const time_t newtime,
-                 const bool testing) {
+                 const bool testing, const int usec) {
 /*----------------------------------------------------------------------------
    Set the System Clock to time 'newtime'.
 
    Also set the kernel time zone value to the value indicated by the
@@ -700,9 +719,9 @@ set_system_clock(const bool hclock_valid
     int minuteswest;
     int rc;
 
     tv.tv_sec = newtime;
-    tv.tv_usec = 0;
+    tv.tv_usec = usec;
 
     broken = localtime(&newtime);
 #ifdef HAVE_TM_GMTOFF
     minuteswest = -broken->tm_gmtoff/60;		/* GNU extension */
@@ -936,9 +955,10 @@ save_adjtime(const struct adjtime adjtim
 static void
 do_adjustment(struct adjtime *adjtime_p,
               const bool hclock_valid, const time_t hclocktime,
               const struct timeval read_time,
-              const bool universal, const bool testing) {
+              const bool universal, const bool testing,
+	      const bool hctosys) {
 /*---------------------------------------------------------------------------
   Do the adjustment requested, by 1) setting the Hardware Clock (if
   necessary), and 2) updating the last-adjusted time in the adjtime
   structure.
@@ -991,9 +1011,9 @@ do_adjustment(struct adjtime *adjtime_p,
                          &adjustment, &retro);
     if (adjustment > 0 || adjustment < -1) {
       set_hardware_clock_exact(hclocktime + adjustment,
                                time_inc(read_time, -retro),
-                               universal, testing);
+                               universal, testing, hctosys);
       adjtime_p->last_adj_time = hclocktime + adjustment;
       adjtime_p->not_adjusted = 0;
       adjtime_p->dirty = TRUE;
     } else
@@ -1047,8 +1067,9 @@ manipulate_clock(const bool show, const 
     struct adjtime adjtime;
       /* Contents of the adjtime file, or what they should be. */
     int rc;  /* local return code */
     bool no_auth;  /* User lacks necessary authorization to access the clock */
+    int usec = 0;
 
     no_auth = ur->get_permissions();
     if (no_auth)
 	  return EX_NOPERM;
@@ -1104,27 +1125,23 @@ manipulate_clock(const bool show, const 
 			       adjtime.not_adjusted,
 			       hclocktime,
 			       &adjustment, &retro);
 	  hclocktime += adjustment;
-	  /* We can safely ignore retro here as it's a positive value,
-	   * thus by ignoring it we at worst set the time a bit more
-	   * backwards than we would ideally, which is safe, as a subsequent
-	   * exact setting will only cause a jump forward which is harmless.
-	   * We could do: usleep((1-retro)*1000000); hclocktime += 1; */
+	  usec = 1000000*retro;
 	}
 
         if (show) {
           display_time(hclock_valid, hclocktime,
                        time_diff(read_time, startup_time));
         } else if (set) {
           set_hardware_clock_exact(set_time, startup_time,
-				      universal, testing);
+				      universal, testing, FALSE);
 	  if (!noadjfile)
             adjust_drift_factor(&adjtime, set_time, hclock_valid, hclocktime,
 			        time_diff(read_time, startup_time));
         } else if (adjust) {
           do_adjustment(&adjtime, hclock_valid, hclocktime,
-                        read_time, universal, testing);
+                        read_time, universal, testing, hctosys);
         } else if (systohc) {
           struct timeval nowtime, reftime;
           /* We can only set_hardware_clock_exact to a whole seconds
              time, so we set it with reference to the most recent
@@ -1134,14 +1151,14 @@ manipulate_clock(const bool show, const 
           reftime.tv_sec = nowtime.tv_sec;
           reftime.tv_usec = 0;
 
           set_hardware_clock_exact((time_t) reftime.tv_sec, reftime,
-                                   universal, testing);
+                                   universal, testing, FALSE);
 	  if (!noadjfile)
             adjust_drift_factor(&adjtime, (time_t) reftime.tv_sec, hclock_valid,
                                 hclocktime, (double) read_time.tv_usec / 1E6);
         } else if (hctosys) {
-          rc = set_system_clock(hclock_valid, hclocktime, testing);
+          rc = set_system_clock(hclock_valid, hclocktime, testing, usec);
           if (rc) {
             printf(_("Unable to set system clock.\n"));
 	    return rc;
           }
@@ -1479,9 +1496,9 @@ main(int argc, char **argv) {
 			"You supplied %d.\n"),
 		      MYNAME, argc);
 	}
 
-	if (show + set + systohc + hctosys + adjust + getepoch + setepoch > 1){
+	if (show + set + systohc + hctosys + getepoch + setepoch > 1){
 		fprintf(stderr, _("You have specified multiple functions.\n"
 				  "You can only perform one function "
 				  "at a time.\n"));
 		hwclock_exit(EX_USAGE);
Index: util-linux-ng-2.14.1/hwclock/hwclock.8
===================================================================
--- util-linux-ng-2.14.1.orig/hwclock/hwclock.8
+++ util-linux-ng-2.14.1/hwclock/hwclock.8
@@ -199,21 +199,17 @@ from not using this option. This option 
 (
 .B \-s
 ). This option is often combined
 .B \-\-preadjust
-and then followed by asynchronous calls with
-.B \-\-adjust
-and
-.B \-\-hctosys
+and then followed by an asynchronous call with
+.B \-\-adjust \-\-hctosys
 
 .TP
 .B \-\-preadjust
 causes hwclock to use the drift information from adjfile when transferring
 the hwclock time to the system time. Unlike
 .B \-\-adjust
-it will not update the adjfile nor change the hwclock. The calculation does
-discard sub-second corrections and may result in a time that's up to 1s behind
-the real value.
+it will not update the adjfile nor change the hwclock.
 
 .TP
 .B \-\-rtc=filename
 overrides the default /dev file name, which is
openSUSE Build Service is sponsored by