1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2014 Dmitry Chagin <dchagin@FreeBSD.org> 5 * Copyright (c) 2023 Jake Freeland <jfree@FreeBSD.org> 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/systm.h> 31 #include <sys/callout.h> 32 #include <sys/fcntl.h> 33 #include <sys/file.h> 34 #include <sys/filedesc.h> 35 #include <sys/filio.h> 36 #include <sys/kernel.h> 37 #include <sys/lock.h> 38 #include <sys/malloc.h> 39 #include <sys/mount.h> 40 #include <sys/mutex.h> 41 #include <sys/poll.h> 42 #include <sys/proc.h> 43 #include <sys/queue.h> 44 #include <sys/selinfo.h> 45 #include <sys/stat.h> 46 #include <sys/sx.h> 47 #include <sys/sysctl.h> 48 #include <sys/sysent.h> 49 #include <sys/sysproto.h> 50 #include <sys/timerfd.h> 51 #include <sys/timespec.h> 52 #include <sys/uio.h> 53 #include <sys/user.h> 54 55 #include <security/audit/audit.h> 56 57 #ifdef COMPAT_FREEBSD32 58 #include <compat/freebsd32/freebsd32.h> 59 #include <compat/freebsd32/freebsd32_proto.h> 60 #endif 61 62 static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures"); 63 64 static struct mtx timerfd_list_lock; 65 static LIST_HEAD(, timerfd) timerfd_list; 66 MTX_SYSINIT(timerfd, &timerfd_list_lock, "timerfd_list_lock", MTX_DEF); 67 68 static struct unrhdr64 tfdino_unr; 69 70 #define TFD_NOJUMP 0 /* Realtime clock has not jumped. */ 71 #define TFD_READ 1 /* Jumped, tfd has been read since. */ 72 #define TFD_ZREAD 2 /* Jumped backwards, CANCEL_ON_SET=false. */ 73 #define TFD_CANCELED 4 /* Jumped, CANCEL_ON_SET=true. */ 74 #define TFD_JUMPED (TFD_ZREAD | TFD_CANCELED) 75 76 struct timerfd { 77 /* User specified. */ 78 struct itimerspec tfd_time; /* tfd timer */ 79 clockid_t tfd_clockid; /* timing base */ 80 int tfd_flags; /* creation flags */ 81 int tfd_timflags; /* timer flags */ 82 83 /* Used internally. */ 84 timerfd_t tfd_count; /* expiration count since last read */ 85 bool tfd_expired; /* true upon initial expiration */ 86 struct mtx tfd_lock; /* mtx lock */ 87 struct callout tfd_callout; /* expiration notification */ 88 struct selinfo tfd_sel; /* I/O alerts */ 89 struct timespec tfd_boottim; /* cached boottime */ 90 int tfd_jumped; /* timer jump status */ 91 LIST_ENTRY(timerfd) entry; /* entry in list */ 92 93 /* For stat(2). */ 94 ino_t tfd_ino; /* inode number */ 95 struct timespec tfd_atim; /* time of last read */ 96 struct timespec tfd_mtim; /* time of last settime */ 97 struct timespec tfd_birthtim; /* creation time */ 98 }; 99 100 static void 101 timerfd_init(void *data) 102 { 103 new_unrhdr64(&tfdino_unr, 1); 104 } 105 106 SYSINIT(timerfd, SI_SUB_VFS, SI_ORDER_ANY, timerfd_init, NULL); 107 108 static inline void 109 timerfd_getboottime(struct timespec *ts) 110 { 111 struct timeval tv; 112 getboottime(&tv); 113 TIMEVAL_TO_TIMESPEC(&tv, ts); 114 } 115 116 /* 117 * Call when a discontinuous jump has occured in CLOCK_REALTIME and 118 * update timerfd's cached boottime. A jump can be triggered using 119 * functions like clock_settime(2) or settimeofday(2). 120 * 121 * Timer is marked TFD_CANCELED if TFD_TIMER_CANCEL_ON_SET is set 122 * and the realtime clock jumps. 123 * Timer is marked TFD_ZREAD if TFD_TIMER_CANCEL_ON_SET is not set, 124 * but the realtime clock jumps backwards. 125 */ 126 void 127 timerfd_jumped(void) 128 { 129 struct timerfd *tfd; 130 struct timespec boottime, diff; 131 132 if (LIST_EMPTY(&timerfd_list)) 133 return; 134 135 timerfd_getboottime(&boottime); 136 mtx_lock(&timerfd_list_lock); 137 LIST_FOREACH(tfd, &timerfd_list, entry) { 138 mtx_lock(&tfd->tfd_lock); 139 if (tfd->tfd_clockid != CLOCK_REALTIME || 140 (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 || 141 timespeccmp(&boottime, &tfd->tfd_boottim, ==)) { 142 mtx_unlock(&tfd->tfd_lock); 143 continue; 144 } 145 146 if (callout_active(&tfd->tfd_callout)) { 147 if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0) 148 tfd->tfd_jumped = TFD_CANCELED; 149 else if (timespeccmp(&boottime, &tfd->tfd_boottim, <)) 150 tfd->tfd_jumped = TFD_ZREAD; 151 152 /* 153 * Do not reschedule callout when 154 * inside interval time loop. 155 */ 156 if (!tfd->tfd_expired) { 157 timespecsub(&boottime, 158 &tfd->tfd_boottim, &diff); 159 timespecsub(&tfd->tfd_time.it_value, 160 &diff, &tfd->tfd_time.it_value); 161 if (callout_stop(&tfd->tfd_callout) == 1) { 162 callout_schedule_sbt(&tfd->tfd_callout, 163 tstosbt(tfd->tfd_time.it_value), 164 0, C_ABSOLUTE); 165 } 166 } 167 } 168 169 tfd->tfd_boottim = boottime; 170 mtx_unlock(&tfd->tfd_lock); 171 } 172 mtx_unlock(&timerfd_list_lock); 173 } 174 175 static int 176 timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred, 177 int flags, struct thread *td) 178 { 179 struct timerfd *tfd = fp->f_data; 180 timerfd_t count; 181 int error = 0; 182 183 if (uio->uio_resid < sizeof(timerfd_t)) 184 return (EINVAL); 185 186 mtx_lock(&tfd->tfd_lock); 187 retry: 188 getnanotime(&tfd->tfd_atim); 189 if ((tfd->tfd_jumped & TFD_JUMPED) != 0) { 190 if (tfd->tfd_jumped == TFD_CANCELED) 191 error = ECANCELED; 192 tfd->tfd_jumped = TFD_READ; 193 tfd->tfd_count = 0; 194 mtx_unlock(&tfd->tfd_lock); 195 return (error); 196 } else { 197 tfd->tfd_jumped = TFD_NOJUMP; 198 } 199 if (tfd->tfd_count == 0) { 200 if ((fp->f_flag & FNONBLOCK) != 0) { 201 mtx_unlock(&tfd->tfd_lock); 202 return (EAGAIN); 203 } 204 td->td_rtcgen = atomic_load_acq_int(&rtc_generation); 205 error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock, 206 PCATCH, "tfdrd", 0); 207 if (error == 0) { 208 goto retry; 209 } else { 210 mtx_unlock(&tfd->tfd_lock); 211 return (error); 212 } 213 } 214 215 count = tfd->tfd_count; 216 tfd->tfd_count = 0; 217 mtx_unlock(&tfd->tfd_lock); 218 error = uiomove(&count, sizeof(timerfd_t), uio); 219 220 return (error); 221 } 222 223 static int 224 timerfd_ioctl(struct file *fp, u_long cmd, void *data, 225 struct ucred *active_cred, struct thread *td) 226 { 227 switch (cmd) { 228 case FIOASYNC: 229 if (*(int *)data != 0) 230 atomic_set_int(&fp->f_flag, FASYNC); 231 else 232 atomic_clear_int(&fp->f_flag, FASYNC); 233 return (0); 234 case FIONBIO: 235 if (*(int *)data != 0) 236 atomic_set_int(&fp->f_flag, FNONBLOCK); 237 else 238 atomic_clear_int(&fp->f_flag, FNONBLOCK); 239 return (0); 240 } 241 return (ENOTTY); 242 } 243 244 static int 245 timerfd_poll(struct file *fp, int events, struct ucred *active_cred, 246 struct thread *td) 247 { 248 struct timerfd *tfd = fp->f_data; 249 int revents = 0; 250 251 mtx_lock(&tfd->tfd_lock); 252 if ((events & (POLLIN | POLLRDNORM)) != 0 && 253 tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ) 254 revents |= events & (POLLIN | POLLRDNORM); 255 if (revents == 0) 256 selrecord(td, &tfd->tfd_sel); 257 mtx_unlock(&tfd->tfd_lock); 258 259 return (revents); 260 } 261 262 static void 263 filt_timerfddetach(struct knote *kn) 264 { 265 struct timerfd *tfd = kn->kn_hook; 266 267 mtx_lock(&tfd->tfd_lock); 268 knlist_remove(&tfd->tfd_sel.si_note, kn, 1); 269 mtx_unlock(&tfd->tfd_lock); 270 } 271 272 static int 273 filt_timerfdread(struct knote *kn, long hint) 274 { 275 struct timerfd *tfd = kn->kn_hook; 276 277 return (tfd->tfd_count > 0); 278 } 279 280 static struct filterops timerfd_rfiltops = { 281 .f_isfd = 1, 282 .f_detach = filt_timerfddetach, 283 .f_event = filt_timerfdread, 284 }; 285 286 static int 287 timerfd_kqfilter(struct file *fp, struct knote *kn) 288 { 289 struct timerfd *tfd = fp->f_data; 290 291 if (kn->kn_filter != EVFILT_READ) 292 return (EINVAL); 293 294 kn->kn_fop = &timerfd_rfiltops; 295 kn->kn_hook = tfd; 296 knlist_add(&tfd->tfd_sel.si_note, kn, 0); 297 298 return (0); 299 } 300 301 static int 302 timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) 303 { 304 struct timerfd *tfd = fp->f_data; 305 306 bzero(sb, sizeof(*sb)); 307 sb->st_nlink = fp->f_count - 1; 308 sb->st_uid = fp->f_cred->cr_uid; 309 sb->st_gid = fp->f_cred->cr_gid; 310 sb->st_blksize = PAGE_SIZE; 311 312 mtx_lock(&tfd->tfd_lock); 313 sb->st_ino = tfd->tfd_ino; 314 sb->st_atim = tfd->tfd_atim; 315 sb->st_mtim = tfd->tfd_mtim; 316 sb->st_birthtim = tfd->tfd_birthtim; 317 mtx_unlock(&tfd->tfd_lock); 318 319 return (0); 320 } 321 322 static int 323 timerfd_close(struct file *fp, struct thread *td) 324 { 325 struct timerfd *tfd = fp->f_data; 326 327 mtx_lock(&timerfd_list_lock); 328 LIST_REMOVE(tfd, entry); 329 mtx_unlock(&timerfd_list_lock); 330 331 callout_drain(&tfd->tfd_callout); 332 seldrain(&tfd->tfd_sel); 333 knlist_destroy(&tfd->tfd_sel.si_note); 334 mtx_destroy(&tfd->tfd_lock); 335 free(tfd, M_TIMERFD); 336 fp->f_ops = &badfileops; 337 338 return (0); 339 } 340 341 static int 342 timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, 343 struct filedesc *fdp) 344 { 345 346 struct timerfd *tfd = fp->f_data; 347 348 kif->kf_type = KF_TYPE_TIMERFD; 349 mtx_lock(&tfd->tfd_lock); 350 kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid; 351 kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags; 352 kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd; 353 mtx_unlock(&tfd->tfd_lock); 354 355 return (0); 356 } 357 358 static struct fileops timerfdops = { 359 .fo_read = timerfd_read, 360 .fo_write = invfo_rdwr, 361 .fo_truncate = invfo_truncate, 362 .fo_ioctl = timerfd_ioctl, 363 .fo_poll = timerfd_poll, 364 .fo_kqfilter = timerfd_kqfilter, 365 .fo_stat = timerfd_stat, 366 .fo_close = timerfd_close, 367 .fo_chmod = invfo_chmod, 368 .fo_chown = invfo_chown, 369 .fo_sendfile = invfo_sendfile, 370 .fo_fill_kinfo = timerfd_fill_kinfo, 371 .fo_flags = DFLAG_PASSABLE, 372 }; 373 374 static void 375 timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value) 376 { 377 struct timespec curr_value; 378 379 *old_value = tfd->tfd_time; 380 if (timespecisset(&tfd->tfd_time.it_value)) { 381 nanouptime(&curr_value); 382 timespecsub(&tfd->tfd_time.it_value, &curr_value, 383 &old_value->it_value); 384 } 385 } 386 387 static void 388 timerfd_expire(void *arg) 389 { 390 struct timerfd *tfd = (struct timerfd *)arg; 391 struct timespec uptime; 392 393 ++tfd->tfd_count; 394 tfd->tfd_expired = true; 395 if (timespecisset(&tfd->tfd_time.it_interval)) { 396 /* Count missed events. */ 397 nanouptime(&uptime); 398 if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) { 399 timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime); 400 tfd->tfd_count += tstosbt(uptime) / 401 tstosbt(tfd->tfd_time.it_interval); 402 } 403 timespecadd(&tfd->tfd_time.it_value, 404 &tfd->tfd_time.it_interval, &tfd->tfd_time.it_value); 405 callout_schedule_sbt(&tfd->tfd_callout, 406 tstosbt(tfd->tfd_time.it_value), 407 0, C_ABSOLUTE); 408 } else { 409 /* Single shot timer. */ 410 callout_deactivate(&tfd->tfd_callout); 411 timespecclear(&tfd->tfd_time.it_value); 412 } 413 414 wakeup(&tfd->tfd_count); 415 selwakeup(&tfd->tfd_sel); 416 KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0); 417 } 418 419 int 420 kern_timerfd_create(struct thread *td, int clockid, int flags) 421 { 422 struct file *fp; 423 struct timerfd *tfd; 424 int error, fd, fflags; 425 426 AUDIT_ARG_VALUE(clockid); 427 AUDIT_ARG_FFLAGS(flags); 428 429 if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC) 430 return (EINVAL); 431 if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0) 432 return (EINVAL); 433 434 fflags = FREAD; 435 if ((flags & TFD_CLOEXEC) != 0) 436 fflags |= O_CLOEXEC; 437 if ((flags & TFD_NONBLOCK) != 0) 438 fflags |= FNONBLOCK; 439 440 error = falloc(td, &fp, &fd, fflags); 441 if (error != 0) 442 return (error); 443 444 tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO); 445 tfd->tfd_clockid = (clockid_t)clockid; 446 tfd->tfd_flags = flags; 447 tfd->tfd_ino = alloc_unr64(&tfdino_unr); 448 mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF); 449 callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0); 450 knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock); 451 timerfd_getboottime(&tfd->tfd_boottim); 452 getnanotime(&tfd->tfd_birthtim); 453 mtx_lock(&timerfd_list_lock); 454 LIST_INSERT_HEAD(&timerfd_list, tfd, entry); 455 mtx_unlock(&timerfd_list_lock); 456 457 finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops); 458 459 fdrop(fp, td); 460 461 td->td_retval[0] = fd; 462 return (0); 463 } 464 465 int 466 kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value) 467 { 468 struct file *fp; 469 struct timerfd *tfd; 470 int error; 471 472 error = fget(td, fd, &cap_write_rights, &fp); 473 if (error != 0) 474 return (error); 475 tfd = fp->f_data; 476 if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { 477 fdrop(fp, td); 478 return (EINVAL); 479 } 480 481 mtx_lock(&tfd->tfd_lock); 482 timerfd_curval(tfd, curr_value); 483 mtx_unlock(&tfd->tfd_lock); 484 485 fdrop(fp, td); 486 return (0); 487 } 488 489 int 490 kern_timerfd_settime(struct thread *td, int fd, int flags, 491 const struct itimerspec *new_value, struct itimerspec *old_value) 492 { 493 struct file *fp; 494 struct timerfd *tfd; 495 struct timespec ts; 496 int error = 0; 497 498 if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0) 499 return (EINVAL); 500 if (!timespecvalid_interval(&new_value->it_value) || 501 !timespecvalid_interval(&new_value->it_interval)) 502 return (EINVAL); 503 504 error = fget(td, fd, &cap_write_rights, &fp); 505 if (error != 0) 506 return (error); 507 tfd = fp->f_data; 508 if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { 509 fdrop(fp, td); 510 return (EINVAL); 511 } 512 513 mtx_lock(&tfd->tfd_lock); 514 getnanotime(&tfd->tfd_mtim); 515 tfd->tfd_timflags = flags; 516 517 /* Store old itimerspec, if applicable. */ 518 if (old_value != NULL) 519 timerfd_curval(tfd, old_value); 520 521 /* Set new expiration. */ 522 tfd->tfd_time = *new_value; 523 if (timespecisset(&tfd->tfd_time.it_value)) { 524 if ((flags & TFD_TIMER_ABSTIME) == 0) { 525 nanouptime(&ts); 526 timespecadd(&tfd->tfd_time.it_value, &ts, 527 &tfd->tfd_time.it_value); 528 } else if (tfd->tfd_clockid == CLOCK_REALTIME) { 529 /* ECANCELED if unread jump is pending. */ 530 if (tfd->tfd_jumped == TFD_CANCELED) 531 error = ECANCELED; 532 /* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */ 533 timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim, 534 &tfd->tfd_time.it_value); 535 } 536 callout_reset_sbt(&tfd->tfd_callout, 537 tstosbt(tfd->tfd_time.it_value), 538 0, timerfd_expire, tfd, C_ABSOLUTE); 539 } else { 540 callout_stop(&tfd->tfd_callout); 541 } 542 tfd->tfd_count = 0; 543 tfd->tfd_expired = false; 544 tfd->tfd_jumped = TFD_NOJUMP; 545 mtx_unlock(&tfd->tfd_lock); 546 547 fdrop(fp, td); 548 return (error); 549 } 550 551 int 552 sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap) 553 { 554 return (kern_timerfd_create(td, uap->clockid, uap->flags)); 555 } 556 557 int 558 sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap) 559 { 560 struct itimerspec curr_value; 561 int error; 562 563 error = kern_timerfd_gettime(td, uap->fd, &curr_value); 564 if (error == 0) 565 error = copyout(&curr_value, uap->curr_value, 566 sizeof(curr_value)); 567 568 return (error); 569 } 570 571 int 572 sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap) 573 { 574 struct itimerspec new_value, old_value; 575 int error; 576 577 error = copyin(uap->new_value, &new_value, sizeof(new_value)); 578 if (error != 0) 579 return (error); 580 if (uap->old_value == NULL) { 581 error = kern_timerfd_settime(td, uap->fd, uap->flags, 582 &new_value, NULL); 583 } else { 584 error = kern_timerfd_settime(td, uap->fd, uap->flags, 585 &new_value, &old_value); 586 if (error == 0) 587 error = copyout(&old_value, uap->old_value, 588 sizeof(old_value)); 589 } 590 return (error); 591 } 592 593 #ifdef COMPAT_FREEBSD32 594 int 595 freebsd32_timerfd_gettime(struct thread *td, 596 struct freebsd32_timerfd_gettime_args *uap) 597 { 598 struct itimerspec curr_value; 599 struct itimerspec32 curr_value32; 600 int error; 601 602 error = kern_timerfd_gettime(td, uap->fd, &curr_value); 603 if (error == 0) { 604 CP(curr_value, curr_value32, it_value.tv_sec); 605 CP(curr_value, curr_value32, it_value.tv_nsec); 606 CP(curr_value, curr_value32, it_interval.tv_sec); 607 CP(curr_value, curr_value32, it_interval.tv_nsec); 608 error = copyout(&curr_value32, uap->curr_value, 609 sizeof(curr_value32)); 610 } 611 612 return (error); 613 } 614 615 int 616 freebsd32_timerfd_settime(struct thread *td, 617 struct freebsd32_timerfd_settime_args *uap) 618 { 619 struct itimerspec new_value, old_value; 620 struct itimerspec32 new_value32, old_value32; 621 int error; 622 623 error = copyin(uap->new_value, &new_value32, sizeof(new_value32)); 624 if (error != 0) 625 return (error); 626 CP(new_value32, new_value, it_value.tv_sec); 627 CP(new_value32, new_value, it_value.tv_nsec); 628 CP(new_value32, new_value, it_interval.tv_sec); 629 CP(new_value32, new_value, it_interval.tv_nsec); 630 if (uap->old_value == NULL) { 631 error = kern_timerfd_settime(td, uap->fd, uap->flags, 632 &new_value, NULL); 633 } else { 634 error = kern_timerfd_settime(td, uap->fd, uap->flags, 635 &new_value, &old_value); 636 if (error == 0) { 637 CP(old_value, old_value32, it_value.tv_sec); 638 CP(old_value, old_value32, it_value.tv_nsec); 639 CP(old_value, old_value32, it_interval.tv_sec); 640 CP(old_value, old_value32, it_interval.tv_nsec); 641 error = copyout(&old_value32, uap->old_value, 642 sizeof(old_value32)); 643 } 644 } 645 return (error); 646 } 647 #endif 648