Re: [arch-projects] hwclock adjust file
[cc'ing arch-projects and Kurt Bosch as they might have opinions] Context: I suggested to Dave that we should move /var/lib/hwclock/adjfile to /etc (which is what upstream does). Sorry for the verbosity, hwclock does this to me... On Sun, Jun 5, 2011 at 5:21 PM, Dave Reisner <d@falconindy.com> wrote:
Poked around at this, and apparently it's FHS that recommends putting the adjtime file in /var/lib/hwclock, instead of /etc:
Hm, didn't know this. That said, I think we should (always) ignore FHS.
In poking around, I noticed various bugs related to a readonly root which made writing to this file problematic. Fedora has gone back to using /etc as well.
Seems to me like /var/lib/hwclock is the appropriate place for it.
I don't really have a "real" solution to this problem. It seems hwclock is really, really messed up. That said, maybe I'm missing the point. I'll explain the way I see it, and you tell me if i got it all wrong. Assumption: - We must never ever make system time go backwards. - We want to find the correct solution and push it upstream rather than work around it in Arch. UTC/LOCALTIME: When the rtc device is instantiated by the kernel the system time is set, assuming rtc is in UTC. If it is not, then we must reset the time to the correct timezone using "hwclock --systz" (we probably want to call this even if we are in UTC as it tells the kernel what timezone we are in for the benefit of fat filesystems (I know, wtf!?)). I believe already here we have a problem, as there is a gap between "system time not set" and "system time set correctly", where the time might be several hours in the future. We should at least make this gap as small as possible, and make sure that things like udev does not get started in this gap. This means that we must know very early whether we are in UTC or in LOCALTIME, storing this information in /var makes it impossible (so we work around it at the moment by having a redundant entry in rc.conf and essentially ignoring the timezone info of adjtime, and upstream work around it by having adjtime in /etc). Another option, that I have considered in the past is to add a module option to rtc-cmos, so you can simply do rtc-cmos.timezone=120 (for CEST) or rtc-cmos.timezone=0 for UTC. However, that's ugly, as I don't think it would be possible to pass it the real timezone, as that would require the kernel to keep a copy of /usr/share/zoneinfo, rather we'd have to pass the offset in minutes, and we would have to adjust this when DST kicks in. That said, maybe there is a nice way to do this that I'm not aware of, as I'm no kernel time/timezone expert. DRIFT This is the original purpose of adjtime, and I believe it belongs in /var (maybe this was the situation when FHS was written ;-) ). My understanding: The rtc runs at a relatively constant frequency, however this frequency is not very accurate. The system time on the other hand has frequency which is both constant and accurate. By measuring how the rtc and the system time drift, we are able to calculate the real frequency of the rtc, and adjusting for this we are able to get a very accurate time reading even when the system time has not been running (computer off or sleeping). This is useful if you want to keep a good time, but don't have a network connection, so cannot use ntp. To do this, we must be able to store somewhere the time when we last adjusted the rtc (so we know how long it has been drifting). If we want our system time to be adjust ed for drift at boot, we need to do "hwclock --adjust; hwclock --hctosys". The call to --adjust needs to save to a file what time it was done at (maybe this could be done with some /run magic, but I don't think it would be pretty, and it would at any rate require adjtime to be in /etc), and the call to --hctosys may only be done very early in boot (otherwise time might go backwards). I think the correct solution is to keep the drift information in /var, and only call "hwclock --adjust" (without the --hctosys) after the system is up and running (possibly just before shutdown/sleep), and never call "hwclock --systohc" automatically (this is only to be done when the system time is known to be correct, e.g. after setting the time). This means that the system time will be a bit wrong, but the errors will not accumulate (you are only off by the amount you drifted after the last clean shutdown, which in a non-networked scenario should be negligible, and a reboot will get you "perfect" time). Note: if ntpd/dual-boot is happening, the adjustment should never be done by hwclock, as we will not know the time since last --adjust, and we will over-shoot the adjustment. TL;DR: I propose splitting adjtime in two (upstream), one for /var (drift) and one for /etc (TZ), if this can be done in a clever way to not lose any backwards compatibility. Cheers, Tom PS If no one points out any mistakes in what I wrote, I might continue the work by Kurt Bosch, and simplify our hwclock handling further.
Sorry, to reply to myself, but as always talking about my problems makes it all clearer: On Sun, Jun 5, 2011 at 7:12 PM, Tom Gundersen <teg@jklm.no> wrote:
I don't really have a "real" solution to this problem. It seems hwclock is really, really messed up. That said, maybe I'm missing the point. I'll explain the way I see it, and you tell me if i got it all wrong.
Turns out I missed the point, and that things are actually fine they way they are upstream. We should slightly change our initscripts though, and move adjfile to /etc. The point being, that we never need to write to adjfile on boot, but we can read it, it will only be written to on shutdown. Furthermore, --systohc/--set will only be called manually by the sysadmin when s/he knows that it makes sense to calibrate the time and drift. --systohc, if you know that your system has been running since the last --set/--systohc this can be used to calculate the drift-rate. --set, this is (obviously) used to set the real time, and to calculate the drift-rate since the last --set/--systohc. -t
This allows HARDWARECLOCK to be removed from rc.conf, if you want to rely on /var/lib/hwclock/adjtime (or wherever the file ends up in the future). It assumes the relevant path is on the rootfs. Furthermore, we can assume (since kernel2.6.??) that the rtc modules set the time from rtc themselves, so our job is just to adjust the tz. (FWIW, this coincides with what systemd does). To get the old behavior of not touching the rtc, set it to anything anything else. We should probably have chosen a syntax for this, like IGNORE. Maybe another time... Signed-off-by: Tom Gundersen <teg@jklm.no> --- rc.sysinit | 11 ++++++----- 1 files changed, 6 insertions(+), 5 deletions(-) diff --git a/rc.sysinit b/rc.sysinit index b2b0943..7b42388 100755 --- a/rc.sysinit +++ b/rc.sysinit @@ -43,10 +43,11 @@ findmnt / --options ro &>/dev/null || minilogd bootlogd -p /run/bootlogd.pid -HWCLOCK_PARAMS="--hctosys" +HWCLOCK_PARAMS="--systz" case $HARDWARECLOCK in - UTC) HWCLOCK_PARAMS+=" --utc";; - localtime) HWCLOCK_PARAMS+=" --localtime";; + "") ;; + UTC) HWCLOCK_PARAMS+=" --utc --noadjfile";; + localtime) HWCLOCK_PARAMS+=" --localtime --noadjfile";; *) HWCLOCK_PARAMS="";; esac @@ -63,7 +64,7 @@ if [[ $HWCLOCK_PARAMS ]]; then done fi - # Do a clock set here for a few reasons: + # Adjust the system time for timezone offset if rtc is not in UTC # 1. Make creation time on udev nodes sane (FS#8665) # 2. Filesystem checks can depend on system time # 3. This will set the clock, if using non-UTC, off the last known @@ -72,7 +73,7 @@ if [[ $HWCLOCK_PARAMS ]]; then # This does *NOT* take into account a time adjustment file as /var may not be # mounted yet. A second set may occur in rc.d/hwclock to match rc.conf. if [[ -f /etc/localtime ]]; then - hwclock $HWCLOCK_PARAMS --noadjfile + hwclock $HWCLOCK_PARAMS fi fi -- 1.7.5.4
Before: adjust and set time on boot, adjust rtc every hour, and on shutdown. Now: do not set time on boot, adjust rtc on shutdown. Rationale: 1) We can never ever set the systemtime after system is up and running, as this might make time go backwards (unless we are careful à la ntpd, but we don't want to get into that business). 2) We are under the assumption that exact time is not needed, we are just trying to avoid large, accumulating drifts over time. With the new (significantly simpler) implementation systemtime will be off by the drift since the last clean shutdown. With the old implementation systemtime would be off by the drift since last shutdown (even if it was unclean). Either way the drifts would not accumulate, and a clean reboot would get you a "perfect" time. Signed-off-by: Tom Gundersen <teg@jklm.no> --- Makefile | 3 +-- adjtime | 15 --------------- hwclock | 23 +++++++---------------- 3 files changed, 8 insertions(+), 33 deletions(-) delete mode 100755 adjtime diff --git a/Makefile b/Makefile index fcabb0e..ff7d907 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ VER := $(shell git describe) -DIRS := /etc/rc.d /etc/conf.d /etc/rc.d/functions.d /etc/cron.hourly /sbin /etc/bash_completion.d /usr/share/zsh/site-functions +DIRS := /etc/rc.d /etc/conf.d /etc/rc.d/functions.d /sbin /etc/bash_completion.d /usr/share/zsh/site-functions minilogd: minilogd.o @@ -9,7 +9,6 @@ installdirs: install: minilogd installdirs install -m644 -t $(DESTDIR)/etc inittab rc.conf install -m755 -t $(DESTDIR)/etc rc.local rc.local.shutdown rc.multi rc.shutdown rc.single rc.sysinit - install -m755 -t $(DESTDIR)/etc/cron.hourly adjtime install -m644 -t $(DESTDIR)/etc/rc.d functions install -m755 -t $(DESTDIR)/etc/rc.d hwclock network netfs install -m755 -t $(DESTDIR)/sbin minilogd rc.d diff --git a/adjtime b/adjtime deleted file mode 100755 index 6a825ca..0000000 --- a/adjtime +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -# Update our hwclock for system drift - -. /etc/rc.conf - -HWCLOCK_PARAMS="--adjust" -case $HARDWARECLOCK in - UTC) HWCLOCK_PARAMS+=" --utc";; - localtime) HWCLOCK_PARAMS+=" --localtime";; - *) HWCLOCK_PARAMS="";; -esac - -if [[ $HWCLOCK_PARAMS && -f /run/daemons/hwclock ]]; then - hwclock $HWCLOCK_PARAMS -fi diff --git a/hwclock b/hwclock index 117ab14..5996b95 100755 --- a/hwclock +++ b/hwclock @@ -11,27 +11,18 @@ esac case "$1" in start) - if [[ $HWCLOCK_PARAMS ]]; then - status "Adjusting Hardware Clock" \ - hwclock --adjust - stat_busy "Setting System Clock" - hwclock --hctosys $HWCLOCK_PARAMS || stat_die - stat_done - # Note: This also enables /etc/cron.hourly/adjtime - add_daemon hwclock - fi - ;; + add_daemon hwclock;; stop) - if [[ $HWCLOCK_PARAMS ]]; then - stat_busy "Saving System Clock" - hwclock --systohc $HWCLOCK_PARAMS || stat_die - stat_done - fi + case $HARDWARECLOCK in + UTC) hwclock --adjust --utc;; + localtime) hwclock --adjust --localtime;; + "") hwclock --adjust;; + esac rm_daemon hwclock ;; restart) $0 stop - sleep 1 + sleep 2 $0 start ;; *) -- 1.7.5.4
On Sun, Jun 5, 2011 at 2:01 PM, Tom Gundersen <teg@jklm.no> wrote:
Sorry, to reply to myself, but as always talking about my problems makes it all clearer:
On Sun, Jun 5, 2011 at 7:12 PM, Tom Gundersen <teg@jklm.no> wrote:
I don't really have a "real" solution to this problem. It seems hwclock is really, really messed up. That said, maybe I'm missing the point. I'll explain the way I see it, and you tell me if i got it all wrong.
Turns out I missed the point, and that things are actually fine they way they are upstream. We should slightly change our initscripts though, and move adjfile to /etc.
The point being, that we never need to write to adjfile on boot, but we can read it, it will only be written to on shutdown. Furthermore, --systohc/--set will only be called manually by the sysadmin when s/he knows that it makes sense to calibrate the time and drift.
--systohc, if you know that your system has been running since the last --set/--systohc this can be used to calculate the drift-rate. --set, this is (obviously) used to set the real time, and to calculate the drift-rate since the last --set/--systohc.
We moved this out of /etc because it is written to, and /etc/ should be treated as read-only by system scripts if I'm not mistaken. I think your logic is totally off here, as /etc/ should be read only? https://bugs.archlinux.org/task/26 -Dan
On Mon, Jun 6, 2011 at 7:12 PM, Dan McGee <dpmcgee@gmail.com> wrote:
We moved this out of /etc because it is written to, and /etc/ should be treated as read-only by system scripts if I'm not mistaken. I think your logic is totally off here, as /etc/ should be read only? https://bugs.archlinux.org/task/26
You are right that this is not ideal, but the problem is ultimately an upstream one (writing to /etc on --adjust). That said, it is ok for system scripts to write to /etc as a result of manual invocation (--set or --systohc), just not automatically. So the only problem we have is with the --adjust on shutdown (which atm is not a big problem since /etc is rw on shutdown anyway). If /etc was already able to be used ro, I would not have suggested this, but it is not, and will not be for some time (waiting for libmount support to reach all the tools might take some time). I think it is better to do the writes in /etc in the short-term and submit a proper fix upstream for the long-term (this I will do, unless it becomes clear that my approach is flawed). The problem we have now is that the UTC/LOCALTIME value is stored on two different places (rc.conf and /var/lib/hwclock/adjtime), and the manpages of hwclock refer to a third one (/etc/adjtime). This means that when hwclock is called from initscripts it uses the value in rc.conf (as /var might not be mounted), but if the admin calls hwclock manually the value in /var will be used. These values might not be in sync, and then confusion/havoc would ensue (as apparently has happened for many users). In case it was not clear, I suggest the following: Now: stop patching util-linux (move adjtime to /etc). Soon: submit a patch to util-linux that keeps the "time of last write to rtc" in /var, and "driftrate" and "UTC/LOCAL" in /etc; apply this in our package if/when it is accepted upstream. If it is not accepted, surely they must have another approach, as they too want /etc to be ro. Makes sense? -t PS I'm a huge proponent of ro /etc, so I wouldn't do anything to jeopardise that.
participants (2)
-
Dan McGee
-
Tom Gundersen