selftests: ntsync: test absolute MONOTONIC waits under time namespaces

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 <maoyixie.tju@gmail.com>
Signed-off-by: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
Reviewed-by: Elizabeth Figura <zfigura@codeweavers.com>
Signed-off-by: Elizabeth Figura <zfigura@codeweavers.com>
Link: https://patch.msgid.link/20260723201301.11826-6-zfigura@codeweavers.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Iván Ezequiel Rodriguez 2026-07-23 15:13:01 -05:00 committed by Greg Kroah-Hartman
parent 2fded3140d
commit facd8463a5

View File

@ -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;
@ -1384,4 +1390,85 @@ TEST(wait_args_validation)
close(fd);
}
/*
* 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