Cover the timens conversion in ntsync_schedule(): with a negative CLOCK_MONOTONIC offset, a 100 ms absolute wait must not return immediately against the host clock.
Suggested-by: Maoyi Xie <[email protected]> Signed-off-by: Iván Ezequiel Rodriguez <[email protected]> --- .../testing/selftests/drivers/ntsync/ntsync.c | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tools/testing/selftests/drivers/ntsync/ntsync.c b/tools/testing/selftests/drivers/ntsync/ntsync.c index e6a37214aa46..ce000ff41535 100644 --- a/tools/testing/selftests/drivers/ntsync/ntsync.c +++ b/tools/testing/selftests/drivers/ntsync/ntsync.c @@ -8,12 +8,18 @@ #define _GNU_SOURCE #include <sys/ioctl.h> #include <sys/stat.h> +#include <sys/wait.h> #include <fcntl.h> +#include <sched.h> #include <time.h> #include <pthread.h> #include <linux/ntsync.h> #include "kselftest_harness.h" +#ifndef CLONE_NEWTIME +#define CLONE_NEWTIME 0x00000080 +#endif + static int read_sem_state(int sem, __u32 *count, __u32 *max) { struct ntsync_sem_args args; @@ -1340,4 +1346,85 @@ TEST(stress_wait) close(stress_device); } +/* + * Absolute MONOTONIC timeouts must honour the caller's time namespace. + * With a negative monotonic offset, a 100 ms wait must still take ~100 ms + * of namespace time (not return immediately against the host clock). + */ +TEST(wait_any_monotonic_timens) +{ + struct ntsync_sem_args sem_args = {0}; + struct ntsync_wait_args wait_args = {0}; + struct timespec start, end; + char buf[64]; + __u64 elapsed_ns; + int fd, offset_fd, sem, ret, status, len; + pid_t pid; + + if (access("/proc/self/ns/time", F_OK)) + SKIP(return, "Time namespaces are not supported"); + + fd = open("/dev/ntsync", O_CLOEXEC | O_RDONLY); + if (fd < 0) + SKIP(return, "/dev/ntsync is not available"); + + ret = unshare(CLONE_NEWTIME); + if (ret) { + close(fd); + if (errno == EPERM) + SKIP(return, "need CAP_SYS_ADMIN for CLONE_NEWTIME"); + ASSERT_EQ(0, ret); + } + + len = snprintf(buf, sizeof(buf), "%d %d 0", CLOCK_MONOTONIC, -10); + offset_fd = open("/proc/self/timens_offsets", O_WRONLY); + ASSERT_LE(0, offset_fd); + ASSERT_EQ(len, write(offset_fd, buf, len)); + close(offset_fd); + + pid = fork(); + ASSERT_LE(0, pid); + if (!pid) { + int obj; + + sem_args.count = 0; + sem_args.max = 1; + sem = ioctl(fd, NTSYNC_IOC_CREATE_SEM, &sem_args); + if (sem < 0) + _exit(1); + + obj = sem; + wait_args.timeout = get_abs_timeout(100); + wait_args.objs = (uintptr_t)&obj; + wait_args.count = 1; + wait_args.owner = 123; + wait_args.index = 0xdeadbeef; + + if (clock_gettime(CLOCK_MONOTONIC, &start)) + _exit(2); + ret = ioctl(fd, NTSYNC_IOC_WAIT_ANY, &wait_args); + if (clock_gettime(CLOCK_MONOTONIC, &end)) + _exit(2); + + if (ret != -1 || errno != ETIMEDOUT) + _exit(3); + + elapsed_ns = (end.tv_sec - start.tv_sec) * 1000000000ULL + + (end.tv_nsec - start.tv_nsec); + /* Without timens conversion this returns in ~0 ms. */ + if (elapsed_ns < 50 * 1000000ULL) + _exit(4); + if (elapsed_ns > 1000 * 1000000ULL) + _exit(5); + + _exit(0); + } + + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + EXPECT_TRUE(WIFEXITED(status)); + EXPECT_EQ(0, WEXITSTATUS(status)); + + close(fd); +} + TEST_HARNESS_MAIN -- 2.43.0

