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