| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- // 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/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 "../kselftest.h"
- #define DELAY 2
- #define USECS_PER_SEC 1000000
- #define NSECS_PER_SEC 1000000000
- 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;
- }
- /*
- * 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 timeval start, struct timeval end)
- {
- long long diff;
- diff = end.tv_usec - start.tv_usec;
- diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC;
- if (llabs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) {
- printf("Diff too high: %lld..", diff);
- return -1;
- }
- return 0;
- }
- static void check_itimer(int which, const char *name)
- {
- struct timeval start, end;
- struct itimerval val = {
- .it_value.tv_sec = DELAY,
- };
- done = 0;
- if (which == ITIMER_VIRTUAL)
- signal(SIGVTALRM, sig_handler);
- else if (which == ITIMER_PROF)
- signal(SIGPROF, sig_handler);
- else if (which == ITIMER_REAL)
- signal(SIGALRM, sig_handler);
- if (gettimeofday(&start, NULL) < 0)
- fatal_error(name, "gettimeofday()");
- 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 (gettimeofday(&end, NULL) < 0)
- fatal_error(name, "gettimeofday()");
- ksft_test_result(check_diff(start, end) == 0, "%s\n", name);
- }
- static void check_timer_create(int which, const char *name)
- {
- struct timeval start, end;
- struct itimerspec val = {
- .it_value.tv_sec = DELAY,
- };
- 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 (gettimeofday(&start, NULL) < 0)
- fatal_error(name, "gettimeofday()");
- if (timer_settime(id, 0, &val, NULL) < 0)
- fatal_error(name, "timer_settime()");
- user_loop();
- if (gettimeofday(&end, NULL) < 0)
- fatal_error(name, "gettimeofday()");
- 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 inline int64_t calcdiff_ns(struct timespec t1, struct timespec t2)
- {
- int64_t diff;
- diff = NSECS_PER_SEC * (int64_t)((int) t1.tv_sec - (int) t2.tv_sec);
- diff += ((int) t1.tv_nsec - (int) t2.tv_nsec);
- return diff;
- }
- static void check_sigev_none(int which, const char *name)
- {
- 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) < NSECS_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)
- {
- 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) < NSECS_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)
- {
- 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) < NSECS_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);
- }
- int main(int argc, char **argv)
- {
- ksft_print_header();
- ksft_set_plan(18);
- 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_itimer(ITIMER_VIRTUAL, "ITIMER_VIRTUAL");
- check_itimer(ITIMER_PROF, "ITIMER_PROF");
- check_itimer(ITIMER_REAL, "ITIMER_REAL");
- check_timer_create(CLOCK_THREAD_CPUTIME_ID, "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, "CLOCK_PROCESS_CPUTIME_ID");
- check_timer_distribution();
- check_sig_ign(0);
- check_sig_ign(1);
- check_rearm();
- check_delete();
- check_sigev_none(CLOCK_MONOTONIC, "CLOCK_MONOTONIC");
- check_sigev_none(CLOCK_PROCESS_CPUTIME_ID, "CLOCK_PROCESS_CPUTIME_ID");
- check_gettime(CLOCK_MONOTONIC, "CLOCK_MONOTONIC");
- check_gettime(CLOCK_PROCESS_CPUTIME_ID, "CLOCK_PROCESS_CPUTIME_ID");
- check_gettime(CLOCK_THREAD_CPUTIME_ID, "CLOCK_THREAD_CPUTIME_ID");
- check_overrun(CLOCK_MONOTONIC, "CLOCK_MONOTONIC");
- check_overrun(CLOCK_PROCESS_CPUTIME_ID, "CLOCK_PROCESS_CPUTIME_ID");
- check_overrun(CLOCK_THREAD_CPUTIME_ID, "CLOCK_THREAD_CPUTIME_ID");
- ksft_finished();
- }
|