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_preboot(void) 252 { 253 const char *test_id = "kevent(EVFILT_TIMER (PREBOOT), EV_ONESHOT, NOTE_ABSTIME)"; 254 struct kevent kev; 255 struct timespec btp; 256 uint64_t end, start, stop; 257 258 test_begin(test_id); 259 260 test_no_kevents(); 261 262 /* 263 * We'll expire it at just before system boot (roughly) with the hope that 264 * we'll get an ~immediate expiration, just as we do for any value specified 265 * between system boot and now. 266 */ 267 start = now(); 268 if (clock_gettime(CLOCK_BOOTTIME, &btp) != 0) 269 err(1, "%s", test_id); 270 271 end = start - SEC_TO_US(btp.tv_sec + 1); 272 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 273 NOTE_ABSTIME | NOTE_USECONDS, end, NULL); 274 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 275 err(1, "%s", test_id); 276 277 /* Retrieve the event */ 278 kev.flags = EV_ADD | EV_ONESHOT; 279 kev.data = 1; 280 kev.fflags = 0; 281 kevent_cmp(&kev, kevent_get(kqfd)); 282 283 stop = now(); 284 if (stop < end) 285 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end); 286 /* Check if the event occurs again */ 287 sleep(3); 288 test_no_kevents(); 289 290 success(); 291 } 292 293 static void 294 test_abstime_postboot(void) 295 { 296 const char *test_id = "kevent(EVFILT_TIMER (POSTBOOT), EV_ONESHOT, NOTE_ABSTIME)"; 297 struct kevent kev; 298 uint64_t end, start, stop; 299 const int timeout_sec = 1; 300 301 test_begin(test_id); 302 303 test_no_kevents(); 304 305 /* 306 * Set a timer for 1 second ago, it should fire immediately rather than 307 * being rejected. 308 */ 309 start = now(); 310 end = start - SEC_TO_US(timeout_sec); 311 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 312 NOTE_ABSTIME | NOTE_USECONDS, end, NULL); 313 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 314 err(1, "%s", test_id); 315 316 /* Retrieve the event */ 317 kev.flags = EV_ADD | EV_ONESHOT; 318 kev.data = 1; 319 kev.fflags = 0; 320 kevent_cmp(&kev, kevent_get(kqfd)); 321 322 stop = now(); 323 if (stop < end) 324 err(1, "too early %jd %jd", (intmax_t)stop, (intmax_t)end); 325 /* Check if the event occurs again */ 326 sleep(3); 327 test_no_kevents(); 328 329 success(); 330 } 331 332 static void 333 test_update(void) 334 { 335 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), EV_ADD | EV_ONESHOT)"; 336 struct kevent kev; 337 long elapsed; 338 uint64_t start; 339 340 test_begin(test_id); 341 342 test_no_kevents(); 343 344 /* First set the timer to 1 second */ 345 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 346 NOTE_USECONDS, SEC_TO_US(1), (void *)1); 347 start = now(); 348 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 349 err(1, "%s", test_id); 350 351 /* Now reduce the timer to 1 ms */ 352 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 353 NOTE_USECONDS, MS_TO_US(1), (void *)2); 354 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 355 err(1, "%s", test_id); 356 357 /* Wait for the event */ 358 kev.flags |= EV_CLEAR; 359 kev.fflags &= ~NOTE_USECONDS; 360 kev.data = 1; 361 kevent_cmp(&kev, kevent_get(kqfd)); 362 elapsed = now() - start; 363 364 /* Check that the timer expired after at least 1 ms, but less than 365 * 1 second. This check is to make sure that the original 1 second 366 * timeout was not used. 367 */ 368 printf("timer expired after %ld us\n", elapsed); 369 if (elapsed < MS_TO_US(1)) 370 errx(1, "early timer expiration: %ld us", elapsed); 371 if (elapsed > SEC_TO_US(1)) 372 errx(1, "late timer expiration: %ld us", elapsed); 373 374 success(); 375 } 376 377 static void 378 test_update_equal(void) 379 { 380 const char *test_id = "kevent(EVFILT_TIMER (UPDATE=), EV_ADD | EV_ONESHOT)"; 381 struct kevent kev; 382 long elapsed; 383 uint64_t start; 384 385 test_begin(test_id); 386 387 test_no_kevents(); 388 389 /* First set the timer to 1 ms */ 390 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 391 NOTE_USECONDS, MS_TO_US(1), NULL); 392 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 393 err(1, "%s", test_id); 394 395 /* Sleep for a significant fraction of the timeout. */ 396 ussleep(600); 397 398 /* Now re-add the timer with the same parameters */ 399 start = now(); 400 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 401 err(1, "%s", test_id); 402 403 /* Wait for the event */ 404 kev.flags |= EV_CLEAR; 405 kev.fflags &= ~NOTE_USECONDS; 406 kev.data = 1; 407 kevent_cmp(&kev, kevent_get(kqfd)); 408 elapsed = now() - start; 409 410 /* Check that the timer expired after at least 1 ms. This check is 411 * to make sure that the timer re-started and that the event is 412 * not from the original add of the timer. 413 */ 414 printf("timer expired after %ld us\n", elapsed); 415 if (elapsed < MS_TO_US(1)) 416 errx(1, "early timer expiration: %ld us", elapsed); 417 418 success(); 419 } 420 421 static void 422 test_update_expired(void) 423 { 424 const char *test_id = "kevent(EVFILT_TIMER (UPDATE EXP), EV_ADD | EV_ONESHOT)"; 425 struct kevent kev; 426 long elapsed; 427 uint64_t start; 428 429 test_begin(test_id); 430 431 test_no_kevents(); 432 433 /* Set the timer to 1ms */ 434 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 435 NOTE_USECONDS, MS_TO_US(1), NULL); 436 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 437 err(1, "%s", test_id); 438 439 /* Wait for 2 ms to give the timer plenty of time to expire. */ 440 mssleep(2); 441 442 /* Now re-add the timer */ 443 start = now(); 444 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 445 err(1, "%s", test_id); 446 447 /* Wait for the event */ 448 kev.flags |= EV_CLEAR; 449 kev.fflags &= ~NOTE_USECONDS; 450 kev.data = 1; 451 kevent_cmp(&kev, kevent_get(kqfd)); 452 elapsed = now() - start; 453 454 /* Check that the timer expired after at least 1 ms. This check 455 * is to make sure that the timer re-started and that the event is 456 * not from the original add (and expiration) of the timer. 457 */ 458 printf("timer expired after %ld us\n", elapsed); 459 if (elapsed < MS_TO_US(1)) 460 errx(1, "early timer expiration: %ld us", elapsed); 461 462 /* Make sure the re-added timer does not fire. In other words, 463 * test that the event received above was the only event from the 464 * add and re-add of the timer. 465 */ 466 mssleep(2); 467 test_no_kevents(); 468 469 success(); 470 } 471 472 static void 473 test_update_periodic(void) 474 { 475 const char *test_id = "kevent(EVFILT_TIMER (UPDATE), periodic)"; 476 struct kevent kev; 477 long elapsed; 478 uint64_t start, stop; 479 480 test_begin(test_id); 481 482 test_no_kevents(); 483 484 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(1), NULL); 485 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 486 err(1, "%s", test_id); 487 488 /* Retrieve the event */ 489 kev.flags = EV_ADD | EV_CLEAR; 490 kev.data = 1; 491 kevent_cmp(&kev, kevent_get(kqfd)); 492 493 /* Check if the event occurs again */ 494 sleep(1); 495 kevent_cmp(&kev, kevent_get(kqfd)); 496 497 /* Re-add with new timeout. */ 498 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD, 0, SEC_TO_MS(2), NULL); 499 start = now(); 500 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 501 err(1, "%s", test_id); 502 503 /* Retrieve the event */ 504 kev.flags = EV_ADD | EV_CLEAR; 505 kev.data = 1; 506 kevent_cmp(&kev, kevent_get(kqfd)); 507 508 stop = now(); 509 elapsed = stop - start; 510 511 /* Check that the timer expired after at least 2 ms. 512 */ 513 printf("timer expired after %ld us\n", elapsed); 514 if (elapsed < MS_TO_US(2)) 515 errx(1, "early timer expiration: %ld us", elapsed); 516 517 /* Delete the event */ 518 kev.flags = EV_DELETE; 519 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 520 err(1, "%s", test_id); 521 522 success(); 523 } 524 525 static void 526 test_update_timing(void) 527 { 528 #define MIN_SLEEP 500 529 #define MAX_SLEEP 1500 530 const char *test_id = "kevent(EVFILT_TIMER (UPDATE TIMING), EV_ADD | EV_ONESHOT)"; 531 struct kevent kev; 532 int iteration; 533 int sleeptime; 534 long elapsed; 535 uint64_t start, stop; 536 537 test_begin(test_id); 538 539 test_no_kevents(); 540 541 /* Re-try the update tests with a variety of delays between the 542 * original timer activation and the update of the timer. The goal 543 * is to show that in all cases the only timer event that is 544 * received is from the update and not the original timer add. 545 */ 546 for (sleeptime = MIN_SLEEP, iteration = 1; 547 sleeptime < MAX_SLEEP; 548 ++sleeptime, ++iteration) { 549 550 /* First set the timer to 1 ms */ 551 EV_SET(&kev, vnode_fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 552 NOTE_USECONDS, MS_TO_US(1), NULL); 553 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 554 err(1, "%s", test_id); 555 556 /* Delay; the delay ranges from less than to greater than the 557 * timer period. 558 */ 559 ussleep(sleeptime); 560 561 /* Now re-add the timer with the same parameters */ 562 start = now(); 563 if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0) 564 err(1, "%s", test_id); 565 566 /* Wait for the event */ 567 kev.flags |= EV_CLEAR; 568 kev.fflags &= ~NOTE_USECONDS; 569 kev.data = 1; 570 kevent_cmp(&kev, kevent_get(kqfd)); 571 stop = now(); 572 elapsed = stop - start; 573 574 /* Check that the timer expired after at least 1 ms. This 575 * check is to make sure that the timer re-started and that 576 * the event is not from the original add of the timer. 577 */ 578 if (elapsed < MS_TO_US(1)) 579 errx(1, "early timer expiration: %ld us", elapsed); 580 581 /* Make sure the re-added timer does not fire. In other words, 582 * test that the event received above was the only event from 583 * the add and re-add of the timer. 584 */ 585 mssleep(2); 586 test_no_kevents_quietly(); 587 } 588 589 success(); 590 } 591 592 void 593 test_evfilt_timer(void) 594 { 595 kqfd = kqueue(); 596 test_kevent_timer_add(); 597 test_kevent_timer_del(); 598 test_kevent_timer_get(); 599 test_oneshot(); 600 test_periodic(); 601 test_abstime(); 602 test_abstime_preboot(); 603 test_abstime_postboot(); 604 test_update(); 605 test_update_equal(); 606 test_update_expired(); 607 test_update_timing(); 608 test_update_periodic(); 609 disable_and_enable(); 610 close(kqfd); 611 } 612