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