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 sx timerfd_list_lock; 65 static LIST_HEAD(, timerfd) timerfd_list; 66 SX_SYSINIT(timerfd, &timerfd_list_lock, "timerfd_list_lock"); 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 timerfd_getboottime(&boottime); 133 sx_xlock(&timerfd_list_lock); 134 LIST_FOREACH(tfd, &timerfd_list, entry) { 135 mtx_lock(&tfd->tfd_lock); 136 if (tfd->tfd_clockid != CLOCK_REALTIME || 137 (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 || 138 timespeccmp(&boottime, &tfd->tfd_boottim, ==)) { 139 mtx_unlock(&tfd->tfd_lock); 140 continue; 141 } 142 143 if (callout_active(&tfd->tfd_callout)) { 144 if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0) 145 tfd->tfd_jumped = TFD_CANCELED; 146 else if (timespeccmp(&boottime, &tfd->tfd_boottim, <)) 147 tfd->tfd_jumped = TFD_ZREAD; 148 149 /* 150 * Do not reschedule callout when 151 * inside interval time loop. 152 */ 153 if (!tfd->tfd_expired) { 154 timespecsub(&boottime, 155 &tfd->tfd_boottim, &diff); 156 timespecsub(&tfd->tfd_time.it_value, 157 &diff, &tfd->tfd_time.it_value); 158 if (callout_stop(&tfd->tfd_callout) == 1) { 159 callout_schedule_sbt(&tfd->tfd_callout, 160 tstosbt(tfd->tfd_time.it_value), 161 0, C_ABSOLUTE); 162 } 163 } 164 } 165 166 tfd->tfd_boottim = boottime; 167 mtx_unlock(&tfd->tfd_lock); 168 } 169 sx_xunlock(&timerfd_list_lock); 170 } 171 172 static int 173 timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred, 174 int flags, struct thread *td) 175 { 176 struct timerfd *tfd = fp->f_data; 177 timerfd_t count; 178 int error = 0; 179 180 if (uio->uio_resid < sizeof(timerfd_t)) 181 return (EINVAL); 182 183 mtx_lock(&tfd->tfd_lock); 184 retry: 185 getnanotime(&tfd->tfd_atim); 186 if ((tfd->tfd_jumped & TFD_JUMPED) != 0) { 187 if (tfd->tfd_jumped == TFD_CANCELED) 188 error = ECANCELED; 189 tfd->tfd_jumped = TFD_READ; 190 tfd->tfd_count = 0; 191 mtx_unlock(&tfd->tfd_lock); 192 return (error); 193 } else { 194 tfd->tfd_jumped = TFD_NOJUMP; 195 } 196 if (tfd->tfd_count == 0) { 197 if ((fp->f_flag & FNONBLOCK) != 0) { 198 mtx_unlock(&tfd->tfd_lock); 199 return (EAGAIN); 200 } 201 td->td_rtcgen = atomic_load_acq_int(&rtc_generation); 202 error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock, 203 PCATCH, "tfdrd", 0); 204 if (error == 0) { 205 goto retry; 206 } else { 207 mtx_unlock(&tfd->tfd_lock); 208 return (error); 209 } 210 } 211 212 count = tfd->tfd_count; 213 tfd->tfd_count = 0; 214 mtx_unlock(&tfd->tfd_lock); 215 error = uiomove(&count, sizeof(timerfd_t), uio); 216 217 return (error); 218 } 219 220 static int 221 timerfd_ioctl(struct file *fp, u_long cmd, void *data, 222 struct ucred *active_cred, struct thread *td) 223 { 224 switch (cmd) { 225 case FIOASYNC: 226 if (*(int *)data != 0) 227 atomic_set_int(&fp->f_flag, FASYNC); 228 else 229 atomic_clear_int(&fp->f_flag, FASYNC); 230 return (0); 231 case FIONBIO: 232 if (*(int *)data != 0) 233 atomic_set_int(&fp->f_flag, FNONBLOCK); 234 else 235 atomic_clear_int(&fp->f_flag, FNONBLOCK); 236 return (0); 237 } 238 return (ENOTTY); 239 } 240 241 static int 242 timerfd_poll(struct file *fp, int events, struct ucred *active_cred, 243 struct thread *td) 244 { 245 struct timerfd *tfd = fp->f_data; 246 int revents = 0; 247 248 mtx_lock(&tfd->tfd_lock); 249 if ((events & (POLLIN | POLLRDNORM)) != 0 && 250 tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ) 251 revents |= events & (POLLIN | POLLRDNORM); 252 if (revents == 0) 253 selrecord(td, &tfd->tfd_sel); 254 mtx_unlock(&tfd->tfd_lock); 255 256 return (revents); 257 } 258 259 static void 260 filt_timerfddetach(struct knote *kn) 261 { 262 struct timerfd *tfd = kn->kn_hook; 263 264 mtx_lock(&tfd->tfd_lock); 265 knlist_remove(&tfd->tfd_sel.si_note, kn, 1); 266 mtx_unlock(&tfd->tfd_lock); 267 } 268 269 static int 270 filt_timerfdread(struct knote *kn, long hint) 271 { 272 struct timerfd *tfd = kn->kn_hook; 273 274 return (tfd->tfd_count > 0); 275 } 276 277 static struct filterops timerfd_rfiltops = { 278 .f_isfd = 1, 279 .f_detach = filt_timerfddetach, 280 .f_event = filt_timerfdread, 281 }; 282 283 static int 284 timerfd_kqfilter(struct file *fp, struct knote *kn) 285 { 286 struct timerfd *tfd = fp->f_data; 287 288 if (kn->kn_filter != EVFILT_READ) 289 return (EINVAL); 290 291 kn->kn_fop = &timerfd_rfiltops; 292 kn->kn_hook = tfd; 293 knlist_add(&tfd->tfd_sel.si_note, kn, 0); 294 295 return (0); 296 } 297 298 static int 299 timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred) 300 { 301 struct timerfd *tfd = fp->f_data; 302 303 bzero(sb, sizeof(*sb)); 304 sb->st_nlink = fp->f_count - 1; 305 sb->st_uid = fp->f_cred->cr_uid; 306 sb->st_gid = fp->f_cred->cr_gid; 307 sb->st_blksize = PAGE_SIZE; 308 309 mtx_lock(&tfd->tfd_lock); 310 sb->st_ino = tfd->tfd_ino; 311 sb->st_atim = tfd->tfd_atim; 312 sb->st_mtim = tfd->tfd_mtim; 313 sb->st_birthtim = tfd->tfd_birthtim; 314 mtx_unlock(&tfd->tfd_lock); 315 316 return (0); 317 } 318 319 static int 320 timerfd_close(struct file *fp, struct thread *td) 321 { 322 struct timerfd *tfd = fp->f_data; 323 324 sx_xlock(&timerfd_list_lock); 325 LIST_REMOVE(tfd, entry); 326 sx_xunlock(&timerfd_list_lock); 327 328 callout_drain(&tfd->tfd_callout); 329 seldrain(&tfd->tfd_sel); 330 knlist_destroy(&tfd->tfd_sel.si_note); 331 mtx_destroy(&tfd->tfd_lock); 332 free(tfd, M_TIMERFD); 333 fp->f_ops = &badfileops; 334 335 return (0); 336 } 337 338 static int 339 timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, 340 struct filedesc *fdp) 341 { 342 343 struct timerfd *tfd = fp->f_data; 344 345 kif->kf_type = KF_TYPE_TIMERFD; 346 mtx_lock(&tfd->tfd_lock); 347 kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid; 348 kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags; 349 kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd; 350 mtx_unlock(&tfd->tfd_lock); 351 352 return (0); 353 } 354 355 static struct fileops timerfdops = { 356 .fo_read = timerfd_read, 357 .fo_write = invfo_rdwr, 358 .fo_truncate = invfo_truncate, 359 .fo_ioctl = timerfd_ioctl, 360 .fo_poll = timerfd_poll, 361 .fo_kqfilter = timerfd_kqfilter, 362 .fo_stat = timerfd_stat, 363 .fo_close = timerfd_close, 364 .fo_chmod = invfo_chmod, 365 .fo_chown = invfo_chown, 366 .fo_sendfile = invfo_sendfile, 367 .fo_fill_kinfo = timerfd_fill_kinfo, 368 .fo_flags = DFLAG_PASSABLE, 369 }; 370 371 static void 372 timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value) 373 { 374 struct timespec curr_value; 375 376 *old_value = tfd->tfd_time; 377 if (timespecisset(&tfd->tfd_time.it_value)) { 378 nanouptime(&curr_value); 379 timespecsub(&tfd->tfd_time.it_value, &curr_value, 380 &old_value->it_value); 381 } 382 } 383 384 static void 385 timerfd_expire(void *arg) 386 { 387 struct timerfd *tfd = (struct timerfd *)arg; 388 struct timespec uptime; 389 390 ++tfd->tfd_count; 391 tfd->tfd_expired = true; 392 if (timespecisset(&tfd->tfd_time.it_interval)) { 393 /* Count missed events. */ 394 nanouptime(&uptime); 395 if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) { 396 timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime); 397 tfd->tfd_count += tstosbt(uptime) / 398 tstosbt(tfd->tfd_time.it_interval); 399 } 400 timespecadd(&tfd->tfd_time.it_value, 401 &tfd->tfd_time.it_interval, &tfd->tfd_time.it_value); 402 callout_schedule_sbt(&tfd->tfd_callout, 403 tstosbt(tfd->tfd_time.it_value), 404 0, C_ABSOLUTE); 405 } else { 406 /* Single shot timer. */ 407 callout_deactivate(&tfd->tfd_callout); 408 timespecclear(&tfd->tfd_time.it_value); 409 } 410 411 wakeup(&tfd->tfd_count); 412 selwakeup(&tfd->tfd_sel); 413 KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0); 414 } 415 416 int 417 kern_timerfd_create(struct thread *td, int clockid, int flags) 418 { 419 struct file *fp; 420 struct timerfd *tfd; 421 int error, fd, fflags = 0; 422 423 AUDIT_ARG_VALUE(clockid); 424 AUDIT_ARG_FFLAGS(flags); 425 426 if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC) 427 return (EINVAL); 428 if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0) 429 return (EINVAL); 430 if ((flags & TFD_CLOEXEC) != 0) 431 fflags |= O_CLOEXEC; 432 433 error = falloc(td, &fp, &fd, fflags); 434 if (error != 0) 435 return (error); 436 437 tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO); 438 tfd->tfd_clockid = (clockid_t)clockid; 439 tfd->tfd_flags = flags; 440 tfd->tfd_ino = alloc_unr64(&tfdino_unr); 441 mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF); 442 callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0); 443 knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock); 444 timerfd_getboottime(&tfd->tfd_boottim); 445 getnanotime(&tfd->tfd_birthtim); 446 sx_xlock(&timerfd_list_lock); 447 LIST_INSERT_HEAD(&timerfd_list, tfd, entry); 448 sx_xunlock(&timerfd_list_lock); 449 450 fflags = FREAD; 451 if ((flags & TFD_NONBLOCK) != 0) 452 fflags |= FNONBLOCK; 453 finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops); 454 455 fdrop(fp, td); 456 457 td->td_retval[0] = fd; 458 return (0); 459 } 460 461 int 462 kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value) 463 { 464 struct file *fp; 465 struct timerfd *tfd; 466 int error; 467 468 error = fget(td, fd, &cap_write_rights, &fp); 469 if (error != 0) 470 return (error); 471 tfd = fp->f_data; 472 if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { 473 fdrop(fp, td); 474 return (EINVAL); 475 } 476 477 mtx_lock(&tfd->tfd_lock); 478 timerfd_curval(tfd, curr_value); 479 mtx_unlock(&tfd->tfd_lock); 480 481 fdrop(fp, td); 482 return (0); 483 } 484 485 int 486 kern_timerfd_settime(struct thread *td, int fd, int flags, 487 const struct itimerspec *new_value, struct itimerspec *old_value) 488 { 489 struct file *fp; 490 struct timerfd *tfd; 491 struct timespec ts; 492 int error = 0; 493 494 if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0) 495 return (EINVAL); 496 if (!timespecvalid_interval(&new_value->it_value) || 497 !timespecvalid_interval(&new_value->it_interval)) 498 return (EINVAL); 499 500 error = fget(td, fd, &cap_write_rights, &fp); 501 if (error != 0) 502 return (error); 503 tfd = fp->f_data; 504 if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) { 505 fdrop(fp, td); 506 return (EINVAL); 507 } 508 509 mtx_lock(&tfd->tfd_lock); 510 getnanotime(&tfd->tfd_mtim); 511 tfd->tfd_timflags = flags; 512 513 /* Store old itimerspec, if applicable. */ 514 if (old_value != NULL) 515 timerfd_curval(tfd, old_value); 516 517 /* Set new expiration. */ 518 tfd->tfd_time = *new_value; 519 if (timespecisset(&tfd->tfd_time.it_value)) { 520 if ((flags & TFD_TIMER_ABSTIME) == 0) { 521 nanouptime(&ts); 522 timespecadd(&tfd->tfd_time.it_value, &ts, 523 &tfd->tfd_time.it_value); 524 } else if (tfd->tfd_clockid == CLOCK_REALTIME) { 525 /* ECANCELED if unread jump is pending. */ 526 if (tfd->tfd_jumped == TFD_CANCELED) 527 error = ECANCELED; 528 /* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */ 529 timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim, 530 &tfd->tfd_time.it_value); 531 } 532 callout_reset_sbt(&tfd->tfd_callout, 533 tstosbt(tfd->tfd_time.it_value), 534 0, timerfd_expire, tfd, C_ABSOLUTE); 535 } else { 536 callout_stop(&tfd->tfd_callout); 537 } 538 tfd->tfd_count = 0; 539 tfd->tfd_expired = false; 540 tfd->tfd_jumped = TFD_NOJUMP; 541 mtx_unlock(&tfd->tfd_lock); 542 543 fdrop(fp, td); 544 return (error); 545 } 546 547 int 548 sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap) 549 { 550 return (kern_timerfd_create(td, uap->clockid, uap->flags)); 551 } 552 553 int 554 sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap) 555 { 556 struct itimerspec curr_value; 557 int error; 558 559 error = kern_timerfd_gettime(td, uap->fd, &curr_value); 560 if (error == 0) 561 error = copyout(&curr_value, uap->curr_value, 562 sizeof(curr_value)); 563 564 return (error); 565 } 566 567 int 568 sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap) 569 { 570 struct itimerspec new_value, old_value; 571 int error; 572 573 error = copyin(uap->new_value, &new_value, sizeof(new_value)); 574 if (error != 0) 575 return (error); 576 if (uap->old_value == NULL) { 577 error = kern_timerfd_settime(td, uap->fd, uap->flags, 578 &new_value, NULL); 579 } else { 580 error = kern_timerfd_settime(td, uap->fd, uap->flags, 581 &new_value, &old_value); 582 if (error == 0) 583 error = copyout(&old_value, uap->old_value, 584 sizeof(old_value)); 585 } 586 return (error); 587 } 588 589 #ifdef COMPAT_FREEBSD32 590 int 591 freebsd32_timerfd_gettime(struct thread *td, 592 struct freebsd32_timerfd_gettime_args *uap) 593 { 594 struct itimerspec curr_value; 595 struct itimerspec32 curr_value32; 596 int error; 597 598 error = kern_timerfd_gettime(td, uap->fd, &curr_value); 599 if (error == 0) { 600 CP(curr_value, curr_value32, it_value.tv_sec); 601 CP(curr_value, curr_value32, it_value.tv_nsec); 602 CP(curr_value, curr_value32, it_interval.tv_sec); 603 CP(curr_value, curr_value32, it_interval.tv_nsec); 604 error = copyout(&curr_value32, uap->curr_value, 605 sizeof(curr_value32)); 606 } 607 608 return (error); 609 } 610 611 int 612 freebsd32_timerfd_settime(struct thread *td, 613 struct freebsd32_timerfd_settime_args *uap) 614 { 615 struct itimerspec new_value, old_value; 616 struct itimerspec32 new_value32, old_value32; 617 int error; 618 619 error = copyin(uap->new_value, &new_value32, sizeof(new_value32)); 620 if (error != 0) 621 return (error); 622 CP(new_value32, new_value, it_value.tv_sec); 623 CP(new_value32, new_value, it_value.tv_nsec); 624 CP(new_value32, new_value, it_interval.tv_sec); 625 CP(new_value32, new_value, it_interval.tv_nsec); 626 if (uap->old_value == NULL) { 627 error = kern_timerfd_settime(td, uap->fd, uap->flags, 628 &new_value, NULL); 629 } else { 630 error = kern_timerfd_settime(td, uap->fd, uap->flags, 631 &new_value, &old_value); 632 if (error == 0) { 633 CP(old_value, old_value32, it_value.tv_sec); 634 CP(old_value, old_value32, it_value.tv_nsec); 635 CP(old_value, old_value32, it_interval.tv_sec); 636 CP(old_value, old_value32, it_interval.tv_nsec); 637 error = copyout(&old_value32, uap->old_value, 638 sizeof(old_value32)); 639 } 640 } 641 return (error); 642 } 643 #endif 644