2006-12-07 00:25:39 +01:00
|
|
|
/*
|
2019-11-08 11:12:13 +01:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*
|
|
|
|
* Since 7a3000f7ba548cf7d74ac77cc63fe8de228a669e (v2.30) hwclock is linked
|
|
|
|
* with parse_date.y from gnullib. This gnulib code is distributed with GPLv3.
|
2020-01-27 16:17:10 +01:00
|
|
|
* Use --disable-hwclock-gplv3 to exclude this code.
|
2019-11-08 11:12:13 +01:00
|
|
|
*
|
2006-12-07 00:25:39 +01:00
|
|
|
*
|
|
|
|
* clock.c was written by Charles Hedrick, hedrick@cs.rutgers.edu, Apr 1992
|
|
|
|
* Modified for clock adjustments - Rob Hooft <hooft@chem.ruu.nl>, Nov 1992
|
|
|
|
* Improvements by Harald Koenig <koenig@nova.tat.physik.uni-tuebingen.de>
|
|
|
|
* and Alan Modra <alan@spri.levels.unisa.edu.au>.
|
|
|
|
*
|
|
|
|
* Major rewrite by Bryan Henderson <bryanh@giraffe-data.com>, 96.09.19.
|
|
|
|
* The new program is called hwclock. New features:
|
2011-07-24 17:35:43 +02:00
|
|
|
*
|
|
|
|
* - You can set the hardware clock without also modifying the system
|
|
|
|
* clock.
|
|
|
|
* - You can read and set the clock with finer than 1 second precision.
|
|
|
|
* - When you set the clock, hwclock automatically refigures the drift
|
|
|
|
* rate, based on how far off the clock was before you set it.
|
2006-12-07 00:25:39 +01:00
|
|
|
*
|
|
|
|
* Reshuffled things, added sparc code, and re-added alpha stuff
|
|
|
|
* by David Mosberger <davidm@azstarnet.com>
|
2007-03-21 15:05:58 +01:00
|
|
|
* and Jay Estabrook <jestabro@amt.tay1.dec.com>
|
2021-04-23 01:31:05 +02:00
|
|
|
* and Martin Ostermann <ost@comnets.rwth-aachen.de>, aeb@cwi.nl, 990212.
|
2006-12-07 00:25:39 +01:00
|
|
|
*
|
2011-07-24 17:35:43 +02:00
|
|
|
* Fix for Award 2094 bug, Dave Coffin (dcoffin@shore.net) 11/12/98
|
2006-12-07 00:25:43 +01:00
|
|
|
* Change of local time handling, Stefan Ring <e9725446@stud3.tuwien.ac.at>
|
2006-12-07 00:25:58 +01:00
|
|
|
* Change of adjtime handling, James P. Rutledge <ao112@rgfn.epcc.edu>.
|
2006-12-07 00:25:44 +01:00
|
|
|
*
|
2019-11-08 11:12:13 +01:00
|
|
|
*
|
2006-12-07 00:25:39 +01:00
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Explanation of `adjusting' (Rob Hooft):
|
|
|
|
*
|
|
|
|
* The problem with my machine is that its CMOS clock is 10 seconds
|
|
|
|
* per day slow. With this version of clock.c, and my '/etc/rc.local'
|
|
|
|
* reading '/etc/clock -au' instead of '/etc/clock -u -s', this error
|
|
|
|
* is automatically corrected at every boot.
|
|
|
|
*
|
|
|
|
* To do this job, the program reads and writes the file '/etc/adjtime'
|
|
|
|
* to determine the correction, and to save its data. In this file are
|
|
|
|
* three numbers:
|
|
|
|
*
|
2011-07-24 17:35:43 +02:00
|
|
|
* 1) the correction in seconds per day. (So if your clock runs 5
|
|
|
|
* seconds per day fast, the first number should read -5.0)
|
|
|
|
* 2) the number of seconds since 1/1/1970 the last time the program
|
|
|
|
* was used
|
|
|
|
* 3) the remaining part of a second which was leftover after the last
|
|
|
|
* adjustment
|
2006-12-07 00:25:39 +01:00
|
|
|
*
|
|
|
|
* Installation and use of this program:
|
|
|
|
*
|
2011-07-24 17:35:43 +02:00
|
|
|
* a) create a file '/etc/adjtime' containing as the first and only
|
|
|
|
* line: '0.0 0 0.0'
|
|
|
|
* b) run 'clock -au' or 'clock -a', depending on whether your cmos is
|
|
|
|
* in universal or local time. This updates the second number.
|
|
|
|
* c) set your system time using the 'date' command.
|
|
|
|
* d) update your cmos time using 'clock -wu' or 'clock -w'
|
|
|
|
* e) replace the first number in /etc/adjtime by your correction.
|
|
|
|
* f) put the command 'clock -au' or 'clock -a' in your '/etc/rc.local'
|
2006-12-07 00:25:39 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <errno.h>
|
2006-12-07 00:25:58 +01:00
|
|
|
#include <getopt.h>
|
2011-07-25 23:46:50 +02:00
|
|
|
#include <limits.h>
|
2014-09-28 21:51:51 +02:00
|
|
|
#include <math.h>
|
2011-07-24 19:58:17 +02:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/time.h>
|
2021-11-18 14:08:35 +01:00
|
|
|
#ifdef HAVE_SYS_SYSCALL_H
|
2020-05-11 13:35:21 +02:00
|
|
|
#include <sys/syscall.h>
|
2021-11-18 14:08:35 +01:00
|
|
|
#endif
|
2011-07-24 19:58:17 +02:00
|
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
2021-04-29 13:55:52 +02:00
|
|
|
#include <inttypes.h>
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2011-07-24 19:58:17 +02:00
|
|
|
#include "c.h"
|
2012-04-04 19:59:35 +02:00
|
|
|
#include "closestream.h"
|
2006-12-07 00:25:39 +01:00
|
|
|
#include "nls.h"
|
2012-06-14 20:58:12 +02:00
|
|
|
#include "optutils.h"
|
2011-07-24 23:18:22 +02:00
|
|
|
#include "pathnames.h"
|
2012-05-31 11:15:07 +02:00
|
|
|
#include "hwclock.h"
|
2016-05-24 11:10:10 +02:00
|
|
|
#include "timeutils.h"
|
2016-08-07 08:27:21 +02:00
|
|
|
#include "env.h"
|
2016-07-17 13:12:52 +02:00
|
|
|
#include "xalloc.h"
|
2018-07-18 13:59:15 +02:00
|
|
|
#include "path.h"
|
|
|
|
#include "strutils.h"
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2007-03-21 14:12:05 +01:00
|
|
|
#ifdef HAVE_LIBAUDIT
|
|
|
|
#include <libaudit.h>
|
|
|
|
static int hwaudit_fd = -1;
|
|
|
|
#endif
|
|
|
|
|
2017-12-24 21:38:58 +01:00
|
|
|
UL_DEBUG_DEFINE_MASK(hwclock);
|
|
|
|
UL_DEBUG_DEFINE_MASKNAMES(hwclock) = UL_DEBUG_EMPTY_MASKNAMES;
|
|
|
|
|
2006-12-07 00:25:39 +01:00
|
|
|
/* The struct that holds our hardware access routines */
|
2017-02-12 01:19:33 +01:00
|
|
|
static struct clock_ops *ur;
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2014-05-05 20:49:29 +02:00
|
|
|
/* Maximal clock adjustment in seconds per day.
|
|
|
|
(adjtime() glibc call has 2145 seconds limit on i386, so it is good enough for us as well,
|
|
|
|
43219 is a maximal safe value preventing exact_adjustment overflow.) */
|
|
|
|
#define MAX_DRIFT 2145.0
|
|
|
|
|
2006-12-07 00:25:39 +01:00
|
|
|
struct adjtime {
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* This is information we keep in the adjtime file that tells us how
|
|
|
|
* to do drift corrections. Elements are all straight from the
|
|
|
|
* adjtime file, so see documentation of that file for details.
|
|
|
|
* Exception is <dirty>, which is an indication that what's in this
|
|
|
|
* structure is not what's in the disk file (because it has been
|
|
|
|
* updated since read from the disk file).
|
|
|
|
*/
|
2017-08-26 14:38:07 +02:00
|
|
|
int dirty;
|
2011-07-24 17:35:43 +02:00
|
|
|
/* line 1 */
|
|
|
|
double drift_factor;
|
|
|
|
time_t last_adj_time;
|
|
|
|
double not_adjusted;
|
|
|
|
/* line 2 */
|
|
|
|
time_t last_calib_time;
|
|
|
|
/*
|
|
|
|
* The most recent time that we set the clock from an external
|
|
|
|
* authority (as opposed to just doing a drift adjustment)
|
|
|
|
*/
|
|
|
|
/* line 3 */
|
2016-07-17 14:25:40 +02:00
|
|
|
enum a_local_utc { UTC = 0, LOCAL, UNKNOWN } local_utc;
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* To which time zone, local or UTC, we most recently set the
|
|
|
|
* hardware clock.
|
|
|
|
*/
|
2006-12-07 00:25:39 +01:00
|
|
|
};
|
|
|
|
|
2017-12-24 21:38:58 +01:00
|
|
|
static void hwclock_init_debug(const char *str)
|
|
|
|
{
|
|
|
|
__UL_INIT_DEBUG_FROM_STRING(hwclock, HWCLOCK_DEBUG_, 0, str);
|
|
|
|
|
|
|
|
DBG(INIT, ul_debug("hwclock debug mask: 0x%04x", hwclock_debug_mask));
|
|
|
|
DBG(INIT, ul_debug("hwclock version: %s", PACKAGE_STRING));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FOR TESTING ONLY: inject random delays of up to 1000ms */
|
|
|
|
static void up_to_1000ms_sleep(void)
|
|
|
|
{
|
|
|
|
int usec = random() % 1000000;
|
|
|
|
|
|
|
|
DBG(RANDOM_SLEEP, ul_debug("sleeping ~%d usec", usec));
|
|
|
|
xusleep(usec);
|
|
|
|
}
|
|
|
|
|
2014-10-15 20:21:13 +02:00
|
|
|
/*
|
|
|
|
* time_t to timeval conversion.
|
|
|
|
*/
|
|
|
|
static struct timeval t2tv(time_t timet)
|
|
|
|
{
|
|
|
|
struct timeval rettimeval;
|
|
|
|
|
|
|
|
rettimeval.tv_sec = timet;
|
|
|
|
rettimeval.tv_usec = 0;
|
|
|
|
return rettimeval;
|
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* The difference in seconds between two times in "timeval" format.
|
|
|
|
*/
|
|
|
|
double time_diff(struct timeval subtrahend, struct timeval subtractor)
|
|
|
|
{
|
|
|
|
return (subtrahend.tv_sec - subtractor.tv_sec)
|
|
|
|
+ (subtrahend.tv_usec - subtractor.tv_usec) / 1E6;
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* The time, in "timeval" format, which is <increment> seconds after the
|
|
|
|
* time <addend>. Of course, <increment> may be negative.
|
|
|
|
*/
|
|
|
|
static struct timeval time_inc(struct timeval addend, double increment)
|
|
|
|
{
|
|
|
|
struct timeval newtime;
|
|
|
|
|
2021-04-23 22:48:13 +02:00
|
|
|
newtime.tv_sec = addend.tv_sec + (time_t)increment;
|
|
|
|
newtime.tv_usec = addend.tv_usec + (increment - (time_t)increment) * 1E6;
|
2011-07-24 17:35:43 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now adjust it so that the microsecond value is between 0 and 1
|
|
|
|
* million.
|
|
|
|
*/
|
|
|
|
if (newtime.tv_usec < 0) {
|
|
|
|
newtime.tv_usec += 1E6;
|
|
|
|
newtime.tv_sec -= 1;
|
|
|
|
} else if (newtime.tv_usec >= 1E6) {
|
|
|
|
newtime.tv_usec -= 1E6;
|
|
|
|
newtime.tv_sec += 1;
|
|
|
|
}
|
|
|
|
return newtime;
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2017-08-26 14:38:07 +02:00
|
|
|
static int
|
2016-07-16 17:45:07 +02:00
|
|
|
hw_clock_is_utc(const struct hwclock_control *ctl,
|
2021-03-04 11:27:47 +01:00
|
|
|
const struct adjtime *adjtime)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
2017-08-26 14:38:07 +02:00
|
|
|
int ret;
|
2006-12-07 00:25:41 +01:00
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
if (ctl->utc)
|
2017-08-26 14:38:07 +02:00
|
|
|
ret = 1; /* --utc explicitly given on command line */
|
2016-07-16 17:45:07 +02:00
|
|
|
else if (ctl->local_opt)
|
2017-08-26 14:38:07 +02:00
|
|
|
ret = 0; /* --localtime explicitly given */
|
2006-12-07 00:25:41 +01:00
|
|
|
else
|
2011-07-24 17:35:43 +02:00
|
|
|
/* get info from adjtime file - default is UTC */
|
2021-03-04 11:27:47 +01:00
|
|
|
ret = (adjtime->local_utc != LOCAL);
|
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2006-12-07 00:25:41 +01:00
|
|
|
printf(_("Assuming hardware clock is kept in %s time.\n"),
|
|
|
|
ret ? _("UTC") : _("local"));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* Read the adjustment parameters out of the /etc/adjtime file.
|
|
|
|
*
|
2017-08-28 02:26:41 +02:00
|
|
|
* Return them as the adjtime structure <*adjtime_p>. Its defaults are
|
|
|
|
* initialized in main().
|
2011-07-24 17:35:43 +02:00
|
|
|
*/
|
2016-07-16 17:45:07 +02:00
|
|
|
static int read_adjtime(const struct hwclock_control *ctl,
|
|
|
|
struct adjtime *adjtime_p)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
|
|
|
FILE *adjfile;
|
|
|
|
char line1[81]; /* String: first line of adjtime file */
|
|
|
|
char line2[81]; /* String: second line of adjtime file */
|
|
|
|
char line3[81]; /* String: third line of adjtime file */
|
2021-04-29 13:55:52 +02:00
|
|
|
int64_t last_adj_time;
|
|
|
|
int64_t last_calib_time;
|
2011-07-24 17:35:43 +02:00
|
|
|
|
2016-07-17 14:25:40 +02:00
|
|
|
if (access(ctl->adj_file_name, R_OK) != 0)
|
2017-08-28 02:26:41 +02:00
|
|
|
return EXIT_SUCCESS;
|
2006-12-07 00:25:41 +01:00
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
adjfile = fopen(ctl->adj_file_name, "r"); /* open file for reading */
|
2011-07-24 17:35:43 +02:00
|
|
|
if (adjfile == NULL) {
|
2016-07-16 17:45:07 +02:00
|
|
|
warn(_("cannot open %s"), ctl->adj_file_name);
|
2017-08-28 02:26:41 +02:00
|
|
|
return EXIT_FAILURE;
|
2006-12-07 00:25:41 +01:00
|
|
|
}
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
if (!fgets(line1, sizeof(line1), adjfile))
|
|
|
|
line1[0] = '\0'; /* In case fgets fails */
|
|
|
|
if (!fgets(line2, sizeof(line2), adjfile))
|
|
|
|
line2[0] = '\0'; /* In case fgets fails */
|
|
|
|
if (!fgets(line3, sizeof(line3), adjfile))
|
|
|
|
line3[0] = '\0'; /* In case fgets fails */
|
|
|
|
|
|
|
|
fclose(adjfile);
|
|
|
|
|
2021-04-29 13:55:52 +02:00
|
|
|
sscanf(line1, "%lf %"SCNd64" %lf",
|
|
|
|
&adjtime_p->drift_factor,
|
|
|
|
&last_adj_time,
|
|
|
|
&adjtime_p->not_adjusted);
|
2011-07-24 17:35:43 +02:00
|
|
|
|
2021-04-29 13:55:52 +02:00
|
|
|
sscanf(line2, "%"SCNd64, &last_calib_time);
|
2021-04-23 22:48:13 +02:00
|
|
|
|
2021-04-29 13:55:52 +02:00
|
|
|
adjtime_p->last_adj_time = (time_t)last_adj_time;
|
|
|
|
adjtime_p->last_calib_time = (time_t)last_calib_time;
|
2011-07-24 17:35:43 +02:00
|
|
|
|
|
|
|
if (!strcmp(line3, "UTC\n")) {
|
|
|
|
adjtime_p->local_utc = UTC;
|
|
|
|
} else if (!strcmp(line3, "LOCAL\n")) {
|
|
|
|
adjtime_p->local_utc = LOCAL;
|
|
|
|
} else {
|
|
|
|
adjtime_p->local_utc = UNKNOWN;
|
|
|
|
if (line3[0]) {
|
2011-07-24 20:30:29 +02:00
|
|
|
warnx(_("Warning: unrecognized third line in adjtime file\n"
|
|
|
|
"(Expected: `UTC' or `LOCAL' or nothing.)"));
|
2011-07-24 17:35:43 +02:00
|
|
|
}
|
|
|
|
}
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose) {
|
2021-04-29 13:55:52 +02:00
|
|
|
printf(_("Last drift adjustment done at %"PRId64" seconds after 1969\n"),
|
|
|
|
(int64_t)adjtime_p->last_adj_time);
|
|
|
|
printf(_("Last calibration done at %"PRId64" seconds after 1969\n"),
|
|
|
|
(int64_t)adjtime_p->last_calib_time);
|
2011-07-24 17:35:43 +02:00
|
|
|
printf(_("Hardware clock is on %s time\n"),
|
|
|
|
(adjtime_p->local_utc ==
|
|
|
|
LOCAL) ? _("local") : (adjtime_p->local_utc ==
|
|
|
|
UTC) ? _("UTC") : _("unknown"));
|
|
|
|
}
|
|
|
|
|
2017-08-28 02:26:41 +02:00
|
|
|
return EXIT_SUCCESS;
|
2011-07-24 17:35:43 +02:00
|
|
|
}
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* Wait until the falling edge of the Hardware Clock's update flag so that
|
|
|
|
* any time that is read from the clock immediately after we return will be
|
|
|
|
* exact.
|
|
|
|
*
|
|
|
|
* The clock only has 1 second precision, so it gives the exact time only
|
|
|
|
* once per second, right on the falling edge of the update flag.
|
|
|
|
*
|
|
|
|
* We wait (up to one second) either blocked waiting for an rtc device or in
|
|
|
|
* a CPU spin loop. The former is probably not very accurate.
|
|
|
|
*
|
|
|
|
* Return 0 if it worked, nonzero if it didn't.
|
|
|
|
*/
|
2016-07-16 17:45:07 +02:00
|
|
|
static int synchronize_to_clock_tick(const struct hwclock_control *ctl)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
2006-12-07 00:25:58 +01:00
|
|
|
int rc;
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2011-07-24 17:35:43 +02:00
|
|
|
printf(_("Waiting for clock tick...\n"));
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
rc = ur->synchronize_to_clock_tick(ctl);
|
2006-12-07 00:25:58 +01:00
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose) {
|
2008-08-09 01:22:08 +02:00
|
|
|
if (rc)
|
|
|
|
printf(_("...synchronization failed\n"));
|
|
|
|
else
|
|
|
|
printf(_("...got clock tick\n"));
|
|
|
|
}
|
2006-12-07 00:25:58 +01:00
|
|
|
|
|
|
|
return rc;
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* Convert a time in broken down format (hours, minutes, etc.) into standard
|
|
|
|
* unix time (seconds into epoch). Return it as *systime_p.
|
|
|
|
*
|
|
|
|
* The broken down time is argument <tm>. This broken down time is either
|
|
|
|
* in local time zone or UTC, depending on value of logical argument
|
|
|
|
* "universal". True means it is in UTC.
|
|
|
|
*
|
|
|
|
* If the argument contains values that do not constitute a valid time, and
|
|
|
|
* mktime() recognizes this, return *valid_p == false and *systime_p
|
|
|
|
* undefined. However, mktime() sometimes goes ahead and computes a
|
|
|
|
* fictional time "as if" the input values were valid, e.g. if they indicate
|
|
|
|
* the 31st day of April, mktime() may compute the time of May 1. In such a
|
|
|
|
* case, we return the same fictional value mktime() does as *systime_p and
|
|
|
|
* return *valid_p == true.
|
|
|
|
*/
|
2017-08-26 14:38:07 +02:00
|
|
|
static int
|
2016-07-16 17:45:07 +02:00
|
|
|
mktime_tz(const struct hwclock_control *ctl, struct tm tm,
|
2017-08-26 14:38:07 +02:00
|
|
|
time_t *systime_p)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
2017-08-26 14:38:07 +02:00
|
|
|
int valid;
|
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
if (ctl->universal)
|
2016-07-10 21:09:55 +02:00
|
|
|
*systime_p = timegm(&tm);
|
|
|
|
else
|
|
|
|
*systime_p = mktime(&tm);
|
|
|
|
if (*systime_p == -1) {
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* This apparently (not specified in mktime() documentation)
|
|
|
|
* means the 'tm' structure does not contain valid values
|
|
|
|
* (however, not containing valid values does _not_ imply
|
|
|
|
* mktime() returns -1).
|
|
|
|
*/
|
2017-08-26 14:38:07 +02:00
|
|
|
valid = 0;
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2011-07-24 17:35:43 +02:00
|
|
|
printf(_("Invalid values in hardware clock: "
|
|
|
|
"%4d/%.2d/%.2d %.2d:%.2d:%.2d\n"),
|
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
|
|
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
|
|
|
} else {
|
2017-08-26 14:38:07 +02:00
|
|
|
valid = 1;
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2021-04-29 13:55:52 +02:00
|
|
|
printf(_("Hw clock time : %4d/%.2d/%.2d %.2d:%.2d:%.2d = "
|
|
|
|
"%"PRId64" seconds since 1969\n"), tm.tm_year + 1900,
|
2011-07-24 17:35:43 +02:00
|
|
|
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min,
|
2021-04-29 13:55:52 +02:00
|
|
|
tm.tm_sec, (int64_t)*systime_p);
|
2011-07-24 17:35:43 +02:00
|
|
|
}
|
2017-08-26 14:38:07 +02:00
|
|
|
return valid;
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* Read the hardware clock and return the current time via <tm> argument.
|
|
|
|
*
|
|
|
|
* Use the method indicated by <method> argument to access the hardware
|
|
|
|
* clock.
|
|
|
|
*/
|
2008-06-17 13:12:12 +02:00
|
|
|
static int
|
2016-07-16 17:45:07 +02:00
|
|
|
read_hardware_clock(const struct hwclock_control *ctl,
|
2017-08-26 14:38:07 +02:00
|
|
|
int *valid_p, time_t *systime_p)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
|
|
|
struct tm tm;
|
|
|
|
int err;
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
err = ur->read_hardware_clock(ctl, &tm);
|
2011-07-24 17:35:43 +02:00
|
|
|
if (err)
|
|
|
|
return err;
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2021-04-29 13:55:52 +02:00
|
|
|
printf(_("Time read from Hardware Clock: %4d/%.2d/%.2d %02d:%02d:%02d\n"),
|
2011-07-24 17:35:43 +02:00
|
|
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
|
|
|
|
tm.tm_min, tm.tm_sec);
|
2017-08-26 14:38:07 +02:00
|
|
|
*valid_p = mktime_tz(ctl, tm, systime_p);
|
2008-06-17 13:12:12 +02:00
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
return 0;
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* Set the Hardware Clock to the time <newtime>, in local time zone or UTC,
|
|
|
|
* according to <universal>.
|
|
|
|
*/
|
2006-12-07 00:25:39 +01:00
|
|
|
static void
|
2016-07-16 17:45:07 +02:00
|
|
|
set_hardware_clock(const struct hwclock_control *ctl, const time_t newtime)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
|
|
|
struct tm new_broken_time;
|
|
|
|
/*
|
|
|
|
* Time to which we will set Hardware Clock, in broken down format,
|
|
|
|
* in the time zone of caller's choice
|
|
|
|
*/
|
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
if (ctl->universal)
|
2017-10-22 02:40:58 +02:00
|
|
|
gmtime_r(&newtime, &new_broken_time);
|
2011-07-24 17:35:43 +02:00
|
|
|
else
|
2017-10-22 02:40:58 +02:00
|
|
|
localtime_r(&newtime, &new_broken_time);
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2011-07-24 17:35:43 +02:00
|
|
|
printf(_("Setting Hardware Clock to %.2d:%.2d:%.2d "
|
2021-04-29 13:55:52 +02:00
|
|
|
"= %"PRId64" seconds since 1969\n"),
|
2011-07-24 17:35:43 +02:00
|
|
|
new_broken_time.tm_hour, new_broken_time.tm_min,
|
2021-04-29 13:55:52 +02:00
|
|
|
new_broken_time.tm_sec, (int64_t)newtime);
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2017-09-20 02:55:23 +02:00
|
|
|
if (!ctl->testing)
|
2016-07-16 17:45:07 +02:00
|
|
|
ur->set_hardware_clock(ctl, &new_broken_time);
|
2011-07-24 17:35:43 +02:00
|
|
|
}
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2018-07-18 13:59:15 +02:00
|
|
|
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;
|
2021-06-18 16:50:58 +02:00
|
|
|
rc = ul_path_scanf(pc, "name", "%128[^\n ]", name);
|
2018-07-18 13:59:15 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
|
|
|
* 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 exactly 2
|
|
|
|
* seconds since "refsystime") and then set the Hardware Clock to 14:03:07,
|
2018-07-18 13:59:15 +02:00
|
|
|
* thus getting a precise and retroactive setting of the clock. The .5 delay is
|
|
|
|
* default on x86, see --delay and get_hardware_delay().
|
2011-07-24 17:35:43 +02:00
|
|
|
*
|
|
|
|
* (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
|
|
|
|
* that there are two independent time scales here).
|
|
|
|
*
|
|
|
|
* This function ought to be able to accept set times as fractional times.
|
|
|
|
* Idea for future enhancement.
|
|
|
|
*/
|
2006-12-07 00:25:39 +01:00
|
|
|
static void
|
2016-07-16 17:45:07 +02:00
|
|
|
set_hardware_clock_exact(const struct hwclock_control *ctl,
|
|
|
|
const time_t sethwtime,
|
|
|
|
const struct timeval refsystime)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
|
|
|
/*
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
* The 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 is because the Hardware Clock updates to the following
|
|
|
|
* second precisely 500 ms (not 1 second!) after you release the
|
|
|
|
* divider reset (after setting the new time) - see description of
|
|
|
|
* DV2, DV1, DV0 in Register A in the MC146818A data sheet (and note
|
|
|
|
* that although that document doesn't say so, real-world code seems
|
|
|
|
* to expect that the SET bit in Register B functions the same way).
|
|
|
|
* That means that, e.g., when you set the clock to 1:02:03, it
|
|
|
|
* effectively really sets it to 1:02:03.5, because it will update to
|
|
|
|
* 1:02:04 only half a second later. Our caller passes the desired
|
|
|
|
* integer Hardware Clock time in sethwtime, and the corresponding
|
|
|
|
* system time (which may have a fractional part, and which may or may
|
|
|
|
* not be the same!) in refsystime. In an ideal situation, we would
|
|
|
|
* then apply sethwtime to the Hardware Clock at refsystime+500ms, so
|
|
|
|
* that when the Hardware Clock ticks forward to sethwtime+1s half a
|
|
|
|
* second later at refsystime+1000ms, everything is in sync. So we
|
|
|
|
* spin, waiting for gettimeofday() to return a time at or after that
|
|
|
|
* time (refsystime+500ms) up to a tolerance value, initially 1ms. If
|
|
|
|
* we miss that time due to being preempted for some other process,
|
|
|
|
* then we increase the margin a little bit (initially 1ms, doubling
|
|
|
|
* each time), add 1 second (or more, if needed to get a time that is
|
|
|
|
* in the future) to both the time for which we are waiting and the
|
|
|
|
* time that we will apply to the Hardware Clock, and start waiting
|
|
|
|
* again.
|
2019-08-29 01:25:57 +02:00
|
|
|
*
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
* For example, the caller requests that we set the Hardware Clock to
|
|
|
|
* 1:02:03, with reference time (current system time) = 6:07:08.250.
|
|
|
|
* We want the Hardware Clock to update to 1:02:04 at 6:07:09.250 on
|
|
|
|
* the system clock, and the first such update will occur 0.500
|
|
|
|
* seconds after we write to the Hardware Clock, so we spin until the
|
|
|
|
* system clock reads 6:07:08.750. If we get there, great, but let's
|
|
|
|
* imagine the system is so heavily loaded that our process is
|
|
|
|
* preempted and by the time we get to run again, the system clock
|
|
|
|
* reads 6:07:11.990. We now want to wait until the next xx:xx:xx.750
|
|
|
|
* time, which is 6:07:12.750 (4.5 seconds after the reference time),
|
|
|
|
* at which point we will set the Hardware Clock to 1:02:07 (4 seconds
|
|
|
|
* after the originally requested time). If we do that successfully,
|
|
|
|
* then at 6:07:13.250 (5 seconds after the reference time), the
|
|
|
|
* Hardware Clock will update to 1:02:08 (5 seconds after the
|
|
|
|
* originally requested time), and all is well thereafter.
|
2011-07-24 17:35:43 +02:00
|
|
|
*/
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
|
|
|
|
time_t newhwtime = sethwtime;
|
|
|
|
double target_time_tolerance_secs = 0.001; /* initial value */
|
|
|
|
double tolerance_incr_secs = 0.001; /* initial value */
|
2018-07-18 13:59:15 +02:00
|
|
|
double delay;
|
|
|
|
struct timeval rtc_set_delay_tv;
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
|
|
|
|
struct timeval targetsystime;
|
|
|
|
struct timeval nowsystime;
|
|
|
|
struct timeval prevsystime = refsystime;
|
|
|
|
double deltavstarget;
|
|
|
|
|
2018-07-18 13:59:15 +02:00
|
|
|
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);
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
|
|
|
|
while (1) {
|
|
|
|
double ticksize;
|
|
|
|
|
2017-12-24 21:38:58 +01:00
|
|
|
ON_DBG(RANDOM_SLEEP, up_to_1000ms_sleep());
|
2011-07-25 22:08:40 +02:00
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
gettimeofday(&nowsystime, NULL);
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
deltavstarget = time_diff(nowsystime, targetsystime);
|
|
|
|
ticksize = time_diff(nowsystime, prevsystime);
|
|
|
|
prevsystime = nowsystime;
|
|
|
|
|
|
|
|
if (ticksize < 0) {
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
printf(_("time jumped backward %.6f seconds "
|
2021-04-29 13:55:52 +02:00
|
|
|
"to %"PRId64".%06"PRId64" - retargeting\n"),
|
|
|
|
ticksize, (int64_t)nowsystime.tv_sec,
|
|
|
|
(int64_t)nowsystime.tv_usec);
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
/* The retarget is handled at the end of the loop. */
|
|
|
|
} else if (deltavstarget < 0) {
|
|
|
|
/* deltavstarget < 0 if current time < target time */
|
2017-12-24 21:38:58 +01:00
|
|
|
DBG(DELTA_VS_TARGET,
|
2021-04-29 13:55:52 +02:00
|
|
|
ul_debug("%"PRId64".%06"PRId64" < %"PRId64".%06"PRId64" (%.6f)",
|
|
|
|
(int64_t)nowsystime.tv_sec, (int64_t)nowsystime.tv_usec,
|
|
|
|
(int64_t)targetsystime.tv_sec,
|
|
|
|
(int64_t)targetsystime.tv_usec, deltavstarget));
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
continue; /* not there yet - keep spinning */
|
|
|
|
} else if (deltavstarget <= target_time_tolerance_secs) {
|
|
|
|
/* Close enough to the target time; done waiting. */
|
|
|
|
break;
|
|
|
|
} else /* (deltavstarget > target_time_tolerance_secs) */ {
|
|
|
|
/*
|
|
|
|
* We missed our window. Increase the tolerance and
|
|
|
|
* aim for the next opportunity.
|
|
|
|
*/
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2021-04-29 13:55:52 +02:00
|
|
|
printf(_("missed it - %"PRId64".%06"PRId64" is too far "
|
|
|
|
"past %"PRId64".%06"PRId64" (%.6f > %.6f)\n"),
|
|
|
|
(int64_t)nowsystime.tv_sec,
|
|
|
|
(int64_t)nowsystime.tv_usec,
|
|
|
|
(int64_t)targetsystime.tv_sec,
|
|
|
|
(int64_t)targetsystime.tv_usec,
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
deltavstarget,
|
|
|
|
target_time_tolerance_secs);
|
|
|
|
target_time_tolerance_secs += tolerance_incr_secs;
|
|
|
|
tolerance_incr_secs *= 2;
|
2011-07-25 22:08:40 +02:00
|
|
|
}
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Aim for the same offset (tv_usec) within the second in
|
|
|
|
* either the current second (if that offset hasn't arrived
|
|
|
|
* yet), or the next second.
|
|
|
|
*/
|
|
|
|
if (nowsystime.tv_usec < targetsystime.tv_usec)
|
|
|
|
targetsystime.tv_sec = nowsystime.tv_sec;
|
|
|
|
else
|
|
|
|
targetsystime.tv_sec = nowsystime.tv_sec + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
newhwtime = sethwtime
|
2020-04-19 07:34:55 +02:00
|
|
|
+ ceil(time_diff(nowsystime, refsystime)
|
|
|
|
- delay /* don't count this */);
|
2017-12-24 21:37:36 +01:00
|
|
|
if (ctl->verbose)
|
2021-04-29 13:55:52 +02:00
|
|
|
printf(_("%"PRId64".%06"PRId64" is close enough to %"PRId64".%06"PRId64" (%.6f < %.6f)\n"
|
|
|
|
"Set RTC to %"PRId64" (%"PRId64" + %d; refsystime = %"PRId64".%06"PRId64")\n"),
|
|
|
|
(int64_t)nowsystime.tv_sec, (int64_t)nowsystime.tv_usec,
|
|
|
|
(int64_t)targetsystime.tv_sec, (int64_t)targetsystime.tv_usec,
|
hwclock: fix possible hang and other set_hardware_clock_exact() issues
In sys-utils/hwclock.c, set_hardware_clock_exact() has some problems when the
process gets pre-empted (for more than 100ms) before reaching the time for
which it waits:
1. The "continue" statement causes execution to skip the final tdiff
assignment at the end of the do...while loop, leading to the while condition
using the wrong value of tdiff, and thus always exiting the loop once
newhwtime != sethwtime (e.g., after 1 second). This masks bug # 2, below.
2. The previously-existing bug is that because it starts over waiting for the
desired time whenever two successive calls to gettimeofday() return values >
100ms apart, the loop will never terminate unless the process holds the CPU
(without losing it for more than 100ms) for at least 500ms. This can happen
on a heavily loaded machine or on a virtual machine (or on a heavily loaded
virtual machine). This has been observed to occur, preventing a machine from
completing the shutdown or reboot process due to a "hwclock --systohc" call in
a shutdown script.
The new implementation presented in this patch takes a somewhat different
approach, intended to accomplish the same goals:
It computes the desired target system time (at which the requested hardware
clock time will be applied to the hardware clock), and waits for that time to
arrive. If it misses the time (such as due to being pre-empted for too long),
it recalculates the target time, and increases the tolerance (how late it can
be relative to the target time, and still be "close enough". Thus, if all is
well, the time will be set *very* precisely. On a machine where the hwclock
process is repeatedly pre-empted, it will set the time as precisely as is
possible under the conditions present on that particular machine. In any
case, it will always terminate eventually (and pretty quickly); it will never
hang forever.
[kzak@redhat.com: - tiny coding style changes]
Signed-off-by: Chris MacGregor <chrismacgregor@google.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
2014-02-27 19:40:59 +01:00
|
|
|
deltavstarget, target_time_tolerance_secs,
|
2021-04-29 13:55:52 +02:00
|
|
|
(int64_t)newhwtime, (int64_t)sethwtime,
|
|
|
|
(int)((int64_t)newhwtime - (int64_t)sethwtime),
|
|
|
|
(int64_t)refsystime.tv_sec, (int64_t)refsystime.tv_usec);
|
2011-07-24 17:35:43 +02:00
|
|
|
|
2016-07-16 17:45:07 +02:00
|
|
|
set_hardware_clock(ctl, newhwtime);
|
2006-12-07 00:25:39 +01:00
|
|
|
}
|
|
|
|
|
2017-09-26 01:30:24 +02:00
|
|
|
static int
|
2017-08-02 22:06:54 +02:00
|
|
|
display_time(struct timeval hwctime)
|
2011-07-24 17:35:43 +02:00
|
|
|
{
|
2017-10-15 02:37:11 +02:00
|
|
|
char buf[ISO_BUFSIZ];
|
2017-08-02 22:06:54 +02:00
|
|
|
|
2017-12-10 00:43:29 +01:00
|
|
|
if (strtimeval_iso(&hwctime, ISO_TIMESTAMP_DOT, buf, sizeof(buf)))
|
2017-09-26 01:30:24 +02:00
|
|
|
return EXIT_FAILURE;
|
2017-12-10 00:43:29 +01:00
|
|
|
|
2017-08-02 22:06:54 +02:00
|
|
|
printf("%s\n", buf);
|
2017-09-26 01:30:24 +02:00
|
|
|
return EXIT_SUCCESS;
|
2011-07-24 17:35:43 +02:00
|
|
|
}
|
2006-12-07 00:25:39 +01:00
|
|
|
|
2011-07-24 17:35:43 +02:00
|
|
|
/*
|
2017-08-13 02:13:22 +02:00
|
|
|
* Adjusts System time, sets the kernel's timezone and RTC timescale.
|
2011-07-24 17:35:43 +02:00
|
|
|
*
|
2017-08-13 02:13:22 +02:00
|
|
|
* The kernel warp_clock function adjusts the System time according to the
|
|
|
|
* tz.tz_minuteswest argument and sets PCIL (see below). At boot settimeofday(2)
|
|
|
|
* has one-shot access to this function as shown in the table below.
|
2011-07-24 17:35:43 +02:00
|
|
|
*
|
hwclock: make glibc 2.31 compatible
______________________________________________________
GNU C Library NEWS -- history of user-visible changes.
Version 2.31
Deprecated and removed features, and other changes affecting compatibility:
* The settimeofday function can still be used to set a system-wide time
zone when the operating system supports it. This is because the Linux
kernel reused the API, on some architectures, to describe a system-wide
time-zone-like offset between the software clock maintained by the kernel,
and the "RTC" clock that keeps time when the system is shut down.
However, to reduce the odds of this offset being set by accident,
settimeofday can no longer be used to set the time and the offset
simultaneously. If both of its two arguments are non-null, the call
will fail (setting errno to EINVAL).
Callers attempting to set this offset should also be prepared for the call
to fail and set errno to ENOSYS; this already happens on the Hurd and on
some Linux architectures. The Linux kernel maintainers are discussing a
more principled replacement for the reused API. After a replacement
becomes available, we will change settimeofday to fail with ENOSYS on all
platforms when its 'tzp' argument is not a null pointer.
settimeofday itself is obsolescent according to POSIX. Programs that set
the system time should use clock_settime and/or the adjtime family of
functions instead. We may cease to make settimeofday available to newly
linked binaries after there is a replacement for Linux's time-zone-like
offset API.
______________________________________________________
hwclock(8) had one settimeofday(2) call where both args were set for
--hctosys when the RTC was ticking UTC. This allowed setting the system
time, timezone, and locking the warp_clock function with a single call.
That operation now takes 3 calls of settimeofday(2).
Although this common operation now takes three calls, the overall logic
for the set_system_clock() function was simplified.
Co-Author: Karel Zak <kzak@redhat.com>
Signed-off-by: J William Piggott <elseifthen@gmx.com>
2020-02-22 02:03:47 +01:00
|
|
|
* +-------------------------------------------------------------------------+
|
|
|
|
* | settimeofday(tv, tz) |
|
|
|
|
* |-------------------------------------------------------------------------|
|
|
|
|
* | Arguments | System Time | TZ | PCIL | | warp_clock |
|
|
|
|
* | tv | tz | set | warped | set | set | firsttime | locked |
|
|
|
|
* |---------|---------|---------------|-----|------|-----------|------------|
|
|
|
|
* | pointer | NULL | yes | no | no | no | 1 | no |
|
|
|
|
* | NULL | ptr2utc | no | no | yes | no | 0 | yes |
|
|
|
|
* | NULL | pointer | no | yes | yes | yes | 0 | yes |
|
|
|
|
* +-------------------------------------------------------------------------+
|
2017-08-13 02:13:22 +02:00
|
|
|
* ptr2utc: tz.tz_minuteswest is zero (UTC).
|
|
|
|
* PCIL: persistent_clock_is_local, sets the "11 minute mode" timescale.
|
|
|
|
* firsttime: locks the warp_clock function (initialized to 1 at boot).
|
2014-09-25 13:38:25 +02:00
|
|
|
*
|
2017-08-13 02:13:22 +02:00
|
|