1 /*
2 * Copyright (c) 2009 Mark Heily <[email protected]>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 *
16 * $FreeBSD$
17 */
18
19 #include "common.h"
20 #include <sys/time.h>
21
22 #define MILLION 1000000
23 #define THOUSAND 1000
24 #define SEC_TO_MS(t) ((t) * THOUSAND) /* Convert seconds to milliseconds. */
25 #define SEC_TO_US(t) ((t) * MILLION) /* Convert seconds to microseconds. */
26 #define MS_TO_US(t) ((t) * THOUSAND) /* Convert milliseconds to microseconds. */
27 #define US_TO_NS(t) ((t) * THOUSAND) /* Convert microseconds to nanoseconds. */
28
29
30 /* Get the current time with microsecond precision. Used for
31 * sub-second timing to make some timer tests run faster.
32 */
33 static uint64_t
now(void)34 now(void)
35 {
36 struct timeval tv;
37
38 gettimeofday(&tv, NULL);
39 /* Promote potentially 32-bit time_t to uint64_t before conversion. */
40 return SEC_TO_US((uint64_t)tv.tv_sec) + tv.tv_usec;
41 }
42
43 /* Sleep for a given number of milliseconds. The timeout is assumed to
44 * be less than 1 second.
45 */
46 static void
mssleep(int t)47 mssleep(int t)
48 {
49 struct timespec stime = {
50 .tv_sec = 0,
51 .tv_nsec = US_TO_NS(MS_TO_US(t)),
52 };
53
54 nanosleep(&stime, NULL);
55 }
56
57 /* Sleep for a given number of microseconds. The timeout is assumed to
58 * be less than 1 second.
59 */
60 static void
ussleep(int t)61 ussleep(int t)
62 {
63 struct timespec stime = {
64 .tv_sec = 0,
65 .tv_nsec = US_TO_NS(t),
66 };
67
68 nanosleep(&stime, NULL);
69 }
70
71 static void
test_kevent_timer_add(void)72 test_kevent_timer_add(void)
73 {
74 const char *test_id = "kevent(EVFILT_TIMER, EV_ADD)";
75 struct kevent kev;
76
77 test_begin(test_id);
78
79 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
80 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
81 err(1, "%s", test_id);
82
83 success();
84 }
85
86 static void
test_kevent_timer_del(void)87 test_kevent_timer_del(void)
88 {
89 const char *test_id = "kevent(EVFILT_TIMER, EV_DELETE)";
90 struct kevent kev;
91
92 test_begin(test_id);
93
94 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
95 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
96 err(1, "%s", test_id);
97
98 test_no_kevents();
99
100 success();
101 }
102
103 static void
test_kevent_timer_get(void)104 test_kevent_timer_get(void)
105 {
106 const char *test_id = "kevent(EVFILT_TIMER, wait)";
107 struct kevent kev;
108
109 test_begin(test_id);
110
111 EV_SET(&kev, 1, EVFILT_TIMER, EV_ADD, 0, 1000, NULL);
112 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
113 err(1, "%s", test_id);
114
115 kev.flags |= EV_CLEAR;
116 kev.data = 1;
117 kevent_cmp(&kev, kevent_get(kqfd));
118
119 EV_SET(&kev, 1, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
120 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
121 err(1, "%s", test_id);
122
123 success();
124 }
125
126 static void
test_oneshot(void)127 test_oneshot(void)
128 {
129 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT)";
130 struct kevent kev;
131
132 test_begin(test_id);
133
134 test_no_kevents();
135
136 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 500,NULL);
137 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
138 err(1, "%s", test_id);
139
140 /* Retrieve the event */
141 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
142 kev.data = 1;
143 kevent_cmp(&kev, kevent_get(kqfd));
144
145 /* Check if the event occurs again */
146 sleep(3);
147 test_no_kevents();
148
149
150 success();
151 }
152
153 static void
test_periodic(void)154 test_periodic(void)
155 {
156 const char *test_id = "kevent(EVFILT_TIMER, periodic)";
157 struct kevent kev;
158
159 test_begin(test_id);
160
161 test_no_kevents();
162
163 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, 1000,NULL);
164 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
165 err(1, "%s", test_id);
166
167 /* Retrieve the event */
168 kev.flags = EV_ADD | EV_CLEAR;
169 kev.data = 1;
170 kevent_cmp(&kev, kevent_get(kqfd));
171
172 /* Check if the event occurs again */
173 sleep(1);
174 kevent_cmp(&kev, kevent_get(kqfd));
175
176 /* Delete the event */
177 kev.flags = EV_DELETE;
178 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
179 err(1, "%s", test_id);
180
181 success();
182 }
183
184 static void
disable_and_enable(void)185 disable_and_enable(void)
186 {
187 const char *test_id = "kevent(EVFILT_TIMER, EV_DISABLE and EV_ENABLE)";
188 struct kevent kev;
189
190 test_begin(test_id);
191
192 test_no_kevents();
193
194 /* Add the watch and immediately disable it */
195 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 2000,NULL);
196 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
197 err(1, "%s", test_id);
198 kev.flags = EV_DISABLE;
199 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
200 err(1, "%s", test_id);
201 test_no_kevents();
202
203 /* Re-enable and check again */
204 kev.flags = EV_ENABLE;
205 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
206 err(1, "%s", test_id);
207
208 kev.flags = EV_ADD | EV_CLEAR | EV_ONESHOT;
209 kev.data = 1;
210 kevent_cmp(&kev, kevent_get(kqfd));
211
212 success();
213 }
214
215 static void
test_abstime(void)216 test_abstime(void)
217 {
218 const char *test_id = "kevent(EVFILT_TIMER, EV_ONESHOT, NOTE_ABSTIME)";
219 struct kevent kev;
220 uint64_t end, start, stop;
221 const int timeout_sec = 3;
222
223 test_begin(test_id);
224
225 test_no_kevents();
226
227 start = now();
228 end = start + SEC_TO_US(timeout_sec);
229 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
230 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
231 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
232 err(1, "%s", test_id);
233
234 /* Retrieve the event */
235 kev.flags = EV_ADD | EV_ONESHOT;
236 kev.data = 1;
237 kev.fflags = 0;
238 kevent_cmp(&kev, kevent_get(kqfd));
239
240 stop = now();
241 if (stop < end)
242 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
243 /* Check if the event occurs again */
244 sleep(3);
245 test_no_kevents();
246
247 success();
248 }
249
250 static void
test_abstime_epoch(void)251 test_abstime_epoch(void)
252 {
253 const char *test_id = "kevent(EVFILT_TIMER (EPOCH), NOTE_ABSTIME)";
254 struct kevent kev;
255
256 test_begin(test_id);
257
258 test_no_kevents();
259
260 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, NOTE_ABSTIME, 0,
261 NULL);
262 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
263 err(1, "%s", test_id);
264
265 /* Retrieve the event */
266 kev.flags = EV_ADD;
267 kev.data = 1;
268 kev.fflags = 0;
269 kevent_cmp(&kev, kevent_get(kqfd));
270
271 /* Delete the event */
272 kev.flags = EV_DELETE;
273 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
274 err(1, "%s", test_id);
275
276 success();
277 }
278
279 static void
test_abstime_preboot(void)280 test_abstime_preboot(void)
281 {
282 const char *test_id = "kevent(EVFILT_TIMER (PREBOOT), EV_ONESHOT, NOTE_ABSTIME)";
283 struct kevent kev;
284 struct timespec btp;
285 uint64_t end, start, stop;
286
287 test_begin(test_id);
288
289 test_no_kevents();
290
291 /*
292 * We'll expire it at just before system boot (roughly) with the hope that
293 * we'll get an ~immediate expiration, just as we do for any value specified
294 * between system boot and now.
295 */
296 start = now();
297 if (clock_gettime(CLOCK_BOOTTIME, &btp) != 0)
298 err(1, "%s", test_id);
299
300 end = start - SEC_TO_US(btp.tv_sec + 1);
301 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
302 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
303 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
304 err(1, "%s", test_id);
305
306 /* Retrieve the event */
307 kev.flags = EV_ADD | EV_ONESHOT;
308 kev.data = 1;
309 kev.fflags = 0;
310 kevent_cmp(&kev, kevent_get(kqfd));
311
312 stop = now();
313 if (stop < end)
314 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
315 /* Check if the event occurs again */
316 sleep(3);
317 test_no_kevents();
318
319 success();
320 }
321
322 static void
test_abstime_postboot(void)323 test_abstime_postboot(void)
324 {
325 const char *test_id = "kevent(EVFILT_TIMER (POSTBOOT), EV_ONESHOT, NOTE_ABSTIME)";
326 struct kevent kev;
327 uint64_t end, start, stop;
328 const int timeout_sec = 1;
329
330 test_begin(test_id);
331
332 test_no_kevents();
333
334 /*
335 * Set a timer for 1 second ago, it should fire immediately rather than
336 * being rejected.
337 */
338 start = now();
339 end = start - SEC_TO_US(timeout_sec);
340 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
341 NOTE_ABSTIME | NOTE_USECONDS, end, NULL);
342 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
343 err(1, "%s", test_id);
344
345 /* Retrieve the event */
346 kev.flags = EV_ADD | EV_ONESHOT;
347 kev.data = 1;
348 kev.fflags = 0;
349 kevent_cmp(&kev, kevent_get(kqfd));
350
351 stop = now();
352 if (stop < end)
353 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end);
354 /* Check if the event occurs again */
355 sleep(3);
356 test_no_kevents();
357
358 success();
359 }
360
361 static void
test_update(void)362 test_update(void)
363 {
364 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)";
365 struct kevent kev;
366 long elapsed;
367 uint64_t start;
368
369 test_begin(test_id);
370
371 test_no_kevents();
372
373 /* First set the timer to 1 second */
374 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
375 NOTE_USECONDS, SEC_TO_US(1), (void *)1);
376 start = now();
377 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
378 err(1, "%s", test_id);
379
380 /* Now reduce the timer to 1 ms */
381 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
382 NOTE_USECONDS, MS_TO_US(1), (void *)2);
383 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
384 err(1, "%s", test_id);
385
386 /* Wait for the event */
387 kev.flags |= EV_CLEAR;
388 kev.fflags &= ~NOTE_USECONDS;
389 kev.data = 1;
390 kevent_cmp(&kev, kevent_get(kqfd));
391 elapsed = now() - start;
392
393 /* Check that the timer expired after at least 1 ms, but less than
394 * 1 second. This check is to make sure that the original 1 second
395 * timeout was not used.
396 */
397 printf("timer expired after %ld us\n", elapsed);
398 if (elapsed < MS_TO_US(1))
399 errx(1, "early timer expiration: %ld us", elapsed);
400 if (elapsed > SEC_TO_US(1))
401 errx(1, "late timer expiration: %ld us", elapsed);
402
403 success();
404 }
405
406 static void
test_update_equal(void)407 test_update_equal(void)
408 {
409 const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)";
410 struct kevent kev;
411 long elapsed;
412 uint64_t start;
413
414 test_begin(test_id);
415
416 test_no_kevents();
417
418 /* First set the timer to 1 ms */
419 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
420 NOTE_USECONDS, MS_TO_US(1), NULL);
421 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
422 err(1, "%s", test_id);
423
424 /* Sleep for a significant fraction of the timeout. */
425 ussleep(600);
426
427 /* Now re-add the timer with the same parameters */
428 start = now();
429 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
430 err(1, "%s", test_id);
431
432 /* Wait for the event */
433 kev.flags |= EV_CLEAR;
434 kev.fflags &= ~NOTE_USECONDS;
435 kev.data = 1;
436 kevent_cmp(&kev, kevent_get(kqfd));
437 elapsed = now() - start;
438
439 /* Check that the timer expired after at least 1 ms. This check is
440 * to make sure that the timer re-started and that the event is
441 * not from the original add of the timer.
442 */
443 printf("timer expired after %ld us\n", elapsed);
444 if (elapsed < MS_TO_US(1))
445 errx(1, "early timer expiration: %ld us", elapsed);
446
447 success();
448 }
449
450 static void
test_update_expired(void)451 test_update_expired(void)
452 {
453 const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)";
454 struct kevent kev;
455 long elapsed;
456 uint64_t start;
457
458 test_begin(test_id);
459
460 test_no_kevents();
461
462 /* Set the timer to 1ms */
463 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
464 NOTE_USECONDS, MS_TO_US(1), NULL);
465 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
466 err(1, "%s", test_id);
467
468 /* Wait for 2 ms to give the timer plenty of time to expire. */
469 mssleep(2);
470
471 /* Now re-add the timer */
472 start = now();
473 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
474 err(1, "%s", test_id);
475
476 /* Wait for the event */
477 kev.flags |= EV_CLEAR;
478 kev.fflags &= ~NOTE_USECONDS;
479 kev.data = 1;
480 kevent_cmp(&kev, kevent_get(kqfd));
481 elapsed = now() - start;
482
483 /* Check that the timer expired after at least 1 ms. This check
484 * is to make sure that the timer re-started and that the event is
485 * not from the original add (and expiration) of the timer.
486 */
487 printf("timer expired after %ld us\n", elapsed);
488 if (elapsed < MS_TO_US(1))
489 errx(1, "early timer expiration: %ld us", elapsed);
490
491 /* Make sure the re-added timer does not fire. In other words,
492 * test that the event received above was the only event from the
493 * add and re-add of the timer.
494 */
495 mssleep(2);
496 test_no_kevents();
497
498 success();
499 }
500
501 static void
test_update_periodic(void)502 test_update_periodic(void)
503 {
504 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)";
505 struct kevent kev;
506 long elapsed;
507 uint64_t start, stop;
508
509 test_begin(test_id);
510
511 test_no_kevents();
512
513 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL);
514 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
515 err(1, "%s", test_id);
516
517 /* Retrieve the event */
518 kev.flags = EV_ADD | EV_CLEAR;
519 kev.data = 1;
520 kevent_cmp(&kev, kevent_get(kqfd));
521
522 /* Check if the event occurs again */
523 sleep(1);
524 kevent_cmp(&kev, kevent_get(kqfd));
525
526 /* Re-add with new timeout. */
527 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL);
528 start = now();
529 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
530 err(1, "%s", test_id);
531
532 /* Retrieve the event */
533 kev.flags = EV_ADD | EV_CLEAR;
534 kev.data = 1;
535 kevent_cmp(&kev, kevent_get(kqfd));
536
537 stop = now();
538 elapsed = stop - start;
539
540 /* Check that the timer expired after at least 2 ms.
541 */
542 printf("timer expired after %ld us\n", elapsed);
543 if (elapsed < MS_TO_US(2))
544 errx(1, "early timer expiration: %ld us", elapsed);
545
546 /* Delete the event */
547 kev.flags = EV_DELETE;
548 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
549 err(1, "%s", test_id);
550
551 success();
552 }
553
554 static void
test_update_timing(void)555 test_update_timing(void)
556 {
557 #define MIN_SLEEP 500
558 #define MAX_SLEEP 1500
559 const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)";
560 struct kevent kev;
561 int iteration;
562 int sleeptime;
563 long elapsed;
564 uint64_t start, stop;
565
566 test_begin(test_id);
567
568 test_no_kevents();
569
570 /* Re-try the update tests with a variety of delays between the
571 * original timer activation and the update of the timer. The goal
572 * is to show that in all cases the only timer event that is
573 * received is from the update and not the original timer add.
574 */
575 for (sleeptime = MIN_SLEEP, iteration = 1;
576 sleeptime < MAX_SLEEP;
577 ++sleeptime, ++iteration) {
578
579 /* First set the timer to 1 ms */
580 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
581 NOTE_USECONDS, MS_TO_US(1), NULL);
582 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
583 err(1, "%s", test_id);
584
585 /* Delay; the delay ranges from less than to greater than the
586 * timer period.
587 */
588 ussleep(sleeptime);
589
590 /* Now re-add the timer with the same parameters */
591 start = now();
592 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
593 err(1, "%s", test_id);
594
595 /* Wait for the event */
596 kev.flags |= EV_CLEAR;
597 kev.fflags &= ~NOTE_USECONDS;
598 kev.data = 1;
599 kevent_cmp(&kev, kevent_get(kqfd));
600 stop = now();
601 elapsed = stop - start;
602
603 /* Check that the timer expired after at least 1 ms. This
604 * check is to make sure that the timer re-started and that
605 * the event is not from the original add of the timer.
606 */
607 if (elapsed < MS_TO_US(1))
608 errx(1, "early timer expiration: %ld us", elapsed);
609
610 /* Make sure the re-added timer does not fire. In other words,
611 * test that the event received above was the only event from
612 * the add and re-add of the timer.
613 */
614 mssleep(2);
615 test_no_kevents_quietly();
616 }
617
618 success();
619 }
620
621 void
test_evfilt_timer(void)622 test_evfilt_timer(void)
623 {
624 kqfd = kqueue();
625 test_kevent_timer_add();
626 test_kevent_timer_del();
627 test_kevent_timer_get();
628 test_oneshot();
629 test_periodic();
630 test_abstime();
631 test_abstime_epoch();
632 test_abstime_preboot();
633 test_abstime_postboot();
634 test_update();
635 test_update_equal();
636 test_update_expired();
637 test_update_timing();
638 test_update_periodic();
639 disable_and_enable();
640 close(kqfd);
641 }
642