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