]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
hwclock: add --delay <seconds>
authorKarel Zak <kzak@redhat.com>
Wed, 18 Jul 2018 11:59:15 +0000 (13:59 +0200)
committerKarel Zak <kzak@redhat.com>
Wed, 18 Jul 2018 11:59:15 +0000 (13:59 +0200)
* add command line option --delay <seconds>

* read RTC type from /sys/class/rtc/rtc<N>/name

* default to 0.5 (500ms) for rtc_cmos or when RTC type is impossible
  determine; otherwise delay is 0.

Signed-off-by: Karel Zak <kzak@redhat.com>
Documentation/TODO
sys-utils/hwclock-cmos.c
sys-utils/hwclock-rtc.c
sys-utils/hwclock.8.in
sys-utils/hwclock.c
sys-utils/hwclock.h

index 488cc067768f6dc7689eb7162e135f37de0332d9..44c9805813e468f689947cc7099dd0205d60198e 100644 (file)
@@ -65,8 +65,6 @@ hwlock
  - use /etc/adjtime as read-only for UTC/LOCAL information only
  - the /var/lib/hwclock/drift should be implemented backwardly compatible,
    it means use the file only if exists, otherwise follow /etc/adjtime 
- - add --delay-on-set <ms> to make possible to by-pass default 500ms delay on --set
-   https://marc.info/?l=util-linux-ng&m=146675599623002&w=2
 
 bash completion
 ---------------
index f7e8a18112ac691a01ad0be318419d10c0ac54c3..426ef4c231da88518d292a89d12376428e5d0f65 100644 (file)
@@ -393,12 +393,18 @@ static int get_permissions_cmos(void)
        return rc;
 }
 
+static get_device_path(void)
+{
+       return NULL;
+}
+
 static struct clock_ops cmos_interface = {
        N_("Using direct ISA access to the clock"),
        get_permissions_cmos,
        read_hardware_clock_cmos,
        set_hardware_clock_cmos,
        synchronize_to_clock_tick_cmos,
+       get_device_path,
 };
 
 /*
index ef95d98073a5ecf98cfea8d32cc76bcd5cb351c9..3b069d7aa37f206afe0879548edb0adae4de3b5f 100644 (file)
@@ -369,12 +369,18 @@ static int get_permissions_rtc(void)
        return 0;
 }
 
+static const char *get_device_path(void)
+{
+       return rtc_dev_name;
+}
+
 static struct clock_ops rtc_interface = {
        N_("Using the rtc interface to the clock."),
        get_permissions_rtc,
        read_hardware_clock_rtc,
        set_hardware_clock_rtc,
        synchronize_to_clock_tick_rtc,
+       get_device_path,
 };
 
 /* return &rtc if /dev/rtc can be opened, NULL otherwise */
index b9f618973cb076bda45b09c26555da381a8fec0f..729b1ddaee537b0b1f67fd4a4a7f066b3caeba67 100644 (file)
@@ -280,6 +280,24 @@ parameters should be observed.
 .RE
 .
 .TP
+.BI \%\-\-delay= seconds
+This option allows to overwrite internally used delay when set clock time. The
+default is 0.5 (500ms) for rtc_cmos, for another RTC types the delay is 0. If
+RTC type is impossible to determine (from sysfs) then it defaults also to 0.5
+to be backwardly compatible.
+.RS
+.PP
+The 500ms default is based on commonly used MC146818A-compatible (x86) hardware clock. This
+Hardware Clock can only be set to any integer time plus one half second.  The
+integer time is required because there is no interface to set or get a
+fractional second.  The additional half second delay is because the Hardware
+Clock updates to the following second precisely 500 ms after setting the new
+time. Unfortunately, this behavior is hardware specific and in same cases
+another delay is required.
+.RE
+.
+.TP
+.TP
 .BR \-D ", " \-\-debug
 .RB Use\  \-\-verbose .
 .RB The\  \%\-\-debug\  option
index b83e71004c140bbae140186a365d87c4f09e636a..d9acbaf7d8bcb7051396b0aecc1ab63eeacf0d87 100644 (file)
@@ -78,6 +78,8 @@
 #include "timeutils.h"
 #include "env.h"
 #include "xalloc.h"
+#include "path.h"
+#include "strutils.h"
 
 #ifdef HAVE_LIBAUDIT
 #include <libaudit.h>
@@ -409,6 +411,47 @@ set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime)
                ur->set_hardware_clock(ctl, &new_broken_time);
 }
 
+static double
+get_hardware_delay(const struct hwclock_control *ctl)
+{
+       const char *devpath, *rtcname;
+       char name[128 + 1];
+       struct path_cxt *pc;
+       int rc;
+
+       devpath = ur->get_device_path();
+       if (!devpath)
+               goto unknown;
+
+       rtcname = strrchr(devpath, '/');
+       if (!rtcname || !*(rtcname + 1))
+               goto unknown;
+       rtcname++;
+
+       pc = ul_new_path("/sys/class/rtc/%s", rtcname);
+       if (!pc)
+               goto unknown;
+       rc = ul_path_scanf(pc, "name", "%128[^\n ]", &name);
+       ul_unref_path(pc);
+
+       if (rc != 1 || !*name)
+               goto unknown;
+
+       if (ctl->verbose)
+               printf(_("RTC type: '%s'\n"), name);
+
+       /* MC146818A-compatible (x86) */
+       if (strcmp(name, "rtc_cmos") == 0)
+               return 0.5;
+
+       /* Another HW */
+       return 0;
+unknown:
+       /* Let's be backwardly compatible */
+       return 0.5;
+}
+
+
 /*
  * Set the Hardware Clock to the time "sethwtime", in local time zone or
  * UTC, according to "universal".
@@ -418,7 +461,8 @@ set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime)
  * 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 exactly 2
  * seconds since "refsystime") and then set the Hardware Clock to 14:03:07,
- * thus getting a precise and retroactive setting of the clock.
+ * thus getting a precise and retroactive setting of the clock. The .5 delay is
+ * default on x86, see --delay and get_hardware_delay().
  *
  * (Don't be confused by the fact that the system clock and the Hardware
  * Clock differ by two hours in the above example. That's just to remind you
@@ -480,15 +524,26 @@ set_hardware_clock_exact(const struct hwclock_control *ctl,
        time_t newhwtime = sethwtime;
        double target_time_tolerance_secs = 0.001;  /* initial value */
        double tolerance_incr_secs = 0.001;         /* initial value */
-       const double RTC_SET_DELAY_SECS = 0.5;      /* 500 ms */
-       const struct timeval RTC_SET_DELAY_TV = { 0, RTC_SET_DELAY_SECS * 1E6 };
+       double delay;
+       struct timeval rtc_set_delay_tv;
 
        struct timeval targetsystime;
        struct timeval nowsystime;
        struct timeval prevsystime = refsystime;
        double deltavstarget;
 
-       timeradd(&refsystime, &RTC_SET_DELAY_TV, &targetsystime);
+       if (ctl->rtc_delay != -1.0)        /* --delay specified */
+               delay = ctl->rtc_delay;
+       else
+               delay = get_hardware_delay(ctl);
+
+       if (ctl->verbose)
+               printf(_("Using delay: %.6f seconds\n"), delay);
+
+       rtc_set_delay_tv.tv_sec = 0;
+       rtc_set_delay_tv.tv_usec = delay * 1E6;
+
+       timeradd(&refsystime, &rtc_set_delay_tv, &targetsystime);
 
        while (1) {
                double ticksize;
@@ -549,7 +604,7 @@ set_hardware_clock_exact(const struct hwclock_control *ctl,
 
        newhwtime = sethwtime
                    + (int)(time_diff(nowsystime, refsystime)
-                           - RTC_SET_DELAY_SECS /* don't count this */
+                           - delay /* don't count this */
                            + 0.5 /* for rounding */);
        if (ctl->verbose)
                printf(_("%ld.%06ld is close enough to %ld.%06ld (%.6f < %.6f)\n"
@@ -1082,6 +1137,7 @@ usage(void)
        printf(_(
               "     --directisa      use the ISA bus instead of %1$s access\n"), _PATH_RTC_DEV);
        puts(_("     --date <time>    date/time input for --set and --predict"));
+       puts(_("     --delay <sec>    delay used when set new RTC time"));
 #if defined(__linux__) && defined(__alpha__)
        puts(_("     --epoch <year>   epoch input for --setepoch"));
 #endif
@@ -1100,7 +1156,10 @@ usage(void)
 
 int main(int argc, char **argv)
 {
-       struct hwclock_control ctl = { .show = 1 }; /* default op is show */
+       struct hwclock_control ctl = {
+                       .show = 1,              /* default op is show */
+                       .rtc_delay = -1.0       /* unspecified */
+       };
        struct timeval startup_time;
        struct adjtime adjtime = { 0 };
        struct timespec when = { 0 };
@@ -1115,6 +1174,7 @@ int main(int argc, char **argv)
        enum {
                OPT_ADJFILE = CHAR_MAX + 1,
                OPT_DATE,
+               OPT_DELAY,
                OPT_DIRECTISA,
                OPT_EPOCH,
                OPT_GET,
@@ -1150,6 +1210,7 @@ int main(int argc, char **argv)
                { "directisa",    no_argument,       NULL, OPT_DIRECTISA  },
                { "test",         no_argument,       NULL, OPT_TEST       },
                { "date",         required_argument, NULL, OPT_DATE       },
+               { "delay",        required_argument, NULL, OPT_DELAY      },
 #ifdef __linux__
                { "rtc",          required_argument, NULL, 'f'            },
 #endif
@@ -1271,6 +1332,9 @@ int main(int argc, char **argv)
                case OPT_DATE:
                        ctl.date_opt = optarg;  /* --date */
                        break;
+               case OPT_DELAY:
+                       ctl.rtc_delay = strtod_or_err(optarg, "invalid --delay argument");
+                       break;
                case OPT_ADJFILE:
                        ctl.adj_file_name = optarg;     /* --adjfile */
                        break;
index 7bb6ec8bd46782083257b1f1ac29955d4ee68fb3..92fdb5f82edc1a07f7e9e9dcbe8812c1a925426c 100644 (file)
@@ -22,6 +22,7 @@ UL_DEBUG_DECLARE_MASK(hwclock);
 struct hwclock_control {
        char *date_opt;
        char *adj_file_name;
+       double rtc_delay;       /* --delay <seconds> */
 #if defined(__linux__) && defined(__alpha__)
        char *epoch_option;
 #endif
@@ -58,6 +59,7 @@ struct clock_ops {
        int (*read_hardware_clock) (const struct hwclock_control *ctl, struct tm * tm);
        int (*set_hardware_clock) (const struct hwclock_control *ctl, const struct tm * tm);
        int (*synchronize_to_clock_tick) (const struct hwclock_control *ctl);
+       const char *(*get_device_path) (void);
 };
 
 extern struct clock_ops *probe_for_cmos_clock(void);