162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Selftests for a few posix timers interface. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com> 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <sys/time.h> 1162306a36Sopenharmony_ci#include <stdio.h> 1262306a36Sopenharmony_ci#include <signal.h> 1362306a36Sopenharmony_ci#include <unistd.h> 1462306a36Sopenharmony_ci#include <time.h> 1562306a36Sopenharmony_ci#include <pthread.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include "../kselftest.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define DELAY 2 2062306a36Sopenharmony_ci#define USECS_PER_SEC 1000000 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic volatile int done; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Busy loop in userspace to elapse ITIMER_VIRTUAL */ 2562306a36Sopenharmony_cistatic void user_loop(void) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci while (!done); 2862306a36Sopenharmony_ci} 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* 3162306a36Sopenharmony_ci * Try to spend as much time as possible in kernelspace 3262306a36Sopenharmony_ci * to elapse ITIMER_PROF. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic void kernel_loop(void) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci void *addr = sbrk(0); 3762306a36Sopenharmony_ci int err = 0; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci while (!done && !err) { 4062306a36Sopenharmony_ci err = brk(addr + 4096); 4162306a36Sopenharmony_ci err |= brk(addr); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/* 4662306a36Sopenharmony_ci * Sleep until ITIMER_REAL expiration. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistatic void idle_loop(void) 4962306a36Sopenharmony_ci{ 5062306a36Sopenharmony_ci pause(); 5162306a36Sopenharmony_ci} 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cistatic void sig_handler(int nr) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci done = 1; 5662306a36Sopenharmony_ci} 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci/* 5962306a36Sopenharmony_ci * Check the expected timer expiration matches the GTOD elapsed delta since 6062306a36Sopenharmony_ci * we armed the timer. Keep a 0.5 sec error margin due to various jitter. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_cistatic int check_diff(struct timeval start, struct timeval end) 6362306a36Sopenharmony_ci{ 6462306a36Sopenharmony_ci long long diff; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci diff = end.tv_usec - start.tv_usec; 6762306a36Sopenharmony_ci diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) { 7062306a36Sopenharmony_ci printf("Diff too high: %lld..", diff); 7162306a36Sopenharmony_ci return -1; 7262306a36Sopenharmony_ci } 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int check_itimer(int which) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int err; 8062306a36Sopenharmony_ci struct timeval start, end; 8162306a36Sopenharmony_ci struct itimerval val = { 8262306a36Sopenharmony_ci .it_value.tv_sec = DELAY, 8362306a36Sopenharmony_ci }; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci printf("Check itimer "); 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (which == ITIMER_VIRTUAL) 8862306a36Sopenharmony_ci printf("virtual... "); 8962306a36Sopenharmony_ci else if (which == ITIMER_PROF) 9062306a36Sopenharmony_ci printf("prof... "); 9162306a36Sopenharmony_ci else if (which == ITIMER_REAL) 9262306a36Sopenharmony_ci printf("real... "); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci fflush(stdout); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci done = 0; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci if (which == ITIMER_VIRTUAL) 9962306a36Sopenharmony_ci signal(SIGVTALRM, sig_handler); 10062306a36Sopenharmony_ci else if (which == ITIMER_PROF) 10162306a36Sopenharmony_ci signal(SIGPROF, sig_handler); 10262306a36Sopenharmony_ci else if (which == ITIMER_REAL) 10362306a36Sopenharmony_ci signal(SIGALRM, sig_handler); 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci err = gettimeofday(&start, NULL); 10662306a36Sopenharmony_ci if (err < 0) { 10762306a36Sopenharmony_ci perror("Can't call gettimeofday()\n"); 10862306a36Sopenharmony_ci return -1; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci err = setitimer(which, &val, NULL); 11262306a36Sopenharmony_ci if (err < 0) { 11362306a36Sopenharmony_ci perror("Can't set timer\n"); 11462306a36Sopenharmony_ci return -1; 11562306a36Sopenharmony_ci } 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci if (which == ITIMER_VIRTUAL) 11862306a36Sopenharmony_ci user_loop(); 11962306a36Sopenharmony_ci else if (which == ITIMER_PROF) 12062306a36Sopenharmony_ci kernel_loop(); 12162306a36Sopenharmony_ci else if (which == ITIMER_REAL) 12262306a36Sopenharmony_ci idle_loop(); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci err = gettimeofday(&end, NULL); 12562306a36Sopenharmony_ci if (err < 0) { 12662306a36Sopenharmony_ci perror("Can't call gettimeofday()\n"); 12762306a36Sopenharmony_ci return -1; 12862306a36Sopenharmony_ci } 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (!check_diff(start, end)) 13162306a36Sopenharmony_ci printf("[OK]\n"); 13262306a36Sopenharmony_ci else 13362306a36Sopenharmony_ci printf("[FAIL]\n"); 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci return 0; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int check_timer_create(int which) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci int err; 14162306a36Sopenharmony_ci timer_t id; 14262306a36Sopenharmony_ci struct timeval start, end; 14362306a36Sopenharmony_ci struct itimerspec val = { 14462306a36Sopenharmony_ci .it_value.tv_sec = DELAY, 14562306a36Sopenharmony_ci }; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci printf("Check timer_create() "); 14862306a36Sopenharmony_ci if (which == CLOCK_THREAD_CPUTIME_ID) { 14962306a36Sopenharmony_ci printf("per thread... "); 15062306a36Sopenharmony_ci } else if (which == CLOCK_PROCESS_CPUTIME_ID) { 15162306a36Sopenharmony_ci printf("per process... "); 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci fflush(stdout); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci done = 0; 15662306a36Sopenharmony_ci err = timer_create(which, NULL, &id); 15762306a36Sopenharmony_ci if (err < 0) { 15862306a36Sopenharmony_ci perror("Can't create timer\n"); 15962306a36Sopenharmony_ci return -1; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci signal(SIGALRM, sig_handler); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci err = gettimeofday(&start, NULL); 16462306a36Sopenharmony_ci if (err < 0) { 16562306a36Sopenharmony_ci perror("Can't call gettimeofday()\n"); 16662306a36Sopenharmony_ci return -1; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci err = timer_settime(id, 0, &val, NULL); 17062306a36Sopenharmony_ci if (err < 0) { 17162306a36Sopenharmony_ci perror("Can't set timer\n"); 17262306a36Sopenharmony_ci return -1; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci user_loop(); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci err = gettimeofday(&end, NULL); 17862306a36Sopenharmony_ci if (err < 0) { 17962306a36Sopenharmony_ci perror("Can't call gettimeofday()\n"); 18062306a36Sopenharmony_ci return -1; 18162306a36Sopenharmony_ci } 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (!check_diff(start, end)) 18462306a36Sopenharmony_ci printf("[OK]\n"); 18562306a36Sopenharmony_ci else 18662306a36Sopenharmony_ci printf("[FAIL]\n"); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return 0; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ciint remain; 19262306a36Sopenharmony_ci__thread int got_signal; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic void *distribution_thread(void *arg) 19562306a36Sopenharmony_ci{ 19662306a36Sopenharmony_ci while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); 19762306a36Sopenharmony_ci return NULL; 19862306a36Sopenharmony_ci} 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_cistatic void distribution_handler(int nr) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci if (!__atomic_exchange_n(&got_signal, 1, __ATOMIC_RELAXED)) 20362306a36Sopenharmony_ci __atomic_fetch_sub(&remain, 1, __ATOMIC_RELAXED); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci/* 20762306a36Sopenharmony_ci * Test that all running threads _eventually_ receive CLOCK_PROCESS_CPUTIME_ID 20862306a36Sopenharmony_ci * timer signals. This primarily tests that the kernel does not favour any one. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_cistatic int check_timer_distribution(void) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci int err, i; 21362306a36Sopenharmony_ci timer_t id; 21462306a36Sopenharmony_ci const int nthreads = 10; 21562306a36Sopenharmony_ci pthread_t threads[nthreads]; 21662306a36Sopenharmony_ci struct itimerspec val = { 21762306a36Sopenharmony_ci .it_value.tv_sec = 0, 21862306a36Sopenharmony_ci .it_value.tv_nsec = 1000 * 1000, 21962306a36Sopenharmony_ci .it_interval.tv_sec = 0, 22062306a36Sopenharmony_ci .it_interval.tv_nsec = 1000 * 1000, 22162306a36Sopenharmony_ci }; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci printf("Check timer_create() per process signal distribution... "); 22462306a36Sopenharmony_ci fflush(stdout); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci remain = nthreads + 1; /* worker threads + this thread */ 22762306a36Sopenharmony_ci signal(SIGALRM, distribution_handler); 22862306a36Sopenharmony_ci err = timer_create(CLOCK_PROCESS_CPUTIME_ID, NULL, &id); 22962306a36Sopenharmony_ci if (err < 0) { 23062306a36Sopenharmony_ci perror("Can't create timer\n"); 23162306a36Sopenharmony_ci return -1; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci err = timer_settime(id, 0, &val, NULL); 23462306a36Sopenharmony_ci if (err < 0) { 23562306a36Sopenharmony_ci perror("Can't set timer\n"); 23662306a36Sopenharmony_ci return -1; 23762306a36Sopenharmony_ci } 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci for (i = 0; i < nthreads; i++) { 24062306a36Sopenharmony_ci if (pthread_create(&threads[i], NULL, distribution_thread, NULL)) { 24162306a36Sopenharmony_ci perror("Can't create thread\n"); 24262306a36Sopenharmony_ci return -1; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Wait for all threads to receive the signal. */ 24762306a36Sopenharmony_ci while (__atomic_load_n(&remain, __ATOMIC_RELAXED)); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci for (i = 0; i < nthreads; i++) { 25062306a36Sopenharmony_ci if (pthread_join(threads[i], NULL)) { 25162306a36Sopenharmony_ci perror("Can't join thread\n"); 25262306a36Sopenharmony_ci return -1; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci if (timer_delete(id)) { 25762306a36Sopenharmony_ci perror("Can't delete timer\n"); 25862306a36Sopenharmony_ci return -1; 25962306a36Sopenharmony_ci } 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci printf("[OK]\n"); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci} 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ciint main(int argc, char **argv) 26662306a36Sopenharmony_ci{ 26762306a36Sopenharmony_ci printf("Testing posix timers. False negative may happen on CPU execution \n"); 26862306a36Sopenharmony_ci printf("based timers if other threads run on the CPU...\n"); 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci if (check_itimer(ITIMER_VIRTUAL) < 0) 27162306a36Sopenharmony_ci return ksft_exit_fail(); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci if (check_itimer(ITIMER_PROF) < 0) 27462306a36Sopenharmony_ci return ksft_exit_fail(); 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci if (check_itimer(ITIMER_REAL) < 0) 27762306a36Sopenharmony_ci return ksft_exit_fail(); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0) 28062306a36Sopenharmony_ci return ksft_exit_fail(); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* 28362306a36Sopenharmony_ci * It's unfortunately hard to reliably test a timer expiration 28462306a36Sopenharmony_ci * on parallel multithread cputime. We could arm it to expire 28562306a36Sopenharmony_ci * on DELAY * nr_threads, with nr_threads busy looping, then wait 28662306a36Sopenharmony_ci * the normal DELAY since the time is elapsing nr_threads faster. 28762306a36Sopenharmony_ci * But for that we need to ensure we have real physical free CPUs 28862306a36Sopenharmony_ci * to ensure true parallelism. So test only one thread until we 28962306a36Sopenharmony_ci * find a better solution. 29062306a36Sopenharmony_ci */ 29162306a36Sopenharmony_ci if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0) 29262306a36Sopenharmony_ci return ksft_exit_fail(); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci if (check_timer_distribution() < 0) 29562306a36Sopenharmony_ci return ksft_exit_fail(); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci return ksft_exit_pass(); 29862306a36Sopenharmony_ci} 299