[chrony-dev] [PATCH v1] rtc: handle uninitialized RTC

[ Thread Index | Date Index | More chrony.tuxfamily.org/chrony-dev Archives ]


Add a configuration option for an "initialization year/timestamp"
chornyd can use to start an uninitialized RTC, so that the RTC driver
can continue its start up, and chrony could later trim the RTC to an
accurate date.

Without this 'kick-off', on systems where the RTC would return -EINVAL
when being queried either for features, or just being read, chronyd
would be unable to start the RTC driver ("513 RTC driver not running")
and hence could/would not set it.

An example RTC where this is the case is RX8130 (of the ds1307
family), which would return -EINVAL *until* the internal oscillator is
started by writing a valid date to it.

Upstream-Status: Submitted [TODO: link!]
Signed-off-by: Johannes Schneider <johannes.schneider@xxxxxxxxxxxxxxxxxxxx>
---
 conf.c                 | 11 +++++++++++
 conf.h                 |  1 +
 doc/chrony.conf.adoc   |  5 +++++
 doc/chrony.conf.man.in |  7 +++++++
 rtc_linux.c            | 29 +++++++++++++++++++++++++++++
 5 files changed, 53 insertions(+)

diff --git a/conf.c b/conf.c
index 06ea1e5..1faffcc 100644
--- a/conf.c
+++ b/conf.c
@@ -92,6 +92,7 @@ static void parse_tempcomp(char *);
 static int print_config = 0;
 static int restarted = 0;
 static char *rtc_device;
+static int rtc_init_year = -1;
 static int acquisition_port = -1;
 static int ntp_port = NTP_PORT;
 static char *keys_file = NULL;
@@ -702,6 +703,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
     parse_double(p, &rtc_autotrim_threshold);
   } else if (!strcasecmp(command, "rtcdevice")) {
     parse_string(p, &rtc_device);
+  } else if (!strcasecmp(command, "rtcinityear")) {
+    parse_int(p, &rtc_init_year);
   } else if (!strcasecmp(command, "rtcfile")) {
     parse_string(p, &rtc_file);
   } else if (!strcasecmp(command, "rtconutc")) {
@@ -1996,6 +1999,14 @@ CNF_GetRtcDevice(void)
 
 /* ================================================== */
 
+int
+CNF_GetRtcInitYear(void)
+{
+  return rtc_init_year;
+}
+
+/* ================================================== */
+
 double
 CNF_GetMaxUpdateSkew(void)
 {
diff --git a/conf.h b/conf.h
index f81f9aa..a1f8b81 100644
--- a/conf.h
+++ b/conf.h
@@ -38,6 +38,7 @@ extern void CNF_Finalise(void);
 extern void CNF_EnablePrint(void);
 
 extern char *CNF_GetRtcDevice(void);
+extern int CNF_GetRtcInitYear(void);
 
 extern void CNF_ReadFile(const char *filename);
 extern void CNF_ParseLine(const char *filename, int number, char *line);
diff --git a/doc/chrony.conf.adoc b/doc/chrony.conf.adoc
index 21d2ce6..8ef2870 100644
--- a/doc/chrony.conf.adoc
+++ b/doc/chrony.conf.adoc
@@ -1966,6 +1966,11 @@ This would set the threshold error to 30 seconds.
 The *rtcdevice* directive sets the path to the device file for accessing the
 RTC. The default path is _@DEFAULT_RTC_DEVICE@_.
 
+[[rtcinityear]]*rtcinityear* _year_::
+Should the RTC not respond during startup of *chronyd* - this could happen if the RTC was never initialized, has a dead battery, or is in some error state - *chronyd* can try to get the RTC going by writing a timestamp to it.
+The *rtcinityear* directive sets the year to be used for this initial timestamp.
+Setting '-1' disables this feature - which is also the default.
+
 [[rtcfile]]*rtcfile* _file_::
 The *rtcfile* directive defines the name of the file in which *chronyd* can
 save parameters associated with tracking the accuracy of the RTC.
diff --git a/doc/chrony.conf.man.in b/doc/chrony.conf.man.in
index 8713321..a04a08f 100644
--- a/doc/chrony.conf.man.in
+++ b/doc/chrony.conf.man.in
@@ -2553,6 +2553,13 @@ The \fBrtcdevice\fP directive sets the path to the device file for accessing the
 RTC. The default path is \fI@DEFAULT_RTC_DEVICE@\fP.
 .RE
 .sp
+\fBrtcinityear\fP \fIyear\fP
+.RS 4
+Should the RTC not respond during startup of \fBchronyd\fP - this could happen if the RTC was never initialized, has a dead battery, or is in some error state - \fBchronyd\fP can try to get the RTC going by writing a timestamp to it.
+The \fBrtcinityear\fP directive sets the year to be used for this initial timestamp.
+Setting '-1' disables this feature - which is also the default.
+.RE
+.sp
 \fBrtcfile\fP \fIfile\fP
 .RS 4
 The \fBrtcfile\fP directive defines the name of the file in which \fBchronyd\fP can
diff --git a/rtc_linux.c b/rtc_linux.c
index f8c8c72..8eedb79 100644
--- a/rtc_linux.c
+++ b/rtc_linux.c
@@ -52,6 +52,8 @@ static void measurement_timeout(void *any);
 
 static void read_from_device(int fd_, int event, void *any);
 
+static void set_rtc(time_t new_rtc_time);
+
 /* ================================================== */
 
 typedef enum {
@@ -499,6 +501,9 @@ switch_interrupts(int on_off)
 int
 RTC_Linux_Initialise(void)
 {
+  int status;
+  struct rtc_time rtc_raw = {0};
+
   /* Try to open the device */
   fd = open(CNF_GetRtcDevice(), O_RDWR);
   if (fd < 0) {
@@ -507,6 +512,30 @@ RTC_Linux_Initialise(void)
     return 0;
   }
 
+  /* the device node is available and could be opened, but is the RTC actually
+     initizlized with a timestamp? e.g. can it be read or does the driver return
+     an error because the RTC is in a failure state (the oscilator is not
+     running; the backup batter is dead; it is "factory-fresh" = was never
+     set */
+  status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
+  if ((CNF_GetRtcInitYear() > 0) && (status < 0)) {
+    LOG(LOGS_ERR, "initial read of RTC failed, trying an initial write to get the oscilator running.");
+    /* all other parameter can be 0 - see man 3 gmtime */
+    rtc_raw.tm_mday = 1;
+    rtc_raw.tm_year = CNF_GetRtcInitYear() - 1900;
+    status = ioctl(fd, RTC_SET_TIME, &rtc_raw);
+    if (status < 0) {
+      LOG(LOGS_ERR, "initial write to the RTC failed");
+      return 0;
+    }
+    status = ioctl(fd, RTC_RD_TIME, &rtc_raw);
+    if (status < 0) {
+      LOG(LOGS_ERR, "starting the RTC by writing to it failed");
+      return 0;
+    }
+    LOG(LOGS_INFO, "initialized RTC to %i-01-01", CNF_GetRtcInitYear());
+  }
+
   /* Make sure the RTC supports interrupts */
   if (!switch_interrupts(1) || !switch_interrupts(0)) {
     close(fd);
-- 
2.34.1


-- 
To unsubscribe email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "unsubscribe" in the subject.
For help email chrony-dev-request@xxxxxxxxxxxxxxxxxxxx with "help" in the subject.
Trouble?  Email listmaster@xxxxxxxxxxxxxxxxxxxx.


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/