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