xref: /freebsd/sys/kern/sys_timerfd.c (revision 815b7436a7c6302365b6514194d27d41cb736227)
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