Skip to content

Commit c1ef667

Browse files
committed
libc: make timerfd_* functions handle wall clock jumps
When wall clock jumps backwards triggered for example by host clock change and propagated through kvmclock synchronization mechanism implemented by the commit e694d06, 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 'expiration' + 'interval' timepoint. 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 <jwkozaczuk@gmail.com>
1 parent 752b6bb commit c1ef667

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

libc/timerfd.cc

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ int timerfd::read(uio *data, int flags)
179179
}
180180

181181
WITH_LOCK(_mutex) {
182+
again:
182183
while (!_expiration || _wakeup_due) {
183184
if (f_flags & O_NONBLOCK) {
184185
return EAGAIN;
@@ -193,14 +194,21 @@ int timerfd::read(uio *data, int flags)
193194
_expiration = 0;
194195
} else {
195196
auto now = time_now();
196-
// set next wakeup for the next multiple of interval from
197-
// _expiration which is after "now".
198-
assert (now >= _expiration);
199-
u64 count = (now - _expiration) / _interval;
200-
_expiration = _expiration + (count+1) * _interval;
201-
_wakeup_due = _expiration;
202-
_wakeup_change_cond.wake_one();
203-
ret = 1 + count;
197+
if (_clockid == CLOCK_MONOTONIC || now >= _expiration) {
198+
// set next wakeup for the next multiple of interval from
199+
// _expiration which is after "now".
200+
assert (now >= _expiration);
201+
u64 count = (now - _expiration) / _interval;
202+
_expiration = _expiration + (count+1) * _interval;
203+
_wakeup_due = _expiration;
204+
_wakeup_change_cond.wake_one();
205+
ret = 1 + count;
206+
} else {
207+
// Clock is REALTIME and now < _expiration (clock may have jumped backwards)
208+
_wakeup_due = _expiration + _interval;
209+
_wakeup_change_cond.wake_one();
210+
goto again;
211+
}
204212
}
205213
copy_to_uio((const char *)&ret, sizeof(ret), data);
206214
return 0;

0 commit comments

Comments
 (0)