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 #define DELAY 2
19 #define USECS_PER_SEC 1000000
20 
21 static volatile int done;
22 
23 /* Busy loop in userspace to elapse ITIMER_VIRTUAL */
24 static void user_loop(void)
25 {
26 	while (!done);
27 }
28 
29 /*
30  * Try to spend as much time as possible in kernelspace
31  * to elapse ITIMER_PROF.
32  */
33 static void kernel_loop(void)
34 {
35 	void *addr = sbrk(0);
36 
37 	while (!done) {
38 		brk(addr + 4096);
39 		brk(addr);
40 	}
41 }
42 
43 /*
44  * Sleep until ITIMER_REAL expiration.
45  */
46 static void idle_loop(void)
47 {
48 	pause();
49 }
50 
51 static void sig_handler(int nr)
52 {
53 	done = 1;
54 }
55 
56 /*
57  * Check the expected timer expiration matches the GTOD elapsed delta since
58  * we armed the timer. Keep a 0.5 sec error margin due to various jitter.
59  */
60 static int check_diff(struct timeval start, struct timeval end)
61 {
62 	long long diff;
63 
64 	diff = end.tv_usec - start.tv_usec;
65 	diff += (end.tv_sec - start.tv_sec) * USECS_PER_SEC;
66 
67 	if (abs(diff - DELAY * USECS_PER_SEC) > USECS_PER_SEC / 2) {
68 		printf("Diff too high: %lld..", diff);
69 		return -1;
70 	}
71 
72 	return 0;
73 }
74 
75 static int check_itimer(int which)
76 {
77 	int err;
78 	struct timeval start, end;
79 	struct itimerval val = {
80 		.it_value.tv_sec = DELAY,
81 	};
82 
83 	printf("Check itimer ");
84 
85 	if (which == ITIMER_VIRTUAL)
86 		printf("virtual... ");
87 	else if (which == ITIMER_PROF)
88 		printf("prof... ");
89 	else if (which == ITIMER_REAL)
90 		printf("real... ");
91 
92 	fflush(stdout);
93 
94 	done = 0;
95 
96 	if (which == ITIMER_VIRTUAL)
97 		signal(SIGVTALRM, sig_handler);
98 	else if (which == ITIMER_PROF)
99 		signal(SIGPROF, sig_handler);
100 	else if (which == ITIMER_REAL)
101 		signal(SIGALRM, sig_handler);
102 
103 	err = gettimeofday(&start, NULL);
104 	if (err < 0) {
105 		perror("Can't call gettimeofday()\n");
106 		return -1;
107 	}
108 
109 	err = setitimer(which, &val, NULL);
110 	if (err < 0) {
111 		perror("Can't set timer\n");
112 		return -1;
113 	}
114 
115 	if (which == ITIMER_VIRTUAL)
116 		user_loop();
117 	else if (which == ITIMER_PROF)
118 		kernel_loop();
119 	else if (which == ITIMER_REAL)
120 		idle_loop();
121 
122 	gettimeofday(&end, NULL);
123 	if (err < 0) {
124 		perror("Can't call gettimeofday()\n");
125 		return -1;
126 	}
127 
128 	if (!check_diff(start, end))
129 		printf("[OK]\n");
130 	else
131 		printf("[FAIL]\n");
132 
133 	return 0;
134 }
135 
136 static int check_timer_create(int which)
137 {
138 	int err;
139 	timer_t id;
140 	struct timeval start, end;
141 	struct itimerspec val = {
142 		.it_value.tv_sec = DELAY,
143 	};
144 
145 	printf("Check timer_create() ");
146 	if (which == CLOCK_THREAD_CPUTIME_ID) {
147 		printf("per thread... ");
148 	} else if (which == CLOCK_PROCESS_CPUTIME_ID) {
149 		printf("per process... ");
150 	}
151 	fflush(stdout);
152 
153 	done = 0;
154 	err = timer_create(which, NULL, &id);
155 	if (err < 0) {
156 		perror("Can't create timer\n");
157 		return -1;
158 	}
159 	signal(SIGALRM, sig_handler);
160 
161 	err = gettimeofday(&start, NULL);
162 	if (err < 0) {
163 		perror("Can't call gettimeofday()\n");
164 		return -1;
165 	}
166 
167 	err = timer_settime(id, 0, &val, NULL);
168 	if (err < 0) {
169 		perror("Can't set timer\n");
170 		return -1;
171 	}
172 
173 	user_loop();
174 
175 	gettimeofday(&end, NULL);
176 	if (err < 0) {
177 		perror("Can't call gettimeofday()\n");
178 		return -1;
179 	}
180 
181 	if (!check_diff(start, end))
182 		printf("[OK]\n");
183 	else
184 		printf("[FAIL]\n");
185 
186 	return 0;
187 }
188 
189 int main(int argc, char **argv)
190 {
191 	int err;
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 -1;
198 
199 	if (check_itimer(ITIMER_PROF) < 0)
200 		return -1;
201 
202 	if (check_itimer(ITIMER_REAL) < 0)
203 		return -1;
204 
205 	if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0)
206 		return -1;
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 -1;
219 
220 	return 0;
221 }
222