kern_time.c revision 1.59
1/* $NetBSD: kern_time.c,v 1.59 2001/11/13 00:34:21 christos Exp $ */ 2 3/*- 4 * Copyright (c) 2000 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Christopher G. Demetriou. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39/* 40 * Copyright (c) 1982, 1986, 1989, 1993 41 * The Regents of the University of California. All rights reserved. 42 * 43 * Redistribution and use in source and binary forms, with or without 44 * modification, are permitted provided that the following conditions 45 * are met: 46 * 1. Redistributions of source code must retain the above copyright 47 * notice, this list of conditions and the following disclaimer. 48 * 2. Redistributions in binary form must reproduce the above copyright 49 * notice, this list of conditions and the following disclaimer in the 50 * documentation and/or other materials provided with the distribution. 51 * 3. All advertising materials mentioning features or use of this software 52 * must display the following acknowledgement: 53 * This product includes software developed by the University of 54 * California, Berkeley and its contributors. 55 * 4. Neither the name of the University nor the names of its contributors 56 * may be used to endorse or promote products derived from this software 57 * without specific prior written permission. 58 * 59 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 60 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 61 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 62 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 63 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 64 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 65 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 66 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 67 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 68 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 69 * SUCH DAMAGE. 70 * 71 * @(#)kern_time.c 8.4 (Berkeley) 5/26/95 72 */ 73 74#include <sys/cdefs.h> 75__KERNEL_RCSID(0, "$NetBSD: kern_time.c,v 1.59 2001/11/13 00:34:21 christos Exp $"); 76 77#include "fs_nfs.h" 78#include "opt_nfs.h" 79#include "opt_nfsserver.h" 80 81#include <sys/param.h> 82#include <sys/resourcevar.h> 83#include <sys/kernel.h> 84#include <sys/systm.h> 85#include <sys/proc.h> 86#include <sys/vnode.h> 87#include <sys/signalvar.h> 88#include <sys/syslog.h> 89 90#include <sys/mount.h> 91#include <sys/syscallargs.h> 92 93#include <uvm/uvm_extern.h> 94 95#if defined(NFS) || defined(NFSSERVER) 96#include <nfs/rpcv2.h> 97#include <nfs/nfsproto.h> 98#include <nfs/nfs_var.h> 99#endif 100 101#include <machine/cpu.h> 102 103/* 104 * Time of day and interval timer support. 105 * 106 * These routines provide the kernel entry points to get and set 107 * the time-of-day and per-process interval timers. Subroutines 108 * here provide support for adding and subtracting timeval structures 109 * and decrementing interval timers, optionally reloading the interval 110 * timers when they expire. 111 */ 112 113/* This function is used by clock_settime and settimeofday */ 114int 115settime(tv) 116 struct timeval *tv; 117{ 118 struct timeval delta; 119 struct cpu_info *ci; 120 int s; 121 122 /* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */ 123 s = splclock(); 124 timersub(tv, &time, &delta); 125 if ((delta.tv_sec < 0 || delta.tv_usec < 0) && securelevel > 1) { 126 splx(s); 127 return (EPERM); 128 } 129#ifdef notyet 130 if ((delta.tv_sec < 86400) && securelevel > 0) { 131 splx(s); 132 return (EPERM); 133 } 134#endif 135 time = *tv; 136 (void) spllowersoftclock(); 137 timeradd(&boottime, &delta, &boottime); 138 /* 139 * XXXSMP 140 * This is wrong. We should traverse a list of all 141 * CPUs and add the delta to the runtime of those 142 * CPUs which have a process on them. 143 */ 144 ci = curcpu(); 145 timeradd(&ci->ci_schedstate.spc_runtime, &delta, 146 &ci->ci_schedstate.spc_runtime); 147# if (defined(NFS) && !defined (NFS_V2_ONLY)) || defined(NFSSERVER) 148 nqnfs_lease_updatetime(delta.tv_sec); 149# endif 150 splx(s); 151 resettodr(); 152 return (0); 153} 154 155/* ARGSUSED */ 156int 157sys_clock_gettime(p, v, retval) 158 struct proc *p; 159 void *v; 160 register_t *retval; 161{ 162 struct sys_clock_gettime_args /* { 163 syscallarg(clockid_t) clock_id; 164 syscallarg(struct timespec *) tp; 165 } */ *uap = v; 166 clockid_t clock_id; 167 struct timeval atv; 168 struct timespec ats; 169 170 clock_id = SCARG(uap, clock_id); 171 if (clock_id != CLOCK_REALTIME) 172 return (EINVAL); 173 174 microtime(&atv); 175 TIMEVAL_TO_TIMESPEC(&atv,&ats); 176 177 return copyout(&ats, SCARG(uap, tp), sizeof(ats)); 178} 179 180/* ARGSUSED */ 181int 182sys_clock_settime(p, v, retval) 183 struct proc *p; 184 void *v; 185 register_t *retval; 186{ 187 struct sys_clock_settime_args /* { 188 syscallarg(clockid_t) clock_id; 189 syscallarg(const struct timespec *) tp; 190 } */ *uap = v; 191 clockid_t clock_id; 192 struct timespec ats; 193 int error; 194 195 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 196 return (error); 197 198 clock_id = SCARG(uap, clock_id); 199 200 if ((error = copyin(SCARG(uap, tp), &ats, sizeof(ats))) != 0) 201 return (error); 202 203 return (clock_settime1(clock_id, &ats)); 204} 205 206 207int 208clock_settime1(clock_id, ats) 209 clockid_t clock_id; 210 struct timespec *ats; 211{ 212 struct timeval atv; 213 int error; 214 215 if (clock_id != CLOCK_REALTIME) 216 return (EINVAL); 217 218 TIMESPEC_TO_TIMEVAL(&atv, ats); 219 if ((error = settime(&atv)) != 0) 220 return (error); 221 222 return 0; 223} 224 225int 226sys_clock_getres(p, v, retval) 227 struct proc *p; 228 void *v; 229 register_t *retval; 230{ 231 struct sys_clock_getres_args /* { 232 syscallarg(clockid_t) clock_id; 233 syscallarg(struct timespec *) tp; 234 } */ *uap = v; 235 clockid_t clock_id; 236 struct timespec ts; 237 int error = 0; 238 239 clock_id = SCARG(uap, clock_id); 240 if (clock_id != CLOCK_REALTIME) 241 return (EINVAL); 242 243 if (SCARG(uap, tp)) { 244 ts.tv_sec = 0; 245 ts.tv_nsec = 1000000000 / hz; 246 247 error = copyout(&ts, SCARG(uap, tp), sizeof(ts)); 248 } 249 250 return error; 251} 252 253/* ARGSUSED */ 254int 255sys_nanosleep(p, v, retval) 256 struct proc *p; 257 void *v; 258 register_t *retval; 259{ 260 static int nanowait; 261 struct sys_nanosleep_args/* { 262 syscallarg(struct timespec *) rqtp; 263 syscallarg(struct timespec *) rmtp; 264 } */ *uap = v; 265 struct timespec rqt; 266 struct timespec rmt; 267 struct timeval atv, utv; 268 int error, s, timo; 269 270 error = copyin((caddr_t)SCARG(uap, rqtp), (caddr_t)&rqt, 271 sizeof(struct timespec)); 272 if (error) 273 return (error); 274 275 TIMESPEC_TO_TIMEVAL(&atv,&rqt) 276 if (itimerfix(&atv) || atv.tv_sec > 1000000000) 277 return (EINVAL); 278 279 s = splclock(); 280 timeradd(&atv,&time,&atv); 281 timo = hzto(&atv); 282 /* 283 * Avoid inadvertantly sleeping forever 284 */ 285 if (timo == 0) 286 timo = 1; 287 splx(s); 288 289 error = tsleep(&nanowait, PWAIT | PCATCH, "nanosleep", timo); 290 if (error == ERESTART) 291 error = EINTR; 292 if (error == EWOULDBLOCK) 293 error = 0; 294 295 if (SCARG(uap, rmtp)) { 296 int error; 297 298 s = splclock(); 299 utv = time; 300 splx(s); 301 302 timersub(&atv, &utv, &utv); 303 if (utv.tv_sec < 0) 304 timerclear(&utv); 305 306 TIMEVAL_TO_TIMESPEC(&utv,&rmt); 307 error = copyout((caddr_t)&rmt, (caddr_t)SCARG(uap,rmtp), 308 sizeof(rmt)); 309 if (error) 310 return (error); 311 } 312 313 return error; 314} 315 316/* ARGSUSED */ 317int 318sys_gettimeofday(p, v, retval) 319 struct proc *p; 320 void *v; 321 register_t *retval; 322{ 323 struct sys_gettimeofday_args /* { 324 syscallarg(struct timeval *) tp; 325 syscallarg(struct timezone *) tzp; 326 } */ *uap = v; 327 struct timeval atv; 328 int error = 0; 329 struct timezone tzfake; 330 331 if (SCARG(uap, tp)) { 332 microtime(&atv); 333 error = copyout(&atv, SCARG(uap, tp), sizeof(atv)); 334 if (error) 335 return (error); 336 } 337 if (SCARG(uap, tzp)) { 338 /* 339 * NetBSD has no kernel notion of time zone, so we just 340 * fake up a timezone struct and return it if demanded. 341 */ 342 tzfake.tz_minuteswest = 0; 343 tzfake.tz_dsttime = 0; 344 error = copyout(&tzfake, SCARG(uap, tzp), sizeof(tzfake)); 345 } 346 return (error); 347} 348 349/* ARGSUSED */ 350int 351sys_settimeofday(p, v, retval) 352 struct proc *p; 353 void *v; 354 register_t *retval; 355{ 356 struct sys_settimeofday_args /* { 357 syscallarg(const struct timeval *) tv; 358 syscallarg(const struct timezone *) tzp; 359 } */ *uap = v; 360 struct timeval atv; 361 struct timezone atz; 362 struct timeval *tv = NULL; 363 struct timezone *tzp = NULL; 364 int error; 365 366 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 367 return (error); 368 369 /* Verify all parameters before changing time. */ 370 if (SCARG(uap, tv)) { 371 if ((error = copyin(SCARG(uap, tv), &atv, sizeof(atv))) != 0) 372 return (error); 373 tv = &atv; 374 } 375 /* XXX since we don't use tz, probably no point in doing copyin. */ 376 if (SCARG(uap, tzp)) { 377 if ((error = copyin(SCARG(uap, tzp), &atz, sizeof(atz))) != 0) 378 return (error); 379 tzp = &atz; 380 } 381 382 return settimeofday1(tv, tzp, p); 383} 384 385int 386settimeofday1(tv, tzp, p) 387 struct timeval *tv; 388 struct timezone *tzp; 389 struct proc *p; 390{ 391 int error; 392 393 if (tv) 394 if ((error = settime(tv)) != 0) 395 return (error); 396 /* 397 * NetBSD has no kernel notion of time zone, and only an 398 * obsolete program would try to set it, so we log a warning. 399 */ 400 if (tzp) 401 log(LOG_WARNING, "pid %d attempted to set the " 402 "(obsolete) kernel time zone\n", p->p_pid); 403 return (0); 404} 405 406int tickdelta; /* current clock skew, us. per tick */ 407long timedelta; /* unapplied time correction, us. */ 408long bigadj = 1000000; /* use 10x skew above bigadj us. */ 409 410/* ARGSUSED */ 411int 412sys_adjtime(p, v, retval) 413 struct proc *p; 414 void *v; 415 register_t *retval; 416{ 417 struct sys_adjtime_args /* { 418 syscallarg(const struct timeval *) delta; 419 syscallarg(struct timeval *) olddelta; 420 } */ *uap = v; 421 struct timeval atv; 422 struct timeval *oatv = NULL; 423 int error; 424 425 if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 426 return (error); 427 428 error = copyin(SCARG(uap, delta), &atv, sizeof(struct timeval)); 429 if (error) 430 return (error); 431 432 if (SCARG(uap, olddelta) != NULL) { 433 if (uvm_useracc((caddr_t)SCARG(uap, olddelta), 434 sizeof(struct timeval), B_WRITE) == FALSE) 435 return (EFAULT); 436 oatv = SCARG(uap, olddelta); 437 } 438 439 return adjtime1(&atv, oatv, p); 440} 441 442int 443adjtime1(delta, olddelta, p) 444 struct timeval *delta; 445 struct timeval *olddelta; 446 struct proc *p; 447{ 448 long ndelta, ntickdelta, odelta; 449 int s; 450 451 /* 452 * Compute the total correction and the rate at which to apply it. 453 * Round the adjustment down to a whole multiple of the per-tick 454 * delta, so that after some number of incremental changes in 455 * hardclock(), tickdelta will become zero, lest the correction 456 * overshoot and start taking us away from the desired final time. 457 */ 458 ndelta = delta->tv_sec * 1000000 + delta->tv_usec; 459 if (ndelta > bigadj || ndelta < -bigadj) 460 ntickdelta = 10 * tickadj; 461 else 462 ntickdelta = tickadj; 463 if (ndelta % ntickdelta) 464 ndelta = ndelta / ntickdelta * ntickdelta; 465 466 /* 467 * To make hardclock()'s job easier, make the per-tick delta negative 468 * if we want time to run slower; then hardclock can simply compute 469 * tick + tickdelta, and subtract tickdelta from timedelta. 470 */ 471 if (ndelta < 0) 472 ntickdelta = -ntickdelta; 473 s = splclock(); 474 odelta = timedelta; 475 timedelta = ndelta; 476 tickdelta = ntickdelta; 477 splx(s); 478 479 if (olddelta) { 480 delta->tv_sec = odelta / 1000000; 481 delta->tv_usec = odelta % 1000000; 482 (void) copyout(delta, olddelta, sizeof(struct timeval)); 483 } 484 return (0); 485} 486 487/* 488 * Get value of an interval timer. The process virtual and 489 * profiling virtual time timers are kept in the p_stats area, since 490 * they can be swapped out. These are kept internally in the 491 * way they are specified externally: in time until they expire. 492 * 493 * The real time interval timer is kept in the process table slot 494 * for the process, and its value (it_value) is kept as an 495 * absolute time rather than as a delta, so that it is easy to keep 496 * periodic real-time signals from drifting. 497 * 498 * Virtual time timers are processed in the hardclock() routine of 499 * kern_clock.c. The real time timer is processed by a timeout 500 * routine, called from the softclock() routine. Since a callout 501 * may be delayed in real time due to interrupt processing in the system, 502 * it is possible for the real time timeout routine (realitexpire, given below), 503 * to be delayed in real time past when it is supposed to occur. It 504 * does not suffice, therefore, to reload the real timer .it_value from the 505 * real time timers .it_interval. Rather, we compute the next time in 506 * absolute time the timer should go off. 507 */ 508/* ARGSUSED */ 509int 510sys_getitimer(p, v, retval) 511 struct proc *p; 512 void *v; 513 register_t *retval; 514{ 515 struct sys_getitimer_args /* { 516 syscallarg(int) which; 517 syscallarg(struct itimerval *) itv; 518 } */ *uap = v; 519 int which = SCARG(uap, which); 520 struct itimerval aitv; 521 int s; 522 523 if ((u_int)which > ITIMER_PROF) 524 return (EINVAL); 525 s = splclock(); 526 if (which == ITIMER_REAL) { 527 /* 528 * Convert from absolute to relative time in .it_value 529 * part of real time timer. If time for real time timer 530 * has passed return 0, else return difference between 531 * current time and time for the timer to go off. 532 */ 533 aitv = p->p_realtimer; 534 if (timerisset(&aitv.it_value)) { 535 if (timercmp(&aitv.it_value, &time, <)) 536 timerclear(&aitv.it_value); 537 else 538 timersub(&aitv.it_value, &time, &aitv.it_value); 539 } 540 } else 541 aitv = p->p_stats->p_timer[which]; 542 splx(s); 543 return (copyout(&aitv, SCARG(uap, itv), sizeof(struct itimerval))); 544} 545 546/* ARGSUSED */ 547int 548sys_setitimer(p, v, retval) 549 struct proc *p; 550 void *v; 551 register_t *retval; 552{ 553 struct sys_setitimer_args /* { 554 syscallarg(int) which; 555 syscallarg(const struct itimerval *) itv; 556 syscallarg(struct itimerval *) oitv; 557 } */ *uap = v; 558 int which = SCARG(uap, which); 559 struct sys_getitimer_args getargs; 560 struct itimerval aitv; 561 const struct itimerval *itvp; 562 int s, error; 563 564 if ((u_int)which > ITIMER_PROF) 565 return (EINVAL); 566 itvp = SCARG(uap, itv); 567 if (itvp && 568 (error = copyin(itvp, &aitv, sizeof(struct itimerval)) != 0)) 569 return (error); 570 if (SCARG(uap, oitv) != NULL) { 571 SCARG(&getargs, which) = which; 572 SCARG(&getargs, itv) = SCARG(uap, oitv); 573 if ((error = sys_getitimer(p, &getargs, retval)) != 0) 574 return (error); 575 } 576 if (itvp == 0) 577 return (0); 578 if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval)) 579 return (EINVAL); 580 s = splclock(); 581 if (which == ITIMER_REAL) { 582 callout_stop(&p->p_realit_ch); 583 if (timerisset(&aitv.it_value)) { 584 /* 585 * Don't need to check hzto() return value, here. 586 * callout_reset() does it for us. 587 */ 588 timeradd(&aitv.it_value, &time, &aitv.it_value); 589 callout_reset(&p->p_realit_ch, hzto(&aitv.it_value), 590 realitexpire, p); 591 } 592 p->p_realtimer = aitv; 593 } else 594 p->p_stats->p_timer[which] = aitv; 595 splx(s); 596 return (0); 597} 598 599/* 600 * Real interval timer expired: 601 * send process whose timer expired an alarm signal. 602 * If time is not set up to reload, then just return. 603 * Else compute next time timer should go off which is > current time. 604 * This is where delay in processing this timeout causes multiple 605 * SIGALRM calls to be compressed into one. 606 */ 607void 608realitexpire(arg) 609 void *arg; 610{ 611 struct proc *p; 612 int s; 613 614 p = (struct proc *)arg; 615 psignal(p, SIGALRM); 616 if (!timerisset(&p->p_realtimer.it_interval)) { 617 timerclear(&p->p_realtimer.it_value); 618 return; 619 } 620 for (;;) { 621 s = splclock(); 622 timeradd(&p->p_realtimer.it_value, 623 &p->p_realtimer.it_interval, &p->p_realtimer.it_value); 624 if (timercmp(&p->p_realtimer.it_value, &time, >)) { 625 /* 626 * Don't need to check hzto() return value, here. 627 * callout_reset() does it for us. 628 */ 629 callout_reset(&p->p_realit_ch, 630 hzto(&p->p_realtimer.it_value), realitexpire, p); 631 splx(s); 632 return; 633 } 634 splx(s); 635 } 636} 637 638/* 639 * Check that a proposed value to load into the .it_value or 640 * .it_interval part of an interval timer is acceptable, and 641 * fix it to have at least minimal value (i.e. if it is less 642 * than the resolution of the clock, round it up.) 643 */ 644int 645itimerfix(tv) 646 struct timeval *tv; 647{ 648 649 if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) 650 return (EINVAL); 651 if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick) 652 tv->tv_usec = tick; 653 return (0); 654} 655 656/* 657 * Decrement an interval timer by a specified number 658 * of microseconds, which must be less than a second, 659 * i.e. < 1000000. If the timer expires, then reload 660 * it. In this case, carry over (usec - old value) to 661 * reduce the value reloaded into the timer so that 662 * the timer does not drift. This routine assumes 663 * that it is called in a context where the timers 664 * on which it is operating cannot change in value. 665 */ 666int 667itimerdecr(itp, usec) 668 struct itimerval *itp; 669 int usec; 670{ 671 672 if (itp->it_value.tv_usec < usec) { 673 if (itp->it_value.tv_sec == 0) { 674 /* expired, and already in next interval */ 675 usec -= itp->it_value.tv_usec; 676 goto expire; 677 } 678 itp->it_value.tv_usec += 1000000; 679 itp->it_value.tv_sec--; 680 } 681 itp->it_value.tv_usec -= usec; 682 usec = 0; 683 if (timerisset(&itp->it_value)) 684 return (1); 685 /* expired, exactly at end of interval */ 686expire: 687 if (timerisset(&itp->it_interval)) { 688 itp->it_value = itp->it_interval; 689 itp->it_value.tv_usec -= usec; 690 if (itp->it_value.tv_usec < 0) { 691 itp->it_value.tv_usec += 1000000; 692 itp->it_value.tv_sec--; 693 } 694 } else 695 itp->it_value.tv_usec = 0; /* sec is already 0 */ 696 return (0); 697} 698 699/* 700 * ratecheck(): simple time-based rate-limit checking. see ratecheck(9) 701 * for usage and rationale. 702 */ 703int 704ratecheck(lasttime, mininterval) 705 struct timeval *lasttime; 706 const struct timeval *mininterval; 707{ 708 struct timeval tv, delta; 709 int s, rv = 0; 710 711 s = splclock(); 712 tv = mono_time; 713 splx(s); 714 715 timersub(&tv, lasttime, &delta); 716 717 /* 718 * check for 0,0 is so that the message will be seen at least once, 719 * even if interval is huge. 720 */ 721 if (timercmp(&delta, mininterval, >=) || 722 (lasttime->tv_sec == 0 && lasttime->tv_usec == 0)) { 723 *lasttime = tv; 724 rv = 1; 725 } 726 727 return (rv); 728} 729 730/* 731 * ppsratecheck(): packets (or events) per second limitation. 732 */ 733int 734ppsratecheck(lasttime, curpps, maxpps) 735 struct timeval *lasttime; 736 int *curpps; 737 int maxpps; /* maximum pps allowed */ 738{ 739 struct timeval tv, delta; 740 int s, rv; 741 742 s = splclock(); 743 tv = mono_time; 744 splx(s); 745 746 timersub(&tv, lasttime, &delta); 747 748 /* 749 * check for 0,0 is so that the message will be seen at least once. 750 * if more than one second have passed since the last update of 751 * lasttime, reset the counter. 752 * 753 * we do increment *curpps even in *curpps < maxpps case, as some may 754 * try to use *curpps for stat purposes as well. 755 */ 756 if ((lasttime->tv_sec == 0 && lasttime->tv_usec == 0) || 757 delta.tv_sec >= 1) { 758 *lasttime = tv; 759 *curpps = 0; 760 rv = 1; 761 } else if (maxpps < 0) 762 rv = 1; 763 else if (*curpps < maxpps) 764 rv = 1; 765 else 766 rv = 0; 767 768#if 1 /*DIAGNOSTIC?*/ 769 /* be careful about wrap-around */ 770 if (*curpps + 1 > *curpps) 771 *curpps = *curpps + 1; 772#else 773 /* 774 * assume that there's not too many calls to this function. 775 * not sure if the assumption holds, as it depends on *caller's* 776 * behavior, not the behavior of this function. 777 * IMHO it is wrong to make assumption on the caller's behavior, 778 * so the above #if is #if 1, not #ifdef DIAGNOSTIC. 779 */ 780 *curpps = *curpps + 1; 781#endif 782 783 return (rv); 784} 785