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 <[email protected]>
---
 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

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/osv-dev/20200313014725.14928-1-jwkozaczuk%40gmail.com.

Reply via email to