| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com> | |
| * | |
| * Selftests for a few posix timers interface. | |
| * | |
| * Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com> | |
| */ | |
| #define _GNU_SOURCE | |
| #include <sys/prctl.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <stdio.h> | |
| #include <signal.h> | |
| #include <stdint.h> | |
| #include <string.h> | |
| #include <unistd.h> | |
| #include <time.h> | |
| #include <pthread.h> | |
| #include <stdbool.h> | |
| #include "clock-helpers.h" | |
| #include "kselftest.h" | |
| #define DELAY 2 | |
| static void __fatal_error(const char *test, const char *name, const char *what) | |
| { | |
| char buf[64]; | |
| char *ret_str = NULL; | |
| ret_str = strerror_r(errno, buf, sizeof(buf)); | |
| if (name && strlen(name) && ret_str) | |
| ksft_exit_fail_msg("%s %s %s %s\n", test, name, what, ret_str); | |
| else if (ret_str) | |
| ksft_exit_fail_msg("%s %s %s\n", test, what, ret_str); | |
| else | |
| ksft_exit_fail_msg("%s %s\n", test, what); | |
| } | |
| #define fatal_error(name, what) __fatal_error(__func__, name, what) | |
| static volatile int done; | |
| /* Busy loop in userspace to elapse ITIMER_VIRTUAL */ | |
| static void user_loop(void) | |
| { | |
| while (!done); | |
| } | |
| /* | |
| * Try to spend as much time as possible in kernelspace | |
| * to elapse ITIMER_PROF. | |
| */ | |
| static void kernel_loop(void) | |
| { | |
| void *addr = sbrk(0); | |
| int err = 0; | |
| while (!done && !err) { | |
| err = brk(addr + 4096); | |
| err |= brk(addr); | |
| } | |
| } | |
| /* | |
| * Sleep until ITIMER_REAL expiration. | |
| */ | |
| static void idle_loop(void) | |
| { | |
| pause(); | |
| } | |
| static void sig_handler(int nr) | |
| { | |
| done = 1; | |
| } | |
| static inline int64_t calcdiff_ns(struct timespec t1, struct timespec t2) | |
| { | |
| int64_t diff; | |
| diff = NSEC_PER_SEC * (int64_t)((int) t1.tv_sec - (int) t2.tv_sec); | |
| diff += ((int) t1.tv_nsec - (int) t2.tv_nsec); | |
| return diff; | |
| } | |
| /* | |
| * Check the expected timer expiration matches the GTOD elapsed delta since | |
| * we armed the timer. Keep a 0.5 sec error margin due to various jitter. | |
| */ | |
| static int check_diff(struct timespec start, struct timespec end) | |
| { | |
| long long diff = calcdiff_ns(end, start); | |
| if (llabs(diff - DELAY * NSEC_PER_SEC) > NSEC_PER_SEC / 2) { | |
| printf("Diff too high: %lld ns..", diff); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| static void check_itimer(int which, const char *name) | |
| { | |
| struct timespec start, end; | |
| struct itimerval val = { | |
| .it_value.tv_sec = DELAY, | |
| }; | |
| int clock_id = CLOCK_REALTIME; | |
| done = 0; | |
| if (which == ITIMER_VIRTUAL) | |
| signal(SIGVTALRM, sig_handler); | |
| else if (which == ITIMER_PROF) { | |
| clock_id = CLOCK_THREAD_CPUTIME_ID; | |
| signal(SIGPROF, sig_handler); | |
| } | |
| else if (which == ITIMER_REAL) | |
| signal(SIGALRM, sig_handler); | |
| if (clock_gettime(clock_id, &start)) | |
| fatal_error(name, "clock_gettime()"); | |
| if (setitimer(which, &val, NULL) < 0) | |
| fatal_error(name, "setitimer()"); | |
| if (which == ITIMER_VIRTUAL) | |
| user_loop(); | |
| else if (which == ITIMER_PROF) | |
| kernel_loop(); | |
| else if (which == ITIMER_REAL) | |
| idle_loop(); | |
| if (clock_gettime(clock_id, &end)) | |
| fatal_error(name, "clock_gettime()"); | |
| ksft_test_result(check_diff(start, end) == 0, "%s\n", name); | |
| } | |
| static void check_timer_create(int which) | |
| { | |
| const char *name = clock_name(which); | |
| struct timespec start, end; | |
| struct itimerspec val = { | |
| .it_value.tv_sec = DELAY, | |
| }; | |
| int clock_id = CLOCK_REALTIME; | |
| timer_t id; | |
| done = 0; | |
| if (timer_create(which, NULL, &id) < 0) | |
| fatal_error(name, "timer_create()"); | |
| if (signal(SIGALRM, sig_handler) == SIG_ERR) | |
| fatal_error(name, "signal()"); | |
| if (clock_gettime(clock_id, &start)) | |
| fatal_error(name, "clock_gettime()"); | |
| if (timer_settime(id, 0, &val, NULL) < 0) | |
| fatal_error(name, "timer_settime()"); | |
| user_loop(); | |
| if (clock_gettime(clock_id, &end)) | |
| fatal_error(name, "clock_gettime()"); | |
| ksft_test_result(check_diff(start, end) == 0, | |
| "timer_create() per %s\n", name); | |
| } | |
| static pthread_t ctd_thread; | |
| static volatile int ctd_count, ctd_failed; | |
| static void ctd_sighandler(int sig) | |
| { | |
| if (pthread_self() != ctd_thread) | |
| ctd_failed = 1; | |
| ctd_count--; | |
| } | |
| static void *ctd_thread_func(void *arg) | |
| { | |
| struct itimerspec val = { | |
| .it_value.tv_sec = 0, | |
| .it_value.tv_nsec = 1000 * 1000, | |
| .it_interval.tv_sec = 0, | |
| .it_interval.tv_nsec = 1000 * 1000, | |
| }; | |
| timer_t id; | |
| /* 1/10 seconds to ensure the leader sleeps */ | |
| usleep(10000); | |
| ctd_count = 100; | |
| if (timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id)) | |
| fatal_error(NULL, "timer_create()"); | |
| if (timer_settime(id, 0, &val, NULL)) | |
| fatal_error(NULL, "timer_settime()"); | |
| while (ctd_count > 0 && !ctd_failed) | |
| ; | |
| if (timer_delete(id)) | |
| fatal_error(NULL, "timer_delete()"); | |
| return NULL; | |
| } | |
| /* | |
| * Test that only the running thread receives the timer signal. | |
| */ | |
| static void check_timer_distribution(void) | |
| { | |
| if (signal(SIGALRM, ctd_sighandler) == SIG_ERR) | |
| fatal_error(NULL, "signal()"); | |
| if (pthread_create(&ctd_thread, NULL, ctd_thread_func, NULL)) | |
| fatal_error(NULL, "pthread_create()"); | |
| if (pthread_join(ctd_thread, NULL)) | |
| fatal_error(NULL, "pthread_join()"); | |
| 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"); | |
| } | |
| struct tmrsig { | |
| int signals; | |
| int overruns; | |
| }; | |
| static void siginfo_handler(int sig, siginfo_t *si, void *uc) | |
| { | |
| struct tmrsig *tsig = si ? si->si_ptr : NULL; | |
| if (tsig) { | |
| tsig->signals++; | |
| tsig->overruns += si->si_overrun; | |
| } | |
| } | |
| static void *ignore_thread(void *arg) | |
| { | |
| unsigned int *tid = arg; | |
| sigset_t set; | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_BLOCK)"); | |
| *tid = gettid(); | |
| sleep(100); | |
| if (sigprocmask(SIG_UNBLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_UNBLOCK)"); | |
| return NULL; | |
| } | |
| static void check_sig_ign(int thread) | |
| { | |
| struct tmrsig tsig = { }; | |
| struct itimerspec its; | |
| unsigned int tid = 0; | |
| struct sigaction sa; | |
| struct sigevent sev; | |
| pthread_t pthread; | |
| timer_t timerid; | |
| sigset_t set; | |
| if (thread) { | |
| if (pthread_create(&pthread, NULL, ignore_thread, &tid)) | |
| fatal_error(NULL, "pthread_create()"); | |
| sleep(1); | |
| } | |
| sa.sa_flags = SA_SIGINFO; | |
| sa.sa_sigaction = siginfo_handler; | |
| sigemptyset(&sa.sa_mask); | |
| if (sigaction(SIGUSR1, &sa, NULL)) | |
| fatal_error(NULL, "sigaction()"); | |
| /* Block the signal */ | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_BLOCK)"); | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_SIGNAL; | |
| sev.sigev_signo = SIGUSR1; | |
| sev.sigev_value.sival_ptr = &tsig; | |
| if (thread) { | |
| sev.sigev_notify = SIGEV_THREAD_ID; | |
| sev._sigev_un._tid = tid; | |
| } | |
| if (timer_create(CLOCK_MONOTONIC, &sev, &timerid)) | |
| fatal_error(NULL, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| timer_settime(timerid, 0, &its, NULL); | |
| sleep(1); | |
| /* Set the signal to be ignored */ | |
| if (signal(SIGUSR1, SIG_IGN) == SIG_ERR) | |
| fatal_error(NULL, "signal(SIG_IGN)"); | |
| sleep(1); | |
| if (thread) { | |
| /* Stop the thread first. No signal should be delivered to it */ | |
| if (pthread_cancel(pthread)) | |
| fatal_error(NULL, "pthread_cancel()"); | |
| if (pthread_join(pthread, NULL)) | |
| fatal_error(NULL, "pthread_join()"); | |
| } | |
| /* Restore the handler */ | |
| if (sigaction(SIGUSR1, &sa, NULL)) | |
| fatal_error(NULL, "sigaction()"); | |
| sleep(1); | |
| /* Unblock it, which should deliver the signal in the !thread case*/ | |
| if (sigprocmask(SIG_UNBLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_UNBLOCK)"); | |
| if (timer_delete(timerid)) | |
| fatal_error(NULL, "timer_delete()"); | |
| if (!thread) { | |
| ksft_test_result(tsig.signals == 1 && tsig.overruns == 29, | |
| "check_sig_ign SIGEV_SIGNAL\n"); | |
| } else { | |
| ksft_test_result(tsig.signals == 0 && tsig.overruns == 0, | |
| "check_sig_ign SIGEV_THREAD_ID\n"); | |
| } | |
| } | |
| static void check_rearm(void) | |
| { | |
| struct tmrsig tsig = { }; | |
| struct itimerspec its; | |
| struct sigaction sa; | |
| struct sigevent sev; | |
| timer_t timerid; | |
| sigset_t set; | |
| sa.sa_flags = SA_SIGINFO; | |
| sa.sa_sigaction = siginfo_handler; | |
| sigemptyset(&sa.sa_mask); | |
| if (sigaction(SIGUSR1, &sa, NULL)) | |
| fatal_error(NULL, "sigaction()"); | |
| /* Block the signal */ | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_BLOCK)"); | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_SIGNAL; | |
| sev.sigev_signo = SIGUSR1; | |
| sev.sigev_value.sival_ptr = &tsig; | |
| if (timer_create(CLOCK_MONOTONIC, &sev, &timerid)) | |
| fatal_error(NULL, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| if (timer_settime(timerid, 0, &its, NULL)) | |
| fatal_error(NULL, "timer_settime()"); | |
| sleep(1); | |
| /* Reprogram the timer to single shot */ | |
| its.it_value.tv_sec = 10; | |
| its.it_value.tv_nsec = 0; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 0; | |
| if (timer_settime(timerid, 0, &its, NULL)) | |
| fatal_error(NULL, "timer_settime()"); | |
| /* Unblock it, which should not deliver a signal */ | |
| if (sigprocmask(SIG_UNBLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_UNBLOCK)"); | |
| if (timer_delete(timerid)) | |
| fatal_error(NULL, "timer_delete()"); | |
| ksft_test_result(!tsig.signals, "check_rearm\n"); | |
| } | |
| static void check_delete(void) | |
| { | |
| struct tmrsig tsig = { }; | |
| struct itimerspec its; | |
| struct sigaction sa; | |
| struct sigevent sev; | |
| timer_t timerid; | |
| sigset_t set; | |
| sa.sa_flags = SA_SIGINFO; | |
| sa.sa_sigaction = siginfo_handler; | |
| sigemptyset(&sa.sa_mask); | |
| if (sigaction(SIGUSR1, &sa, NULL)) | |
| fatal_error(NULL, "sigaction()"); | |
| /* Block the signal */ | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_BLOCK)"); | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_SIGNAL; | |
| sev.sigev_signo = SIGUSR1; | |
| sev.sigev_value.sival_ptr = &tsig; | |
| if (timer_create(CLOCK_MONOTONIC, &sev, &timerid)) | |
| fatal_error(NULL, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| if (timer_settime(timerid, 0, &its, NULL)) | |
| fatal_error(NULL, "timer_settime()"); | |
| sleep(1); | |
| if (timer_delete(timerid)) | |
| fatal_error(NULL, "timer_delete()"); | |
| /* Unblock it, which should not deliver a signal */ | |
| if (sigprocmask(SIG_UNBLOCK, &set, NULL)) | |
| fatal_error(NULL, "sigprocmask(SIG_UNBLOCK)"); | |
| ksft_test_result(!tsig.signals, "check_delete\n"); | |
| } | |
| static void check_sigev_none(int which) | |
| { | |
| const char *name = clock_name(which); | |
| struct timespec start, now; | |
| struct itimerspec its; | |
| struct sigevent sev; | |
| timer_t timerid; | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_NONE; | |
| if (timer_create(which, &sev, &timerid)) | |
| fatal_error(name, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| timer_settime(timerid, 0, &its, NULL); | |
| if (clock_gettime(which, &start)) | |
| fatal_error(name, "clock_gettime()"); | |
| do { | |
| if (clock_gettime(which, &now)) | |
| fatal_error(name, "clock_gettime()"); | |
| } while (calcdiff_ns(now, start) < NSEC_PER_SEC); | |
| if (timer_gettime(timerid, &its)) | |
| fatal_error(name, "timer_gettime()"); | |
| if (timer_delete(timerid)) | |
| fatal_error(name, "timer_delete()"); | |
| ksft_test_result(its.it_value.tv_sec || its.it_value.tv_nsec, | |
| "check_sigev_none %s\n", name); | |
| } | |
| static void check_gettime(int which) | |
| { | |
| const char *name = clock_name(which); | |
| struct itimerspec its, prev; | |
| struct timespec start, now; | |
| struct sigevent sev; | |
| timer_t timerid; | |
| int wraps = 0; | |
| sigset_t set; | |
| /* Block the signal */ | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(name, "sigprocmask(SIG_BLOCK)"); | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_SIGNAL; | |
| sev.sigev_signo = SIGUSR1; | |
| if (timer_create(which, &sev, &timerid)) | |
| fatal_error(name, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| if (timer_settime(timerid, 0, &its, NULL)) | |
| fatal_error(name, "timer_settime()"); | |
| if (timer_gettime(timerid, &prev)) | |
| fatal_error(name, "timer_gettime()"); | |
| if (clock_gettime(which, &start)) | |
| fatal_error(name, "clock_gettime()"); | |
| do { | |
| if (clock_gettime(which, &now)) | |
| fatal_error(name, "clock_gettime()"); | |
| if (timer_gettime(timerid, &its)) | |
| fatal_error(name, "timer_gettime()"); | |
| if (its.it_value.tv_nsec > prev.it_value.tv_nsec) | |
| wraps++; | |
| prev = its; | |
| } while (calcdiff_ns(now, start) < NSEC_PER_SEC); | |
| if (timer_delete(timerid)) | |
| fatal_error(name, "timer_delete()"); | |
| ksft_test_result(wraps > 1, "check_gettime %s\n", name); | |
| } | |
| static void check_overrun(int which) | |
| { | |
| const char *name = clock_name(which); | |
| struct timespec start, now; | |
| struct tmrsig tsig = { }; | |
| struct itimerspec its; | |
| struct sigaction sa; | |
| struct sigevent sev; | |
| timer_t timerid; | |
| sigset_t set; | |
| sa.sa_flags = SA_SIGINFO; | |
| sa.sa_sigaction = siginfo_handler; | |
| sigemptyset(&sa.sa_mask); | |
| if (sigaction(SIGUSR1, &sa, NULL)) | |
| fatal_error(name, "sigaction()"); | |
| /* Block the signal */ | |
| sigemptyset(&set); | |
| sigaddset(&set, SIGUSR1); | |
| if (sigprocmask(SIG_BLOCK, &set, NULL)) | |
| fatal_error(name, "sigprocmask(SIG_BLOCK)"); | |
| memset(&sev, 0, sizeof(sev)); | |
| sev.sigev_notify = SIGEV_SIGNAL; | |
| sev.sigev_signo = SIGUSR1; | |
| sev.sigev_value.sival_ptr = &tsig; | |
| if (timer_create(which, &sev, &timerid)) | |
| fatal_error(name, "timer_create()"); | |
| /* Start the timer to expire in 100ms and 100ms intervals */ | |
| its.it_value.tv_sec = 0; | |
| its.it_value.tv_nsec = 100000000; | |
| its.it_interval.tv_sec = 0; | |
| its.it_interval.tv_nsec = 100000000; | |
| if (timer_settime(timerid, 0, &its, NULL)) | |
| fatal_error(name, "timer_settime()"); | |
| if (clock_gettime(which, &start)) | |
| fatal_error(name, "clock_gettime()"); | |
| do { | |
| if (clock_gettime(which, &now)) | |
| fatal_error(name, "clock_gettime()"); | |
| } while (calcdiff_ns(now, start) < NSEC_PER_SEC); | |
| /* Unblock it, which should deliver a signal */ | |
| if (sigprocmask(SIG_UNBLOCK, &set, NULL)) | |
| fatal_error(name, "sigprocmask(SIG_UNBLOCK)"); | |
| if (timer_delete(timerid)) | |
| fatal_error(name, "timer_delete()"); | |
| ksft_test_result(tsig.signals == 1 && tsig.overruns == 9, | |
| "check_overrun %s\n", name); | |
| } | |
| #include <sys/syscall.h> | |
| static int do_timer_create(int *id) | |
| { | |
| return syscall(__NR_timer_create, CLOCK_MONOTONIC, NULL, id); | |
| } | |
| static int do_timer_delete(int id) | |
| { | |
| return syscall(__NR_timer_delete, id); | |
| } | |
| #ifndef PR_TIMER_CREATE_RESTORE_IDS | |
| # define PR_TIMER_CREATE_RESTORE_IDS 77 | |
| # define PR_TIMER_CREATE_RESTORE_IDS_OFF 0 | |
| # define PR_TIMER_CREATE_RESTORE_IDS_ON 1 | |
| # define PR_TIMER_CREATE_RESTORE_IDS_GET 2 | |
| #endif | |
| static void check_timer_create_exact(void) | |
| { | |
| int id; | |
| if (prctl(PR_TIMER_CREATE_RESTORE_IDS, PR_TIMER_CREATE_RESTORE_IDS_ON, 0, 0, 0)) { | |
| switch (errno) { | |
| case EINVAL: | |
| ksft_test_result_skip("check timer create exact, not supported\n"); | |
| return; | |
| default: | |
| ksft_test_result_skip("check timer create exact, errno = %d\n", errno); | |
| return; | |
| } | |
| } | |
| if (prctl(PR_TIMER_CREATE_RESTORE_IDS, PR_TIMER_CREATE_RESTORE_IDS_GET, 0, 0, 0) != 1) | |
| fatal_error(NULL, "prctl(GET) failed\n"); | |
| id = 8; | |
| if (do_timer_create(&id) < 0) | |
| fatal_error(NULL, "timer_create()"); | |
| if (do_timer_delete(id)) | |
| fatal_error(NULL, "timer_delete()"); | |
| if (prctl(PR_TIMER_CREATE_RESTORE_IDS, PR_TIMER_CREATE_RESTORE_IDS_OFF, 0, 0, 0)) | |
| fatal_error(NULL, "prctl(OFF)"); | |
| if (prctl(PR_TIMER_CREATE_RESTORE_IDS, PR_TIMER_CREATE_RESTORE_IDS_GET, 0, 0, 0) != 0) | |
| fatal_error(NULL, "prctl(GET) failed\n"); | |
| if (id != 8) { | |
| ksft_test_result_fail("check timer create exact %d != 8\n", id); | |
| return; | |
| } | |
| /* Validate that it went back to normal mode and allocates ID 9 */ | |
| if (do_timer_create(&id) < 0) | |
| fatal_error(NULL, "timer_create()"); | |
| if (do_timer_delete(id)) | |
| fatal_error(NULL, "timer_delete()"); | |
| if (id == 9) | |
| ksft_test_result_pass("check timer create exact\n"); | |
| else | |
| ksft_test_result_fail("check timer create exact. Disabling failed.\n"); | |
| } | |
| int main(int argc, char **argv) | |
| { | |
| bool run_sig_ign_tests = ksft_min_kernel_version(6, 13); | |
| ksft_print_header(); | |
| if (run_sig_ign_tests) { | |
| ksft_set_plan(19); | |
| } else { | |
| ksft_set_plan(10); | |
| } | |
| ksft_print_msg("Testing posix timers. False negative may happen on CPU execution \n"); | |
| ksft_print_msg("based timers if other threads run on the CPU...\n"); | |
| check_timer_create_exact(); | |
| check_itimer(ITIMER_VIRTUAL, "ITIMER_VIRTUAL"); | |
| check_itimer(ITIMER_PROF, "ITIMER_PROF"); | |
| check_itimer(ITIMER_REAL, "ITIMER_REAL"); | |
| check_timer_create(CLOCK_THREAD_CPUTIME_ID); | |
| /* | |
| * It's unfortunately hard to reliably test a timer expiration | |
| * on parallel multithread cputime. We could arm it to expire | |
| * on DELAY * nr_threads, with nr_threads busy looping, then wait | |
| * the normal DELAY since the time is elapsing nr_threads faster. | |
| * But for that we need to ensure we have real physical free CPUs | |
| * to ensure true parallelism. So test only one thread until we | |
| * find a better solution. | |
| */ | |
| check_timer_create(CLOCK_PROCESS_CPUTIME_ID); | |
| check_timer_distribution(); | |
| if (run_sig_ign_tests) { | |
| check_sig_ign(0); | |
| check_sig_ign(1); | |
| check_rearm(); | |
| check_delete(); | |
| check_sigev_none(CLOCK_MONOTONIC); | |
| check_sigev_none(CLOCK_PROCESS_CPUTIME_ID); | |
| check_gettime(CLOCK_MONOTONIC); | |
| check_gettime(CLOCK_PROCESS_CPUTIME_ID); | |
| check_gettime(CLOCK_THREAD_CPUTIME_ID); | |
| } else { | |
| ksft_print_msg("Skipping SIG_IGN tests on kernel < 6.13\n"); | |
| } | |
| check_overrun(CLOCK_MONOTONIC); | |
| check_overrun(CLOCK_PROCESS_CPUTIME_ID); | |
| check_overrun(CLOCK_THREAD_CPUTIME_ID); | |
| ksft_finished(); | |
| } |