mirror of
https://github.com/netbirdio/gvisor.git
synced 2026-05-22 17:12:49 -07:00
Merge pull request #450 from Pixep:feature/add-clock-boottime-as-monotonic
PiperOrigin-RevId: 258996346
This commit is contained in:
@@ -121,8 +121,15 @@ func getClock(t *kernel.Task, clockID int32) (ktime.Clock, error) {
|
||||
switch clockID {
|
||||
case linux.CLOCK_REALTIME, linux.CLOCK_REALTIME_COARSE:
|
||||
return t.Kernel().RealtimeClock(), nil
|
||||
case linux.CLOCK_MONOTONIC, linux.CLOCK_MONOTONIC_COARSE, linux.CLOCK_MONOTONIC_RAW:
|
||||
case linux.CLOCK_MONOTONIC, linux.CLOCK_MONOTONIC_COARSE,
|
||||
linux.CLOCK_MONOTONIC_RAW, linux.CLOCK_BOOTTIME:
|
||||
// CLOCK_MONOTONIC approximates CLOCK_MONOTONIC_RAW.
|
||||
// CLOCK_BOOTTIME is internally mapped to CLOCK_MONOTONIC, as:
|
||||
// - CLOCK_BOOTTIME should behave as CLOCK_MONOTONIC while also
|
||||
// including suspend time.
|
||||
// - gVisor has no concept of suspend/resume.
|
||||
// - CLOCK_MONOTONIC already includes save/restore time, which is
|
||||
// the closest to suspend time.
|
||||
return t.Kernel().MonotonicClock(), nil
|
||||
case linux.CLOCK_PROCESS_CPUTIME_ID:
|
||||
return t.ThreadGroup().CPUClock(), nil
|
||||
|
||||
@@ -37,7 +37,7 @@ func TimerfdCreate(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel
|
||||
switch clockID {
|
||||
case linux.CLOCK_REALTIME:
|
||||
c = t.Kernel().RealtimeClock()
|
||||
case linux.CLOCK_MONOTONIC:
|
||||
case linux.CLOCK_MONOTONIC, linux.CLOCK_BOOTTIME:
|
||||
c = t.Kernel().MonotonicClock()
|
||||
default:
|
||||
return 0, nil, syserror.EINVAL
|
||||
|
||||
@@ -132,6 +132,9 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
|
||||
return "CLOCK_MONOTONIC_COARSE";
|
||||
case CLOCK_MONOTONIC_RAW:
|
||||
return "CLOCK_MONOTONIC_RAW";
|
||||
case CLOCK_BOOTTIME:
|
||||
// CLOCK_BOOTTIME is a monotonic clock.
|
||||
return "CLOCK_BOOTTIME";
|
||||
default:
|
||||
return absl::StrCat(info.param);
|
||||
}
|
||||
@@ -140,15 +143,13 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
|
||||
INSTANTIATE_TEST_SUITE_P(ClockGettime, MonotonicClockTest,
|
||||
::testing::Values(CLOCK_MONOTONIC,
|
||||
CLOCK_MONOTONIC_COARSE,
|
||||
CLOCK_MONOTONIC_RAW),
|
||||
CLOCK_MONOTONIC_RAW, CLOCK_BOOTTIME),
|
||||
PrintClockId);
|
||||
|
||||
TEST(ClockGettime, UnimplementedReturnsEINVAL) {
|
||||
SKIP_IF(!IsRunningOnGvisor());
|
||||
|
||||
struct timespec tp;
|
||||
EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME, &tp),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
EXPECT_THAT(clock_gettime(CLOCK_REALTIME_ALARM, &tp),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
EXPECT_THAT(clock_gettime(CLOCK_BOOTTIME_ALARM, &tp),
|
||||
|
||||
@@ -44,21 +44,24 @@ PosixErrorOr<FileDescriptor> TimerfdCreate(int clockid, int flags) {
|
||||
//
|
||||
// - Because clock_gettime(CLOCK_MONOTONIC) is implemented through the VDSO,
|
||||
// it technically uses a closely-related, but distinct, time domain from the
|
||||
// CLOCK_MONOTONIC used to trigger timerfd expirations.
|
||||
// CLOCK_MONOTONIC used to trigger timerfd expirations. The same applies to
|
||||
// CLOCK_BOOTTIME which is an alias for CLOCK_MONOTONIC.
|
||||
absl::Duration TimerSlack() { return absl::Milliseconds(500); }
|
||||
|
||||
TEST(TimerfdTest, IsInitiallyStopped) {
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
|
||||
class TimerfdTest : public ::testing::TestWithParam<int> {};
|
||||
|
||||
TEST_P(TimerfdTest, IsInitiallyStopped) {
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
|
||||
struct itimerspec its = {};
|
||||
ASSERT_THAT(timerfd_gettime(tfd.get(), &its), SyscallSucceeds());
|
||||
EXPECT_EQ(0, its.it_value.tv_sec);
|
||||
EXPECT_EQ(0, its.it_value.tv_nsec);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, SingleShot) {
|
||||
TEST_P(TimerfdTest, SingleShot) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(1);
|
||||
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
|
||||
struct itimerspec its = {};
|
||||
its.it_value = absl::ToTimespec(kDelay);
|
||||
ASSERT_THAT(timerfd_settime(tfd.get(), /* flags = */ 0, &its, nullptr),
|
||||
@@ -72,11 +75,11 @@ TEST(TimerfdTest, SingleShot) {
|
||||
EXPECT_EQ(1, val);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, Periodic) {
|
||||
TEST_P(TimerfdTest, Periodic) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(1);
|
||||
constexpr int kPeriods = 3;
|
||||
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
|
||||
struct itimerspec its = {};
|
||||
its.it_value = absl::ToTimespec(kDelay);
|
||||
its.it_interval = absl::ToTimespec(kDelay);
|
||||
@@ -92,10 +95,10 @@ TEST(TimerfdTest, Periodic) {
|
||||
EXPECT_GE(val, kPeriods);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, BlockingRead) {
|
||||
TEST_P(TimerfdTest, BlockingRead) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(3);
|
||||
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, 0));
|
||||
auto const tfd = ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), 0));
|
||||
struct itimerspec its = {};
|
||||
its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
|
||||
auto const start_time = absl::Now();
|
||||
@@ -111,11 +114,11 @@ TEST(TimerfdTest, BlockingRead) {
|
||||
EXPECT_GE((end_time - start_time) + TimerSlack(), kDelay);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, NonblockingRead_NoRandomSave) {
|
||||
TEST_P(TimerfdTest, NonblockingRead_NoRandomSave) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(5);
|
||||
|
||||
auto const tfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
|
||||
|
||||
// Since the timer is initially disabled and has never fired, read should
|
||||
// return EAGAIN.
|
||||
@@ -148,11 +151,11 @@ TEST(TimerfdTest, NonblockingRead_NoRandomSave) {
|
||||
SyscallFailsWithErrno(EAGAIN));
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
|
||||
TEST_P(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(3);
|
||||
|
||||
auto const tfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
|
||||
struct itimerspec its = {};
|
||||
its.it_value.tv_sec = absl::ToInt64Seconds(kDelay);
|
||||
auto const start_time = absl::Now();
|
||||
@@ -181,15 +184,15 @@ TEST(TimerfdTest, BlockingPoll_SetTimeResetsExpirations) {
|
||||
SyscallFailsWithErrno(EAGAIN));
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, SetAbsoluteTime) {
|
||||
TEST_P(TimerfdTest, SetAbsoluteTime) {
|
||||
constexpr absl::Duration kDelay = absl::Seconds(3);
|
||||
|
||||
// Use a non-blocking timerfd so that if TFD_TIMER_ABSTIME is incorrectly
|
||||
// non-functional, we get EAGAIN rather than a test timeout.
|
||||
auto const tfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
|
||||
struct itimerspec its = {};
|
||||
ASSERT_THAT(clock_gettime(CLOCK_MONOTONIC, &its.it_value), SyscallSucceeds());
|
||||
ASSERT_THAT(clock_gettime(GetParam(), &its.it_value), SyscallSucceeds());
|
||||
its.it_value.tv_sec += absl::ToInt64Seconds(kDelay);
|
||||
ASSERT_THAT(timerfd_settime(tfd.get(), TFD_TIMER_ABSTIME, &its, nullptr),
|
||||
SyscallSucceeds());
|
||||
@@ -201,7 +204,34 @@ TEST(TimerfdTest, SetAbsoluteTime) {
|
||||
EXPECT_EQ(1, val);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, ClockRealtime) {
|
||||
TEST_P(TimerfdTest, IllegalReadWrite) {
|
||||
auto const tfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(GetParam(), TFD_NONBLOCK));
|
||||
uint64_t val = 0;
|
||||
EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0),
|
||||
SyscallFailsWithErrno(ESPIPE));
|
||||
EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0),
|
||||
SyscallFailsWithErrno(ESPIPE));
|
||||
}
|
||||
|
||||
std::string PrintClockId(::testing::TestParamInfo<int> info) {
|
||||
switch (info.param) {
|
||||
case CLOCK_MONOTONIC:
|
||||
return "CLOCK_MONOTONIC";
|
||||
case CLOCK_BOOTTIME:
|
||||
return "CLOCK_BOOTTIME";
|
||||
default:
|
||||
return absl::StrCat(info.param);
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AllTimerTypes, TimerfdTest,
|
||||
::testing::Values(CLOCK_MONOTONIC, CLOCK_BOOTTIME),
|
||||
PrintClockId);
|
||||
|
||||
TEST(TimerfdClockRealtimeTest, ClockRealtime) {
|
||||
// Since CLOCK_REALTIME can, by definition, change, we can't make any
|
||||
// non-flaky assertions about the amount of time it takes for a
|
||||
// CLOCK_REALTIME-based timer to expire. Just check that it expires at all,
|
||||
@@ -220,18 +250,6 @@ TEST(TimerfdTest, ClockRealtime) {
|
||||
EXPECT_EQ(1, val);
|
||||
}
|
||||
|
||||
TEST(TimerfdTest, IllegalReadWrite) {
|
||||
auto const tfd =
|
||||
ASSERT_NO_ERRNO_AND_VALUE(TimerfdCreate(CLOCK_MONOTONIC, TFD_NONBLOCK));
|
||||
uint64_t val = 0;
|
||||
EXPECT_THAT(PreadFd(tfd.get(), &val, sizeof(val), 0),
|
||||
SyscallFailsWithErrno(ESPIPE));
|
||||
EXPECT_THAT(WriteFd(tfd.get(), &val, sizeof(val)),
|
||||
SyscallFailsWithErrno(EINVAL));
|
||||
EXPECT_THAT(PwriteFd(tfd.get(), &val, sizeof(val), 0),
|
||||
SyscallFailsWithErrno(ESPIPE));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace testing
|
||||
|
||||
@@ -39,6 +39,8 @@ std::string PrintClockId(::testing::TestParamInfo<clockid_t> info) {
|
||||
return "CLOCK_MONOTONIC";
|
||||
case CLOCK_REALTIME:
|
||||
return "CLOCK_REALTIME";
|
||||
case CLOCK_BOOTTIME:
|
||||
return "CLOCK_BOOTTIME";
|
||||
default:
|
||||
return absl::StrCat(info.param);
|
||||
}
|
||||
@@ -95,7 +97,8 @@ TEST_P(CorrectVDSOClockTest, IsCorrect) {
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ClockGettime, CorrectVDSOClockTest,
|
||||
::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME),
|
||||
::testing::Values(CLOCK_MONOTONIC, CLOCK_REALTIME,
|
||||
CLOCK_BOOTTIME),
|
||||
PrintClockId);
|
||||
|
||||
} // namespace
|
||||
|
||||
+4
-1
@@ -33,6 +33,8 @@ int __common_clock_gettime(clockid_t clock, struct timespec* ts) {
|
||||
ret = ClockRealtime(ts);
|
||||
break;
|
||||
|
||||
case CLOCK_BOOTTIME:
|
||||
// Fallthrough, CLOCK_BOOTTIME is an alias for CLOCK_MONOTONIC
|
||||
case CLOCK_MONOTONIC:
|
||||
ret = ClockMonotonic(ts);
|
||||
break;
|
||||
@@ -122,7 +124,8 @@ extern "C" int __kernel_clock_getres(clockid_t clock, struct timespec* res) {
|
||||
|
||||
switch (clock) {
|
||||
case CLOCK_REALTIME:
|
||||
case CLOCK_MONOTONIC: {
|
||||
case CLOCK_MONOTONIC:
|
||||
case CLOCK_BOOTTIME: {
|
||||
res->tv_sec = 0;
|
||||
res->tv_nsec = 1;
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user