linux/tools/testing/selftests/timers/posix_timers.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
   4 *
   5 * Selftests for a few posix timers interface.
   6 *
   7 * Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com>
   8 */
   9
  10#include <sys/time.h>
  11#include <stdio.h>
  12#include <signal.h>
  13#include <unistd.h>
  14#include <time.h>
  15#include <pthread.h>
  16
  17#include "../kselftest.h"
  18
  19#define DELAY 2
  20#define USECS_PER_SEC 1000000
  21
  22static volatile int done;
  23
  24/* Busy loop in userspace to elapse ITIMER_VIRTUAL */
  25static void user_loop(void)
  26{
  27        while (!done);
  28}
  29
  30/*
  31 * Try to spend as much time as possible in kernelspace
  32 * to elapse ITIMER_PROF.
  33 */
  34static void kernel_loop(void)
  35{
  36        void *addr = sbrk(0);
  37        int err = 0;
  38
  39        while (!done && !err) {
  40                err = brk(addr + 4096);
  41                err |= brk(addr);
  42        }
  43}
  44
  45/*
  46 * Sleep until ITIMER_REAL expiration.
  47 */
  48static void idle_loop(void)
  49{
  50        pause();
  51}
  52
  53static void sig_handler(int nr)
  54{
  55        done = 1;
  56}
  57
  58/*
  59 * Check the expected timer expiration matches the GTOD elapsed delta since
  60 * we armed the timer. Keep a 0.5 sec error margin due to various jitter.
  61 */
  62static int check_diff(struct timeval start, struct timeval end)
  63{
  64        long long diff;
  65
  66        diff = end.tv_usec - start.tv_usec;
  67        diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC;
  68
  69        if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) {
  70                printf("Diff too high: %lld..", diff);
  71                return -1;
  72        }
  73
  74        return 0;
  75}
  76
  77static int check_itimer(int which)
  78{
  79        int err;
  80        struct timeval start, end;
  81        struct itimerval val = {
  82                .it_value.tv_sec = DELAY,
  83        };
  84
  85        printf("Check itimer ");
  86
  87        if (which == ITIMER_VIRTUAL)
  88                printf("virtual... ");
  89        else if (which == ITIMER_PROF)
  90                printf("prof... ");
  91        else if (which == ITIMER_REAL)
  92                printf("real... ");
  93
  94        fflush(stdout);
  95
  96        done = 0;
  97
  98        if (which == ITIMER_VIRTUAL)
  99                signal(SIGVTALRM, sig_handler);
 100        else if (which == ITIMER_PROF)
 101                signal(SIGPROF, sig_handler);
 102        else if (which == ITIMER_REAL)
 103                signal(SIGALRM, sig_handler);
 104
 105        err = gettimeofday(&start, NULL);
 106        if (err < 0) {
 107                perror("Can't call gettimeofday()\n");
 108                return -1;
 109        }
 110
 111        err = setitimer(which, &val, NULL);
 112        if (err < 0) {
 113                perror("Can't set timer\n");
 114                return -1;
 115        }
 116
 117        if (which == ITIMER_VIRTUAL)
 118                user_loop();
 119        else if (which == ITIMER_PROF)
 120                kernel_loop();
 121        else if (which == ITIMER_REAL)
 122                idle_loop();
 123
 124        err = gettimeofday(&end, NULL);
 125        if (err < 0) {
 126                perror("Can't call gettimeofday()\n");
 127                return -1;
 128        }
 129
 130        if (!check_diff(start, end))
 131                printf("[OK]\n");
 132        else
 133                printf("[FAIL]\n");
 134
 135        return 0;
 136}
 137
 138static int check_timer_create(int which)
 139{
 140        int err;
 141        timer_t id;
 142        struct timeval start, end;
 143        struct itimerspec val = {
 144                .it_value.tv_sec = DELAY,
 145        };
 146
 147        printf("Check timer_create() ");
 148        if (which == CLOCK_THREAD_CPUTIME_ID) {
 149                printf("per thread... ");
 150        } else if (which == CLOCK_PROCESS_CPUTIME_ID) {
 151                printf("per process... ");
 152        }
 153        fflush(stdout);
 154
 155        done = 0;
 156        err = timer_create(which, NULL, &id);
 157        if (err < 0) {
 158                perror("Can't create timer\n");
 159                return -1;
 160        }
 161        signal(SIGALRM, sig_handler);
 162
 163        err = gettimeofday(&start, NULL);
 164        if (err < 0) {
 165                perror("Can't call gettimeofday()\n");
 166                return -1;
 167        }
 168
 169        err = timer_settime(id, 0, &val, NULL);
 170        if (err < 0) {
 171                perror("Can't set timer\n");
 172                return -1;
 173        }
 174
 175        user_loop();
 176
 177        err = gettimeofday(&end, NULL);
 178        if (err < 0) {
 179                perror("Can't call gettimeofday()\n");
 180                return -1;
 181        }
 182
 183        if (!check_diff(start, end))
 184                printf("[OK]\n");
 185        else
 186                printf("[FAIL]\n");
 187
 188        return 0;
 189}
 190
 191int main(int argc, char **argv)
 192{
 193        printf("Testing posix timers. False negative may happen on CPU execution \n");
 194        printf("based timers if other threads run on the CPU...\n");
 195
 196        if (check_itimer(ITIMER_VIRTUAL) < 0)
 197                return ksft_exit_fail();
 198
 199        if (check_itimer(ITIMER_PROF) < 0)
 200                return ksft_exit_fail();
 201
 202        if (check_itimer(ITIMER_REAL) < 0)
 203                return ksft_exit_fail();
 204
 205        if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0)
 206                return ksft_exit_fail();
 207
 208        /*
 209         * It's unfortunately hard to reliably test a timer expiration
 210         * on parallel multithread cputime. We could arm it to expire
 211         * on DELAY * nr_threads, with nr_threads busy looping, then wait
 212         * the normal DELAY since the time is elapsing nr_threads faster.
 213         * But for that we need to ensure we have real physical free CPUs
 214         * to ensure true parallelism. So test only one thread until we
 215         * find a better solution.
 216         */
 217        if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0)
 218                return ksft_exit_fail();
 219
 220        return ksft_exit_pass();
 221}
 222