When wall clock jumps backwards triggered for example by host clock
change and propagated through kvmclock synchronization mechanism
implemented by the commit
https://github.com/cloudius-systems/osv/commit/e694d066d20d87897d4a9b6dc721c0926d0fdc74,
timerfd::read() may see 'now' timepoint before the 'expiration' upon
wakeup event which is opposite to what the current implementation
expects and leads to broken assert crash.
This patch fixes the implementation of timerfd::read() by
differentiating between CLOCK_MONOTONIC and CLOCK_REALTIME
and handling the case when clock is realtime (wall clock)
and 'now' is before the 'expiration'. In which case we
simply re-arm to wake up at the same 'expiration' timepoint
which is still ahead.
Partially addresses #1076. Please note that we need to adjust
tst-timerfd to account for clock jumping forwards or backwards.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
libc/timerfd.cc | 24 ++++++++++++++++--------
1 file changed, 16 insertions(+), 8 deletions(-)
diff --git a/libc/timerfd.cc b/libc/timerfd.cc
index 5d382fa3..70500dca 100644
--- a/libc/timerfd.cc
+++ b/libc/timerfd.cc
@@ -179,6 +179,7 @@ int timerfd::read(uio *data, int flags)
}
WITH_LOCK(_mutex) {
+again:
while (!_expiration || _wakeup_due) {
if (f_flags & O_NONBLOCK) {
return EAGAIN;
@@ -193,14 +194,21 @@ int timerfd::read(uio *data, int flags)
_expiration = 0;
} else {
auto now = time_now();
- // set next wakeup for the next multiple of interval from
- // _expiration which is after "now".
- assert (now >= _expiration);
- u64 count = (now - _expiration) / _interval;
- _expiration = _expiration + (count+1) * _interval;
- _wakeup_due = _expiration;
- _wakeup_change_cond.wake_one();
- ret = 1 + count;
+ if (_clockid == CLOCK_MONOTONIC || now >= _expiration) {
+ // set next wakeup for the next multiple of interval from
+ // _expiration which is after "now".
+ assert (now >= _expiration);
+ u64 count = (now - _expiration) / _interval;
+ _expiration = _expiration + (count+1) * _interval;
+ _wakeup_due = _expiration;
+ _wakeup_change_cond.wake_one();
+ ret = 1 + count;
+ } else {
+ // Clock is REALTIME and now < _expiration (clock may have jumped backwards)
+ _wakeup_due = _expiration;
+ _wakeup_change_cond.wake_one();
+ goto again;
+ }
}
copy_to_uio((const char *)&ret, sizeof(ret), data);
return 0;
--
2.20.1