Commit 6d029c25 authored by Oleg Nesterov's avatar Oleg Nesterov Committed by Thomas Gleixner

selftests/timers/posix_timers: Reimplement check_timer_distribution()

check_timer_distribution() runs ten threads in a busy loop and tries to
test that the kernel distributes a process posix CPU timer signal to every
thread over time.

There is not guarantee that this is true even after commit bcb7ee79
("posix-timers: Prefer delivery of signals to the current thread") because
that commit only avoids waking up the sleeping process leader thread, but
that has nothing to do with the actual signal delivery.

As the signal is process wide the first thread which observes sigpending
and wins the race to lock sighand will deliver the signal. Testing shows
that this hangs on a regular base because some threads never win the race.

The comment "This primarily tests that the kernel does not favour any one."
is wrong. The kernel does favour a thread which hits the timer interrupt
when CLOCK_PROCESS_CPUTIME_ID expires.

Rewrite the test so it only checks that the group leader sleeping in join()
never receives SIGALRM and the thread which burns CPU cycles receives all
signals.

In older kernels which do not have commit bcb7ee79 ("posix-timers:
Prefer delivery of signals to the current thread") the test-case fails
immediately, the very 1st tick wakes the leader up. Otherwise it quickly
succeeds after 100 ticks.

CI testing wants to use newer selftest versions on stable kernels. In this
case the test is guaranteed to fail.

So check in the failure case whether the kernel version is less than v6.3
and skip the test result in that case.

[ tglx: Massaged change log, renamed the version check helper ]

Fixes: e797203f ("selftests/timers/posix_timers: Test delivery of signals across threads")
Signed-off-by: default avatarOleg Nesterov <oleg@redhat.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20240409133802.GD29396@redhat.com
parent c1d11fc2
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
#include <stdarg.h> #include <stdarg.h>
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
#include <sys/utsname.h>
#endif #endif
#ifndef ARRAY_SIZE #ifndef ARRAY_SIZE
...@@ -388,4 +389,16 @@ static inline __printf(1, 2) int ksft_exit_skip(const char *msg, ...) ...@@ -388,4 +389,16 @@ static inline __printf(1, 2) int ksft_exit_skip(const char *msg, ...)
exit(KSFT_SKIP); exit(KSFT_SKIP);
} }
static inline int ksft_min_kernel_version(unsigned int min_major,
unsigned int min_minor)
{
unsigned int major, minor;
struct utsname info;
if (uname(&info) || sscanf(info.release, "%u.%u.", &major, &minor) != 2)
ksft_exit_fail_msg("Can't parse kernel version\n");
return major > min_major || (major == min_major && minor >= min_minor);
}
#endif /* __KSELFTEST_H */ #endif /* __KSELFTEST_H */
...@@ -184,80 +184,71 @@ static int check_timer_create(int which) ...@@ -184,80 +184,71 @@ static int check_timer_create(int which)
return 0; return 0;
} }
int remain; static pthread_t ctd_thread;
__thread int got_signal; static volatile int ctd_count, ctd_failed;
static void *distribution_thread(void *arg) static void ctd_sighandler(int sig)
{ {
while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); if (pthread_self() != ctd_thread)
return NULL; ctd_failed = 1;
ctd_count--;
} }
static void distribution_handler(int nr) static void *ctd_thread_func(void *arg)
{ {
if (!__atomic_exchange_n(&got_signal, 1, __ATOMIC_RELAXED))
__atomic_fetch_sub(&remain, 1, __ATOMIC_RELAXED);
}
/*
* Test that all running threads _eventually_ receive CLOCK_PROCESS_CPUTIME_ID
* timer signals. This primarily tests that the kernel does not favour any one.
*/
static int check_timer_distribution(void)
{
int err, i;
timer_t id;
const int nthreads = 10;
pthread_t threads[nthreads];
struct itimerspec val = { struct itimerspec val = {
.it_value.tv_sec = 0, .it_value.tv_sec = 0,
.it_value.tv_nsec = 1000 * 1000, .it_value.tv_nsec = 1000 * 1000,
.it_interval.tv_sec = 0, .it_interval.tv_sec = 0,
.it_interval.tv_nsec = 1000 * 1000, .it_interval.tv_nsec = 1000 * 1000,
}; };
timer_t id;
remain = nthreads + 1; /* worker threads + this thread */ /* 1/10 seconds to ensure the leader sleeps */
signal(SIGALRM, distribution_handler); usleep(10000);
err = timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id);
if (err < 0) {
ksft_perror("Can't create timer");
return -1;
}
err = timer_settime(id, 0, &val, NULL);
if (err < 0) {
ksft_perror("Can't set timer");
return -1;
}
for (i = 0; i < nthreads; i++) { ctd_count = 100;
err = pthread_create(&threads[i], NULL, distribution_thread, if (timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id))
NULL); return "Can't create timer\n";
if (err) { if (timer_settime(id, 0, &val, NULL))
ksft_print_msg("Can't create thread: %s (%d)\n", return "Can't set timer\n";
strerror(errno), errno);
return -1;
}
}
/* Wait for all threads to receive the signal. */ while (ctd_count > 0 && !ctd_failed)
while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); ;
for (i = 0; i < nthreads; i++) { if (timer_delete(id))
err = pthread_join(threads[i], NULL); return "Can't delete timer\n";
if (err) {
ksft_print_msg("Can't join thread: %s (%d)\n",
strerror(errno), errno);
return -1;
}
}
if (timer_delete(id)) { return NULL;
ksft_perror("Can't delete timer"); }
return -1;
} /*
* Test that only the running thread receives the timer signal.
*/
static int check_timer_distribution(void)
{
const char *errmsg;
ksft_test_result_pass("check_timer_distribution\n"); signal(SIGALRM, ctd_sighandler);
errmsg = "Can't create thread\n";
if (pthread_create(&ctd_thread, NULL, ctd_thread_func, NULL))
goto err;
errmsg = "Can't join thread\n";
if (pthread_join(ctd_thread, (void **)&errmsg) || errmsg)
goto err;
if (!ctd_failed)
ksft_test_result_pass("check signal distribution\n");
else if (ksft_min_kernel_version(6, 3))
ksft_test_result_fail("check signal distribution\n");
else
ksft_test_result_skip("check signal distribution (old kernel)\n");
return 0; return 0;
err:
ksft_print_msg(errmsg);
return -1;
} }
int main(int argc, char **argv) int main(int argc, char **argv)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment