[LTP] [PATCH v4 2/2] Add clock_settime04 test
Cyril Hrubis
chrubis@suse.cz
Tue Jul 29 16:02:11 CEST 2025
Hi!
> Test that changing the value of the CLOCK_REALTIME clock via
> clock_settime(2) shall have no effect on a thread that is blocked
> on a relative/absolute clock_nanosleep().
>
> Signed-off-by: Andrea Cervesato <andrea.cervesato@suse.com>
> ---
> runtest/syscalls | 1 +
> testcases/kernel/syscalls/clock_settime/.gitignore | 1 +
> .../syscalls/clock_settime/clock_settime04.c | 140 +++++++++++++++++++++
> 3 files changed, 142 insertions(+)
>
> diff --git a/runtest/syscalls b/runtest/syscalls
> index e738c332bf973840e6cc0bde489882eb65018991..5f392e4845e9ac7bcda30065997a5e6a7fb56945 100644
> --- a/runtest/syscalls
> +++ b/runtest/syscalls
> @@ -112,6 +112,7 @@ leapsec01 leapsec01
> clock_settime01 clock_settime01
> clock_settime02 clock_settime02
> clock_settime03 clock_settime03
> +clock_settime04 clock_settime04
>
> clone01 clone01
> clone02 clone02
> diff --git a/testcases/kernel/syscalls/clock_settime/.gitignore b/testcases/kernel/syscalls/clock_settime/.gitignore
> index b66169b3eb7b4d8c8ea95e9e689b612d8da37b11..8bcc83d6fc9162087e99193a00b8d3d784d4737d 100644
> --- a/testcases/kernel/syscalls/clock_settime/.gitignore
> +++ b/testcases/kernel/syscalls/clock_settime/.gitignore
> @@ -1,3 +1,4 @@
> clock_settime01
> clock_settime02
> clock_settime03
> +clock_settime04
> diff --git a/testcases/kernel/syscalls/clock_settime/clock_settime04.c b/testcases/kernel/syscalls/clock_settime/clock_settime04.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..6372b038e18fbab94f2dc34b6745137eeb7dc578
> --- /dev/null
> +++ b/testcases/kernel/syscalls/clock_settime/clock_settime04.c
> @@ -0,0 +1,140 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * Copyright (c) 2025 Andrea Cervesato <andrea.cervesato@suse.com>
> + */
> +
> +/*\
> + * Verify that changing the value of the CLOCK_REALTIME clock via
> + * clock_settime() shall have no effect on a thread that is blocked on
> + * absolute/relative clock_nanosleep().
> + */
> +
> +#include "tst_test.h"
> +#include "tst_timer.h"
> +#include "tst_safe_clocks.h"
> +#include "time64_variants.h"
> +
> +#define SEC_TO_US(x) (x * 1000 * 1000)
> +
> +#define CHILD_SLEEP_US SEC_TO_US(5)
> +#define PARENT_SLEEP_US SEC_TO_US(2)
> +#define DELTA_US SEC_TO_US(1)
> +
> +static struct tst_ts *begin, *sleep_child, *sleep_parent, *end;
> +
> +static struct time64_variants variants[] = {
> + {
> + .clock_nanosleep = libc_clock_nanosleep,
> + .ts_type = TST_LIBC_TIMESPEC,
> + .desc = "vDSO or syscall with libc spec"
> + },
> +
> +#if (__NR_clock_nanosleep != __LTP__NR_INVALID_SYSCALL)
> + {
> + .clock_nanosleep = sys_clock_nanosleep,
> + .ts_type = TST_KERN_OLD_TIMESPEC,
> + .desc = "syscall with old kernel spec"
> + },
> +#endif
> +
> +#if (__NR_clock_nanosleep_time64 != __LTP__NR_INVALID_SYSCALL)
> + {
> + .clock_nanosleep = sys_clock_nanosleep64,
> + .ts_type = TST_KERN_TIMESPEC,
> + .desc = "syscall time64 with kernel spec"
> + },
> +#endif
> +};
> +
> +static void child_nanosleep(struct time64_variants *tv, const int flags)
> +{
> + long long delta;
> +
> + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, tst_ts_get(begin));
Here as well, SAFE_CLOCK_GETTIME() uses struct timespec as defined by
libc, so we have to either use syscall that corresponds to the
begin->type (tv->clock_gettime), or covert the value from
SAFE_CLOCK_GETTIME() into the right type. I guess that it would look
like:
struct timespec begin_ts;
SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, &begin_ts);
tst_ts_set_sec(begin, begin_ts.tv_sec);
tst_ts_set_nsec(begin, begin_ts.tv_nsec);
Or maybe we can add tst_ts_convert() that would convert the timespec
into a different type. That way we may do:
begin->type = TST_LIBC_TIMESPEC;
SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, tst_ts_get(begin));
tst_ts_convert(begin, tv->type);
static inline void tst_ts_convert(struct tst_ts t, enum tst_ts_type type)
{
long long sec = tst_ts_get_sec(t);
long long nsec = tst_ts_get_nsec(t);
t->type = type;
tst_ts_set_sec(t, sec);
tst_ts_set_sec(t, nsec);
}
> + if (flags & TIMER_ABSTIME) {
> + tst_res(TINFO, "Using absolute time sleep");
> +
> + *sleep_child = tst_ts_add_us(*begin, CHILD_SLEEP_US);
> + } else {
> + tst_res(TINFO, "Using relative time sleep");
> +
> + tst_ts_set_sec(sleep_child, 0);
> + tst_ts_set_nsec(sleep_child, 0);
> +
> + *sleep_child = tst_ts_add_us(*sleep_child, CHILD_SLEEP_US);
These three lines could be just:
*sleep_child = tst_tst_from_us(sleep_child->type, PARENT_SLEEP_US);
No need to zero it and then add the sleep value in us.
> + }
> +
> + TEST(tv->clock_nanosleep(CLOCK_REALTIME, flags, tst_ts_get(sleep_child), NULL));
> + if (TST_RET)
> + tst_brk(TBROK | TERRNO, "clock_nanosleep() error");
> +
> + SAFE_CLOCK_GETTIME(CLOCK_MONOTONIC, tst_ts_get(end));
Here as well, we either have to keep everything in the libc timespec and
use the tst_timespec_*() functions or covert the values properly.
> + if (tst_ts_lt(*end, *begin)) {
> + tst_res(TFAIL, "clock_settime() didn't sleep enough. "
> + "begin: %lld ms >= end: %lld ms",
> + tst_ts_to_ms(*begin),
> + tst_ts_to_ms(*end));
> + return;
> + }
> +
> + delta = tst_ts_abs_diff_us(*begin, *end);
> + if (!(flags & TIMER_ABSTIME))
> + delta -= CHILD_SLEEP_US;
> +
> + if (delta > DELTA_US) {
> + tst_res(TFAIL, "parent clock_settime() affected child sleep. "
> + "begin: %lld ms, end: %lld ms",
> + tst_ts_to_ms(*begin),
> + tst_ts_to_ms(*end));
> + return;
> + }
> +
> + tst_res(TPASS, "parent clock_settime() didn't affect child sleep "
> + "(delta time: %lld us)", delta);
> +}
> +
> +static void run(unsigned int tc_index)
> +{
> + struct time64_variants *tv = &variants[tst_variant];
> +
> + if (!SAFE_FORK()) {
> + child_nanosleep(tv, tc_index ? TIMER_ABSTIME : 0);
> + exit(0);
> + }
> +
> + SAFE_CLOCK_GETTIME(CLOCK_REALTIME, tst_ts_get(begin));
> + SAFE_CLOCK_NANOSLEEP(CLOCK_REALTIME, 0, tst_ts_get(sleep_parent), NULL);
> + SAFE_CLOCK_SETTIME(CLOCK_REALTIME, tst_ts_get(begin));
The SAFE_CLOCK_*() calls work with the struct timespec as defined by
libc. Using the tst_ts_get() here with the variant is wrong in this
case. The tst_ts_get() can be used only when we use the corresponding
tv->foo calls.
In this case we simply need to define the begin and sleep_parent as
struct timespec.
> +}
> +
> +static void setup(void)
> +{
> + begin->type = end->type = sleep_child->type = sleep_parent->type =
> + variants[tst_variant].ts_type;
> +
> + tst_ts_set_sec(sleep_parent, 0);
> + tst_ts_set_nsec(sleep_parent, 0);
> +
> + *sleep_parent = tst_ts_add_us(*sleep_parent, PARENT_SLEEP_US);
> + tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
> +}
> +
> +static struct tst_test test = {
> + .test = run,
> + .setup = setup,
> + .tcnt = 2,
> + .needs_root = 1,
> + .forks_child = 1,
> + .restore_wallclock = 1,
> + .test_variants = ARRAY_SIZE(variants),
> + .bufs = (struct tst_buffers []) {
> + {&sleep_child, .size = sizeof(struct tst_ts)},
> + {&sleep_parent, .size = sizeof(struct tst_ts)},
> + {&begin, .size = sizeof(struct tst_ts)},
> + {&end, .size = sizeof(struct tst_ts)},
> + {},
> + }
> +};
>
> --
> 2.50.1
>
>
> --
> Mailing list info: https://lists.linux.it/listinfo/ltp
--
Cyril Hrubis
chrubis@suse.cz
More information about the ltp
mailing list