]>
Commit | Line | Data |
---|---|---|
0bc4b0cf FW |
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 | ||
88471649 SK |
18 | #include "../kselftest.h" |
19 | ||
0bc4b0cf FW |
20 | #define DELAY 2 |
21 | #define USECS_PER_SEC 1000000 | |
22 | ||
23 | static volatile int done; | |
24 | ||
25 | /* Busy loop in userspace to elapse ITIMER_VIRTUAL */ | |
26 | static 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 | */ | |
35 | static void kernel_loop(void) | |
36 | { | |
37 | void *addr = sbrk(0); | |
2430ec65 | 38 | int err = 0; |
0bc4b0cf | 39 | |
2430ec65 JS |
40 | while (!done && !err) { |
41 | err = brk(addr + 4096); | |
42 | err |= brk(addr); | |
0bc4b0cf FW |
43 | } |
44 | } | |
45 | ||
46 | /* | |
47 | * Sleep until ITIMER_REAL expiration. | |
48 | */ | |
49 | static void idle_loop(void) | |
50 | { | |
51 | pause(); | |
52 | } | |
53 | ||
54 | static 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 | */ | |
63 | static 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 | ||
78 | static 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 | ||
139 | static 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; | |
c88b05b2 | 157 | err = timer_create(which, NULL, &id); |
0bc4b0cf FW |
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 | ||
192 | int main(int argc, char **argv) | |
193 | { | |
0bc4b0cf FW |
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) | |
88471649 | 198 | return ksft_exit_fail(); |
0bc4b0cf FW |
199 | |
200 | if (check_itimer(ITIMER_PROF) < 0) | |
88471649 | 201 | return ksft_exit_fail(); |
0bc4b0cf FW |
202 | |
203 | if (check_itimer(ITIMER_REAL) < 0) | |
88471649 | 204 | return ksft_exit_fail(); |
0bc4b0cf FW |
205 | |
206 | if (check_timer_create(CLOCK_THREAD_CPUTIME_ID) < 0) | |
88471649 | 207 | return ksft_exit_fail(); |
0bc4b0cf FW |
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) | |
88471649 | 219 | return ksft_exit_fail(); |
0bc4b0cf | 220 | |
88471649 | 221 | return ksft_exit_pass(); |
0bc4b0cf | 222 | } |