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$ 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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