t_sleep.c revision 1.4 1 /* $NetBSD: t_sleep.c,v 1.4 2012/11/09 04:43:25 pgoyette Exp $ */
2
3 /*-
4 * Copyright (c) 2006 Frank Kardel
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <atf-c.h>
30 #include <errno.h>
31 #include <poll.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <time.h>
36 #include <unistd.h>
37
38 #include <sys/cdefs.h>
39 #include <sys/event.h>
40 #include <sys/signal.h>
41
42 #define BILLION 1000000000LL /* nano-seconds per second */
43 #define MILLION 1000000LL /* nano-seconds per milli-second */
44
45 #define ALARM 12 /* SIGALRM after this many seconds */
46 #define KEVNT_TIMEOUT 16300 /* measured in milli-seconds */
47 #define FUZZ (40 * MILLION) /* scheduling fuzz accepted - 40 ms */
48 #define MAXSLEEP (22 * BILLION) /* 22 seconds */
49
50 /*
51 * Timer notes
52 *
53 * Most tests use FUZZ as their initial delay value, but 'sleep'
54 * starts at 1sec (since it cannot handle sub-second intervals).
55 * Subsequent passes double the previous interval, up to MAXSLEEP.
56 *
57 * The ALARM is only set if the current pass's delay is larger.
58 *
59 * The 'kevent' test expects the ALARM to be set on the same pass
60 * where the delay is larger than the KEVNT_TIMEOUT. The ALARM
61 * needs to fire before the timeout.
62 *
63 * So, ALARM must be less than KEVNT_TIMEOUT, and both must be
64 * large enough to occur on the final pass; ie, delay < MAXSLEEP
65 * but 2*delay >= MAXSLEEP
66 *
67 * The above values should result in 5 passes for the 'sleep' test
68 * and 10 passes for the other tests.
69 */
70
71 static volatile int sig;
72
73 int sleeptest(int (*)(struct timespec *, struct timespec *), bool, bool);
74 int do_nanosleep(struct timespec *, struct timespec *);
75 int do_select(struct timespec *, struct timespec *);
76 int do_poll(struct timespec *, struct timespec *);
77 int do_sleep(struct timespec *, struct timespec *);
78 int do_kevent(struct timespec *, struct timespec *);
79 void sigalrm(int);
80
81 void
82 sigalrm(int s)
83 {
84
85 sig++;
86 }
87
88 int
89 do_nanosleep(struct timespec *delay, struct timespec *remain)
90 {
91 int ret;
92
93 if (nanosleep(delay, remain) == -1)
94 ret = (errno == EINTR ? 0 : errno);
95 else
96 ret = 0;
97 return ret;
98 }
99
100 int
101 do_select(struct timespec *delay, struct timespec *remain)
102 {
103 int ret;
104 struct timeval tv;
105
106 TIMESPEC_TO_TIMEVAL(&tv, delay);
107 if (select(0, NULL, NULL, NULL, &tv) == -1)
108 ret = (errno == EINTR ? 0 : errno);
109 else
110 ret = 0;
111 return ret;
112 }
113
114 int
115 do_poll(struct timespec *delay, struct timespec *remain)
116 {
117 int ret;
118 struct timeval tv;
119
120 TIMESPEC_TO_TIMEVAL(&tv, delay);
121 if (pollts(NULL, 0, delay, NULL) == -1)
122 ret = (errno == EINTR ? 0 : errno);
123 else
124 ret = 0;
125 return ret;
126 }
127
128 int
129 do_sleep(struct timespec *delay, struct timespec *remain)
130 {
131 struct timeval tv;
132
133 TIMESPEC_TO_TIMEVAL(&tv, delay);
134 remain->tv_sec = sleep(delay->tv_sec);
135 remain->tv_nsec = 0;
136
137 return 0;
138 }
139
140 int
141 do_kevent(struct timespec *delay, struct timespec *remain)
142 {
143 struct kevent ktimer;
144 struct kevent kresult;
145 int rtc, kq, kerrno;
146 int tmo;
147
148 ATF_REQUIRE_MSG((kq = kqueue()) != -1, "kqueue: %s", strerror(errno));
149
150 tmo = KEVNT_TIMEOUT;
151 EV_SET(&ktimer, 1, EVFILT_TIMER, EV_ADD, 0, tmo, 0);
152
153 rtc = kevent(kq, &ktimer, 1, &kresult, 1, delay);
154 kerrno = errno;
155
156 (void)close(kq);
157
158 if (rtc == -1) {
159 ATF_REQUIRE_MSG(kerrno == EINTR, "kevent: %s", strerror(errno));
160 return 0;
161 }
162
163 if (delay->tv_sec * BILLION + delay->tv_nsec > tmo * MILLION)
164 ATF_REQUIRE_MSG(rtc > 0,
165 "kevent: ALARM did not cause EVFILT_TIMER event");
166
167 return 0;
168 }
169
170 ATF_TC(nanosleep);
171 ATF_TC_HEAD(nanosleep, tc)
172 {
173
174 atf_tc_set_md_var(tc, "descr", "Test nanosleep(2) timing");
175 atf_tc_set_md_var(tc, "timeout", "65");
176 }
177
178 ATF_TC_BODY(nanosleep, tc)
179 {
180
181 sleeptest(do_nanosleep, true, false);
182 }
183
184 ATF_TC(select);
185 ATF_TC_HEAD(select, tc)
186 {
187
188 atf_tc_set_md_var(tc, "descr", "Test select(2) timing");
189 atf_tc_set_md_var(tc, "timeout", "65");
190 }
191
192 ATF_TC_BODY(select, tc)
193 {
194
195 sleeptest(do_select, true, true);
196 }
197
198 ATF_TC(poll);
199 ATF_TC_HEAD(poll, tc)
200 {
201
202 atf_tc_set_md_var(tc, "descr", "Test poll(2) timing");
203 atf_tc_set_md_var(tc, "timeout", "65");
204 }
205
206 ATF_TC_BODY(poll, tc)
207 {
208
209 sleeptest(do_poll, true, true);
210 }
211
212 ATF_TC(sleep);
213 ATF_TC_HEAD(sleep, tc)
214 {
215
216 atf_tc_set_md_var(tc, "descr", "Test sleep(3) timing");
217 atf_tc_set_md_var(tc, "timeout", "65");
218 }
219
220 ATF_TC_BODY(sleep, tc)
221 {
222
223 sleeptest(do_sleep, false, false);
224 }
225
226 ATF_TC(kevent);
227 ATF_TC_HEAD(kevent, tc)
228 {
229
230 atf_tc_set_md_var(tc, "descr", "Test kevent(2) timing");
231 atf_tc_set_md_var(tc, "timeout", "65");
232 }
233
234 ATF_TC_BODY(kevent, tc)
235 {
236
237 sleeptest(do_kevent, true, true);
238 }
239
240 int
241 sleeptest(int (*test)(struct timespec *, struct timespec *),
242 bool subsec, bool sim_remain)
243 {
244 struct timespec tsa, tsb, tslp, tremain;
245 int64_t delta1, delta2, delta3, round;
246
247 sig = 0;
248 signal(SIGALRM, sigalrm);
249
250 if (subsec) {
251 round = 1;
252 delta3 = FUZZ;
253 } else {
254 round = 1000000000;
255 delta3 = round;
256 }
257
258 tslp.tv_sec = delta3 / 1000000000;
259 tslp.tv_nsec = delta3 % 1000000000;
260
261 while (delta3 <= MAXSLEEP) {
262 /*
263 * disturb sleep by signal on purpose
264 */
265 if (delta3 > ALARM * BILLION && sig == 0)
266 alarm(ALARM);
267
268 clock_gettime(CLOCK_REALTIME, &tsa);
269 (*test)(&tslp, &tremain);
270 clock_gettime(CLOCK_REALTIME, &tsb);
271
272 if (sim_remain) {
273 timespecsub(&tsb, &tsa, &tremain);
274 timespecsub(&tslp, &tremain, &tremain);
275 }
276
277 delta1 = (int64_t)tsb.tv_sec - (int64_t)tsa.tv_sec;
278 delta1 *= BILLION;
279 delta1 += (int64_t)tsb.tv_nsec - (int64_t)tsa.tv_nsec;
280
281 delta2 = (int64_t)tremain.tv_sec * BILLION;
282 delta2 += (int64_t)tremain.tv_nsec;
283
284 delta3 = (int64_t)tslp.tv_sec * BILLION;
285 delta3 += (int64_t)tslp.tv_nsec - delta1 - delta2;
286
287 delta3 /= round;
288 delta3 *= round;
289
290 if (delta3 > FUZZ || delta3 < -FUZZ) {
291 if (!sim_remain &&
292 system("cpuctl identify 0 | grep -q QEMU") == 0)
293 atf_tc_expect_fail("Long reschedule latency "
294 "due to PR kern/43997");
295
296 atf_tc_fail("Reschedule latency %"PRId64" exceeds "
297 "allowable fuzz %lld", delta3, FUZZ);
298 }
299 delta3 = (int64_t)tslp.tv_sec * 2 * BILLION;
300 delta3 += (int64_t)tslp.tv_nsec * 2;
301
302 delta3 /= round;
303 delta3 *= round;
304 if (delta3 < FUZZ)
305 break;
306 tslp.tv_sec = delta3 / BILLION;
307 tslp.tv_nsec = delta3 % BILLION;
308 }
309 ATF_REQUIRE_MSG(sig == 1, "Alarm did not fire!");
310
311 atf_tc_pass();
312 }
313
314 ATF_TP_ADD_TCS(tp)
315 {
316 ATF_TP_ADD_TC(tp, nanosleep);
317 ATF_TP_ADD_TC(tp, select);
318 ATF_TP_ADD_TC(tp, poll);
319 ATF_TP_ADD_TC(tp, sleep);
320 ATF_TP_ADD_TC(tp, kevent);
321
322 return atf_no_error();
323 }
324