Signed-off-by: Preetpalbugs <preetpal.si...@s.amity.edu>
---
 posix_timers.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 290 insertions(+)
 create mode 100644 posix_timers.c

diff --git a/posix_timers.c b/posix_timers.c
new file mode 100644
index 0000000..486b71b
--- /dev/null
+++ b/posix_timers.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweis...@redhat.com>
+ *
+ * Selftests for a few posix timers interface.
+ *
+ * Kernel loop code stolen from Steven Rostedt <srost...@redhat.com>
+ */
+
+#include <sys/time.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <time.h>
+#include <pthread.h>
+
+#include "../kselftest.h"
+
+#define DELAY 2
+#define USECS_PER_SEC 1000000
+
+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 int check_itimer(int which)
+{
+       const char *name;
+       int err;
+       struct timeval start, end;
+       struct itimerval val = {
+               .it_value.tv_sec = DELAY,
+       };
+
+       if (which == ITIMER_VIRTUAL)
+               name = "ITIMER_VIRTUAL";
+       else if (which == ITIMER_PROF)
+               name = "ITIMER_PROF";
+       else if (which == ITIMER_REAL)
+               name = "ITIMER_REAL";
+       else
+               return -1;
+
+       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);
+
+       err = gettimeofday(&start, NULL);
+       if (err < 0) {
+               ksft_perror("Can't call gettimeofday()");
+               return -1;
+       }
+
+       err = setitimer(which, &val, NULL);
+       if (err < 0) {
+               ksft_perror("Can't set timer");
+               return -1;
+       }
+
+       if (which == ITIMER_VIRTUAL)
+               user_loop();
+       else if (which == ITIMER_PROF)
+               kernel_loop();
+       else if (which == ITIMER_REAL)
+               idle_loop();
+
+       err = gettimeofday(&end, NULL);
+       if (err < 0) {
+               ksft_perror("Can't call gettimeofday()");
+               return -1;
+       }
+
+       ksft_test_result(check_diff(start, end) == 0, "%s\n", name);
+
+       return 0;
+}
+
+static int check_timer_create(int which)
+{
+       const char *type;
+       int err;
+       timer_t id;
+       struct timeval start, end;
+       struct itimerspec val = {
+               .it_value.tv_sec = DELAY,
+       };
+
+       if (which == CLOCK_THREAD_CPUTIME_ID) {
+               type = "thread";
+       } else if (which == CLOCK_PROCESS_CPUTIME_ID) {
+               type = "process";
+       } else {
+               ksft_print_msg("Unknown timer_create() type %d\n", which);
+               return -1;
+       }
+
+       done = 0;
+       err = timer_create(which, NULL, &id);
+       if (err < 0) {
+               ksft_perror("Can't create timer");
+               return -1;
+       }
+       signal(SIGALRM, sig_handler);
+
+       err = gettimeofday(&start, NULL);
+       if (err < 0) {
+               ksft_perror("Can't call gettimeofday()");
+               return -1;
+       }
+
+       err = timer_settime(id, 0, &val, NULL);
+       if (err < 0) {
+               ksft_perror("Can't set timer");
+               return -1;
+       }
+
+       user_loop();
+
+       err = gettimeofday(&end, NULL);
+       if (err < 0) {
+               ksft_perror("Can't call gettimeofday()");
+               return -1;
+       }
+
+       ksft_test_result(check_diff(start, end) == 0,
+                        "timer_create() per %s\n", type);
+
+       return 0;
+}
+
+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))
+               return "Can't create timer\n";
+       if (timer_settime(id, 0, &val, NULL))
+               return "Can't set timer\n";
+
+       while (ctd_count > 0 && !ctd_failed)
+               ;
+
+       if (timer_delete(id))
+               return "Can't delete timer\n";
+
+       return NULL;
+}
+
+/*
+ * Test that only the running thread receives the timer signal.
+ */
+static int check_timer_distribution(void)
+{
+       const char *errmsg;
+
+       signal(SIGALRM, ctd_sighandler);
+
+       errmsg = "Can't create thread\n";
+       if (pthread_create(&ctd_thread, NULL, ctd_thread_func, NULL))
+               goto err;
+
+       errmsg = "Cannot 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;
+err:
+       ksft_print_msg("%s", errmsg);
+       return -1;
+}
+
+int main(int argc, char **argv)
+{
+       ksft_print_header();
+       ksft_set_plan(6);
+
+       ksft_print_msg("Testing POSIX timers. False negative may happen on CPU 
execution \n");
+       ksft_print_msg("This may happen on CPU-based timers if other threads 
run on the CPU...\n");
+
+       if (check_itimer(ITIMER_VIRTUAL) < 0)
+               ksft_exit_fail();
+
+       if (check_itimer(ITIMER_PROF) < 0)
+               ksft_exit_fail();
+
+       if (check_itimer(ITIMER_REAL) < 0)
+               ksft_exit_fail();
+
+       if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0)
+               ksft_exit_fail();
+
+       /*
+        * 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.
+        */
+       if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0)
+               ksft_exit_fail();
+
+       if (check_timer_distribution() < 0)
+               ksft_exit_fail();
+
+       ksft_finished();
+}
-- 
2.50.1


Reply via email to