18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 Red Hat, Inc., Frederic Weisbecker <fweisbec@redhat.com>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Selftests for a few posix timers interface.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Kernel loop code stolen from Steven Rostedt <srostedt@redhat.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <sys/time.h>
118c2ecf20Sopenharmony_ci#include <stdio.h>
128c2ecf20Sopenharmony_ci#include <signal.h>
138c2ecf20Sopenharmony_ci#include <unistd.h>
148c2ecf20Sopenharmony_ci#include <time.h>
158c2ecf20Sopenharmony_ci#include <pthread.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include "../kselftest.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#define DELAY 2
208c2ecf20Sopenharmony_ci#define USECS_PER_SEC 1000000
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic volatile int done;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/* Busy loop in userspace to elapse ITIMER_VIRTUAL */
258c2ecf20Sopenharmony_cistatic void user_loop(void)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	while (!done);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Try to spend as much time as possible in kernelspace
328c2ecf20Sopenharmony_ci * to elapse ITIMER_PROF.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic void kernel_loop(void)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	void *addr = sbrk(0);
378c2ecf20Sopenharmony_ci	int err = 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	while (!done && !err) {
408c2ecf20Sopenharmony_ci		err = brk(addr + 4096);
418c2ecf20Sopenharmony_ci		err |= brk(addr);
428c2ecf20Sopenharmony_ci	}
438c2ecf20Sopenharmony_ci}
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * Sleep until ITIMER_REAL expiration.
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic void idle_loop(void)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	pause();
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic void sig_handler(int nr)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	done = 1;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/*
598c2ecf20Sopenharmony_ci * Check the expected timer expiration matches the GTOD elapsed delta since
608c2ecf20Sopenharmony_ci * we armed the timer. Keep a 0.5 sec error margin due to various jitter.
618c2ecf20Sopenharmony_ci */
628c2ecf20Sopenharmony_cistatic int check_diff(struct timeval start, struct timeval end)
638c2ecf20Sopenharmony_ci{
648c2ecf20Sopenharmony_ci	long long diff;
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	diff = end.tv_usec - start.tv_usec;
678c2ecf20Sopenharmony_ci	diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) {
708c2ecf20Sopenharmony_ci		printf("Diff too high: %lld..", diff);
718c2ecf20Sopenharmony_ci		return -1;
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic int check_itimer(int which)
788c2ecf20Sopenharmony_ci{
798c2ecf20Sopenharmony_ci	int err;
808c2ecf20Sopenharmony_ci	struct timeval start, end;
818c2ecf20Sopenharmony_ci	struct itimerval val = {
828c2ecf20Sopenharmony_ci		.it_value.tv_sec = DELAY,
838c2ecf20Sopenharmony_ci	};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	printf("Check itimer ");
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if (which == ITIMER_VIRTUAL)
888c2ecf20Sopenharmony_ci		printf("virtual... ");
898c2ecf20Sopenharmony_ci	else if (which == ITIMER_PROF)
908c2ecf20Sopenharmony_ci		printf("prof... ");
918c2ecf20Sopenharmony_ci	else if (which == ITIMER_REAL)
928c2ecf20Sopenharmony_ci		printf("real... ");
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	fflush(stdout);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	done = 0;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (which == ITIMER_VIRTUAL)
998c2ecf20Sopenharmony_ci		signal(SIGVTALRM, sig_handler);
1008c2ecf20Sopenharmony_ci	else if (which == ITIMER_PROF)
1018c2ecf20Sopenharmony_ci		signal(SIGPROF, sig_handler);
1028c2ecf20Sopenharmony_ci	else if (which == ITIMER_REAL)
1038c2ecf20Sopenharmony_ci		signal(SIGALRM, sig_handler);
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	err = gettimeofday(&start, NULL);
1068c2ecf20Sopenharmony_ci	if (err < 0) {
1078c2ecf20Sopenharmony_ci		perror("Can't call gettimeofday()\n");
1088c2ecf20Sopenharmony_ci		return -1;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	err = setitimer(which, &val, NULL);
1128c2ecf20Sopenharmony_ci	if (err < 0) {
1138c2ecf20Sopenharmony_ci		perror("Can't set timer\n");
1148c2ecf20Sopenharmony_ci		return -1;
1158c2ecf20Sopenharmony_ci	}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (which == ITIMER_VIRTUAL)
1188c2ecf20Sopenharmony_ci		user_loop();
1198c2ecf20Sopenharmony_ci	else if (which == ITIMER_PROF)
1208c2ecf20Sopenharmony_ci		kernel_loop();
1218c2ecf20Sopenharmony_ci	else if (which == ITIMER_REAL)
1228c2ecf20Sopenharmony_ci		idle_loop();
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	err = gettimeofday(&end, NULL);
1258c2ecf20Sopenharmony_ci	if (err < 0) {
1268c2ecf20Sopenharmony_ci		perror("Can't call gettimeofday()\n");
1278c2ecf20Sopenharmony_ci		return -1;
1288c2ecf20Sopenharmony_ci	}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	if (!check_diff(start, end))
1318c2ecf20Sopenharmony_ci		printf("[OK]\n");
1328c2ecf20Sopenharmony_ci	else
1338c2ecf20Sopenharmony_ci		printf("[FAIL]\n");
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return 0;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int check_timer_create(int which)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	int err;
1418c2ecf20Sopenharmony_ci	timer_t id;
1428c2ecf20Sopenharmony_ci	struct timeval start, end;
1438c2ecf20Sopenharmony_ci	struct itimerspec val = {
1448c2ecf20Sopenharmony_ci		.it_value.tv_sec = DELAY,
1458c2ecf20Sopenharmony_ci	};
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	printf("Check timer_create() ");
1488c2ecf20Sopenharmony_ci	if (which == CLOCK_THREAD_CPUTIME_ID) {
1498c2ecf20Sopenharmony_ci		printf("per thread... ");
1508c2ecf20Sopenharmony_ci	} else if (which == CLOCK_PROCESS_CPUTIME_ID) {
1518c2ecf20Sopenharmony_ci		printf("per process... ");
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci	fflush(stdout);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	done = 0;
1568c2ecf20Sopenharmony_ci	err = timer_create(which, NULL, &id);
1578c2ecf20Sopenharmony_ci	if (err < 0) {
1588c2ecf20Sopenharmony_ci		perror("Can't create timer\n");
1598c2ecf20Sopenharmony_ci		return -1;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci	signal(SIGALRM, sig_handler);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	err = gettimeofday(&start, NULL);
1648c2ecf20Sopenharmony_ci	if (err < 0) {
1658c2ecf20Sopenharmony_ci		perror("Can't call gettimeofday()\n");
1668c2ecf20Sopenharmony_ci		return -1;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	err = timer_settime(id, 0, &val, NULL);
1708c2ecf20Sopenharmony_ci	if (err < 0) {
1718c2ecf20Sopenharmony_ci		perror("Can't set timer\n");
1728c2ecf20Sopenharmony_ci		return -1;
1738c2ecf20Sopenharmony_ci	}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	user_loop();
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	err = gettimeofday(&end, NULL);
1788c2ecf20Sopenharmony_ci	if (err < 0) {
1798c2ecf20Sopenharmony_ci		perror("Can't call gettimeofday()\n");
1808c2ecf20Sopenharmony_ci		return -1;
1818c2ecf20Sopenharmony_ci	}
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	if (!check_diff(start, end))
1848c2ecf20Sopenharmony_ci		printf("[OK]\n");
1858c2ecf20Sopenharmony_ci	else
1868c2ecf20Sopenharmony_ci		printf("[FAIL]\n");
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	return 0;
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ciint main(int argc, char **argv)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	printf("Testing posix timers. False negative may happen on CPU execution \n");
1948c2ecf20Sopenharmony_ci	printf("based timers if other threads run on the CPU...\n");
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	if (check_itimer(ITIMER_VIRTUAL) < 0)
1978c2ecf20Sopenharmony_ci		return ksft_exit_fail();
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	if (check_itimer(ITIMER_PROF) < 0)
2008c2ecf20Sopenharmony_ci		return ksft_exit_fail();
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (check_itimer(ITIMER_REAL) < 0)
2038c2ecf20Sopenharmony_ci		return ksft_exit_fail();
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0)
2068c2ecf20Sopenharmony_ci		return ksft_exit_fail();
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	/*
2098c2ecf20Sopenharmony_ci	 * It's unfortunately hard to reliably test a timer expiration
2108c2ecf20Sopenharmony_ci	 * on parallel multithread cputime. We could arm it to expire
2118c2ecf20Sopenharmony_ci	 * on DELAY * nr_threads, with nr_threads busy looping, then wait
2128c2ecf20Sopenharmony_ci	 * the normal DELAY since the time is elapsing nr_threads faster.
2138c2ecf20Sopenharmony_ci	 * But for that we need to ensure we have real physical free CPUs
2148c2ecf20Sopenharmony_ci	 * to ensure true parallelism. So test only one thread until we
2158c2ecf20Sopenharmony_ci	 * find a better solution.
2168c2ecf20Sopenharmony_ci	 */
2178c2ecf20Sopenharmony_ci	if (check_timer_create(CLOCK_PROCESS_CPUTIME_ID) < 0)
2188c2ecf20Sopenharmony_ci		return ksft_exit_fail();
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci	return ksft_exit_pass();
2218c2ecf20Sopenharmony_ci}
222