Page MenuHomeFreeBSD

D38459.diff
No OneTemporary

D38459.diff

diff --git a/lib/libc/sys/Symbol.map b/lib/libc/sys/Symbol.map
--- a/lib/libc/sys/Symbol.map
+++ b/lib/libc/sys/Symbol.map
@@ -421,6 +421,9 @@
fspacectl;
kqueuex;
swapoff;
+ timerfd_create;
+ timerfd_gettime;
+ timerfd_settime;
};
FBSDprivate_1.0 {
diff --git a/sys/bsm/audit_kevents.h b/sys/bsm/audit_kevents.h
--- a/sys/bsm/audit_kevents.h
+++ b/sys/bsm/audit_kevents.h
@@ -663,6 +663,7 @@
#define AUE_AIO_WRITEV 43267 /* FreeBSD-specific. */
#define AUE_AIO_READV 43268 /* FreeBSD-specific. */
#define AUE_FSPACECTL 43269 /* FreeBSD-specific. */
+#define AUE_TIMERFD 43270 /* FreeBSD/Linux. */
/*
* Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the
diff --git a/sys/compat/freebsd32/freebsd32_proto.h b/sys/compat/freebsd32/freebsd32_proto.h
--- a/sys/compat/freebsd32/freebsd32_proto.h
+++ b/sys/compat/freebsd32/freebsd32_proto.h
@@ -685,6 +685,16 @@
struct freebsd32_aio_readv_args {
char aiocbp_l_[PADL_(struct aiocb32 *)]; struct aiocb32 * aiocbp; char aiocbp_r_[PADR_(struct aiocb32 *)];
};
+struct freebsd32_timerfd_gettime_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char curr_value_l_[PADL_(struct itimerspec32 *)]; struct itimerspec32 * curr_value; char curr_value_r_[PADR_(struct itimerspec32 *)];
+};
+struct freebsd32_timerfd_settime_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+ char new_value_l_[PADL_(const struct itimerspec32 *)]; const struct itimerspec32 * new_value; char new_value_r_[PADR_(const struct itimerspec32 *)];
+ char old_value_l_[PADL_(struct itimerspec32 *)]; struct itimerspec32 * old_value; char old_value_r_[PADR_(struct itimerspec32 *)];
+};
int freebsd32_wait4(struct thread *, struct freebsd32_wait4_args *);
int freebsd32_ptrace(struct thread *, struct freebsd32_ptrace_args *);
int freebsd32_recvmsg(struct thread *, struct freebsd32_recvmsg_args *);
@@ -800,6 +810,8 @@
int freebsd32___sysctlbyname(struct thread *, struct freebsd32___sysctlbyname_args *);
int freebsd32_aio_writev(struct thread *, struct freebsd32_aio_writev_args *);
int freebsd32_aio_readv(struct thread *, struct freebsd32_aio_readv_args *);
+int freebsd32_timerfd_gettime(struct thread *, struct freebsd32_timerfd_gettime_args *);
+int freebsd32_timerfd_settime(struct thread *, struct freebsd32_timerfd_settime_args *);
#ifdef COMPAT_43
@@ -1293,6 +1305,8 @@
#define FREEBSD32_SYS_AUE_freebsd32___sysctlbyname AUE_SYSCTL
#define FREEBSD32_SYS_AUE_freebsd32_aio_writev AUE_AIO_WRITEV
#define FREEBSD32_SYS_AUE_freebsd32_aio_readv AUE_AIO_READV
+#define FREEBSD32_SYS_AUE_freebsd32_timerfd_gettime AUE_TIMERFD
+#define FREEBSD32_SYS_AUE_freebsd32_timerfd_settime AUE_TIMERFD
#undef PAD_
#undef PADL_
diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h
--- a/sys/compat/freebsd32/freebsd32_syscall.h
+++ b/sys/compat/freebsd32/freebsd32_syscall.h
@@ -502,4 +502,7 @@
#define FREEBSD32_SYS_sched_getcpu 581
#define FREEBSD32_SYS_swapoff 582
#define FREEBSD32_SYS_kqueuex 583
-#define FREEBSD32_SYS_MAXSYSCALL 584
+#define FREEBSD32_SYS_timerfd_create 584
+#define FREEBSD32_SYS_freebsd32_timerfd_gettime 585
+#define FREEBSD32_SYS_freebsd32_timerfd_settime 586
+#define FREEBSD32_SYS_MAXSYSCALL 587
diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c
--- a/sys/compat/freebsd32/freebsd32_syscalls.c
+++ b/sys/compat/freebsd32/freebsd32_syscalls.c
@@ -590,4 +590,7 @@
"sched_getcpu", /* 581 = sched_getcpu */
"swapoff", /* 582 = swapoff */
"kqueuex", /* 583 = kqueuex */
+ "timerfd_create", /* 584 = timerfd_create */
+ "freebsd32_timerfd_gettime", /* 585 = freebsd32_timerfd_gettime */
+ "freebsd32_timerfd_settime", /* 586 = freebsd32_timerfd_settime */
};
diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c
--- a/sys/compat/freebsd32/freebsd32_sysent.c
+++ b/sys/compat/freebsd32/freebsd32_sysent.c
@@ -646,4 +646,7 @@
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_sched_getcpu, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 581 = sched_getcpu */
{ .sy_narg = AS(swapoff_args), .sy_call = (sy_call_t *)sys_swapoff, .sy_auevent = AUE_SWAPOFF, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 582 = swapoff */
{ .sy_narg = AS(kqueuex_args), .sy_call = (sy_call_t *)sys_kqueuex, .sy_auevent = AUE_KQUEUE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 583 = kqueuex */
+ { .sy_narg = AS(timerfd_create_args), .sy_call = (sy_call_t *)sys_timerfd_create, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 584 = timerfd_create */
+ { .sy_narg = AS(freebsd32_timerfd_gettime_args), .sy_call = (sy_call_t *)freebsd32_timerfd_gettime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 585 = freebsd32_timerfd_gettime */
+ { .sy_narg = AS(freebsd32_timerfd_settime_args), .sy_call = (sy_call_t *)freebsd32_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 586 = freebsd32_timerfd_settime */
};
diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c
--- a/sys/compat/freebsd32/freebsd32_systrace_args.c
+++ b/sys/compat/freebsd32/freebsd32_systrace_args.c
@@ -3328,6 +3328,32 @@
*n_args = 1;
break;
}
+ /* timerfd_create */
+ case 584: {
+ struct timerfd_create_args *p = params;
+ iarg[a++] = p->clockid; /* int */
+ iarg[a++] = p->flags; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* freebsd32_timerfd_gettime */
+ case 585: {
+ struct freebsd32_timerfd_gettime_args *p = params;
+ iarg[a++] = p->fd; /* int */
+ uarg[a++] = (intptr_t)p->curr_value; /* struct itimerspec32 * */
+ *n_args = 2;
+ break;
+ }
+ /* freebsd32_timerfd_settime */
+ case 586: {
+ struct freebsd32_timerfd_settime_args *p = params;
+ iarg[a++] = p->fd; /* int */
+ iarg[a++] = p->flags; /* int */
+ uarg[a++] = (intptr_t)p->new_value; /* const struct itimerspec32 * */
+ uarg[a++] = (intptr_t)p->old_value; /* struct itimerspec32 * */
+ *n_args = 4;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -8981,6 +9007,51 @@
break;
};
break;
+ /* timerfd_create */
+ case 584:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd32_timerfd_gettime */
+ case 585:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland struct itimerspec32 *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* freebsd32_timerfd_settime */
+ case 586:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "userland const struct itimerspec32 *";
+ break;
+ case 3:
+ p = "userland struct itimerspec32 *";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -10844,6 +10915,21 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* timerfd_create */
+ case 584:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* freebsd32_timerfd_gettime */
+ case 585:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* freebsd32_timerfd_settime */
+ case 586:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/compat/linux/linux_event.h b/sys/compat/linux/linux_event.h
--- a/sys/compat/linux/linux_event.h
+++ b/sys/compat/linux/linux_event.h
@@ -56,15 +56,4 @@
#define LINUX_EFD_SEMAPHORE (1 << 0)
-#define LINUX_TFD_TIMER_ABSTIME (1 << 0)
-#define LINUX_TFD_TIMER_CANCEL_ON_SET (1 << 1)
-#define LINUX_TFD_CLOEXEC LINUX_O_CLOEXEC
-#define LINUX_TFD_NONBLOCK LINUX_O_NONBLOCK
-
-#define LINUX_TFD_SHARED_FCNTL_FLAGS (LINUX_TFD_CLOEXEC \
- |LINUX_TFD_NONBLOCK)
-#define LINUX_TFD_CREATE_FLAGS LINUX_TFD_SHARED_FCNTL_FLAGS
-#define LINUX_TFD_SETTIME_FLAGS (LINUX_TFD_TIMER_ABSTIME \
- |LINUX_TFD_TIMER_CANCEL_ON_SET)
-
#endif /* !_LINUX_EVENT_H_ */
diff --git a/sys/compat/linux/linux_event.c b/sys/compat/linux/linux_event.c
--- a/sys/compat/linux/linux_event.c
+++ b/sys/compat/linux/linux_event.c
@@ -47,6 +47,7 @@
#include <sys/specialfd.h>
#include <sys/sx.h>
#include <sys/syscallsubr.h>
+#include <sys/timerfd.h>
#include <sys/timespec.h>
#include <sys/user.h>
@@ -102,55 +103,6 @@
int error;
};
-/* timerfd */
-typedef uint64_t timerfd_t;
-
-static fo_rdwr_t timerfd_read;
-static fo_ioctl_t timerfd_ioctl;
-static fo_poll_t timerfd_poll;
-static fo_kqfilter_t timerfd_kqfilter;
-static fo_stat_t timerfd_stat;
-static fo_close_t timerfd_close;
-static fo_fill_kinfo_t timerfd_fill_kinfo;
-
-static struct fileops timerfdops = {
- .fo_read = timerfd_read,
- .fo_write = invfo_rdwr,
- .fo_truncate = invfo_truncate,
- .fo_ioctl = timerfd_ioctl,
- .fo_poll = timerfd_poll,
- .fo_kqfilter = timerfd_kqfilter,
- .fo_stat = timerfd_stat,
- .fo_close = timerfd_close,
- .fo_chmod = invfo_chmod,
- .fo_chown = invfo_chown,
- .fo_sendfile = invfo_sendfile,
- .fo_fill_kinfo = timerfd_fill_kinfo,
- .fo_flags = DFLAG_PASSABLE
-};
-
-static void filt_timerfddetach(struct knote *kn);
-static int filt_timerfdread(struct knote *kn, long hint);
-
-static struct filterops timerfd_rfiltops = {
- .f_isfd = 1,
- .f_detach = filt_timerfddetach,
- .f_event = filt_timerfdread
-};
-
-struct timerfd {
- clockid_t tfd_clockid;
- struct itimerspec tfd_time;
- struct callout tfd_callout;
- timerfd_t tfd_count;
- bool tfd_canceled;
- struct selinfo tfd_sel;
- struct mtx tfd_lock;
-};
-
-static void linux_timerfd_expire(void *);
-static void linux_timerfd_curval(struct timerfd *, struct itimerspec *);
-
static int
epoll_create_common(struct thread *td, int flags)
{
@@ -661,255 +613,14 @@
int
linux_timerfd_create(struct thread *td, struct linux_timerfd_create_args *args)
{
- struct timerfd *tfd;
- struct file *fp;
clockid_t clockid;
- int fflags, fd, error;
-
- if ((args->flags & ~LINUX_TFD_CREATE_FLAGS) != 0)
- return (EINVAL);
-
- error = linux_to_native_clockid(&clockid, args->clockid);
- if (error != 0)
- return (error);
- if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
- return (EINVAL);
-
- fflags = 0;
- if ((args->flags & LINUX_TFD_CLOEXEC) != 0)
- fflags |= O_CLOEXEC;
-
- error = falloc(td, &fp, &fd, fflags);
- if (error != 0)
- return (error);
-
- tfd = malloc(sizeof(*tfd), M_EPOLL, M_WAITOK | M_ZERO);
- tfd->tfd_clockid = clockid;
- mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF);
-
- callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0);
- knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock);
-
- fflags = FREAD;
- if ((args->flags & LINUX_O_NONBLOCK) != 0)
- fflags |= FNONBLOCK;
-
- finit(fp, fflags, DTYPE_LINUXTFD, tfd, &timerfdops);
- fdrop(fp, td);
-
- td->td_retval[0] = fd;
- return (error);
-}
-
-static int
-timerfd_close(struct file *fp, struct thread *td)
-{
- struct timerfd *tfd;
-
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL)
- return (EINVAL);
-
- timespecclear(&tfd->tfd_time.it_value);
- timespecclear(&tfd->tfd_time.it_interval);
-
- callout_drain(&tfd->tfd_callout);
-
- seldrain(&tfd->tfd_sel);
- knlist_destroy(&tfd->tfd_sel.si_note);
-
- fp->f_ops = &badfileops;
- mtx_destroy(&tfd->tfd_lock);
- free(tfd, M_EPOLL);
-
- return (0);
-}
-
-static int
-timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
- int flags, struct thread *td)
-{
- struct timerfd *tfd;
- timerfd_t count;
- int error;
-
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL)
- return (EINVAL);
-
- if (uio->uio_resid < sizeof(timerfd_t))
- return (EINVAL);
-
- error = 0;
- mtx_lock(&tfd->tfd_lock);
-retry:
- if (tfd->tfd_canceled) {
- tfd->tfd_count = 0;
- mtx_unlock(&tfd->tfd_lock);
- return (ECANCELED);
- }
- if (tfd->tfd_count == 0) {
- if ((fp->f_flag & FNONBLOCK) != 0) {
- mtx_unlock(&tfd->tfd_lock);
- return (EAGAIN);
- }
- error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock, PCATCH, "ltfdrd", 0);
- if (error == 0)
- goto retry;
- }
- if (error == 0) {
- count = tfd->tfd_count;
- tfd->tfd_count = 0;
- mtx_unlock(&tfd->tfd_lock);
- error = uiomove(&count, sizeof(timerfd_t), uio);
- } else
- mtx_unlock(&tfd->tfd_lock);
-
- return (error);
-}
-
-static int
-timerfd_poll(struct file *fp, int events, struct ucred *active_cred,
- struct thread *td)
-{
- struct timerfd *tfd;
- int revents = 0;
-
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL)
- return (POLLERR);
-
- mtx_lock(&tfd->tfd_lock);
- if ((events & (POLLIN|POLLRDNORM)) && tfd->tfd_count > 0)
- revents |= events & (POLLIN|POLLRDNORM);
- if (revents == 0)
- selrecord(td, &tfd->tfd_sel);
- mtx_unlock(&tfd->tfd_lock);
-
- return (revents);
-}
-
-static int
-timerfd_kqfilter(struct file *fp, struct knote *kn)
-{
- struct timerfd *tfd;
-
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL)
- return (EINVAL);
-
- if (kn->kn_filter == EVFILT_READ)
- kn->kn_fop = &timerfd_rfiltops;
- else
- return (EINVAL);
-
- kn->kn_hook = tfd;
- knlist_add(&tfd->tfd_sel.si_note, kn, 0);
-
- return (0);
-}
-
-static void
-filt_timerfddetach(struct knote *kn)
-{
- struct timerfd *tfd = kn->kn_hook;
-
- mtx_lock(&tfd->tfd_lock);
- knlist_remove(&tfd->tfd_sel.si_note, kn, 1);
- mtx_unlock(&tfd->tfd_lock);
-}
-
-static int
-filt_timerfdread(struct knote *kn, long hint)
-{
- struct timerfd *tfd = kn->kn_hook;
-
- return (tfd->tfd_count > 0);
-}
-
-static int
-timerfd_ioctl(struct file *fp, u_long cmd, void *data,
- struct ucred *active_cred, struct thread *td)
-{
-
- if (fp->f_data == NULL || fp->f_type != DTYPE_LINUXTFD)
- return (EINVAL);
-
- switch (cmd) {
- case FIONBIO:
- case FIOASYNC:
- return (0);
- }
-
- return (ENOTTY);
-}
-
-static int
-timerfd_stat(struct file *fp, struct stat *st, struct ucred *active_cred)
-{
-
- return (ENXIO);
-}
-
-static int
-timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif, struct filedesc *fdp)
-{
-
- kif->kf_type = KF_TYPE_UNKNOWN;
- return (0);
-}
-
-static void
-linux_timerfd_clocktime(struct timerfd *tfd, struct timespec *ts)
-{
-
- if (tfd->tfd_clockid == CLOCK_REALTIME)
- getnanotime(ts);
- else /* CLOCK_MONOTONIC */
- getnanouptime(ts);
-}
-
-static void
-linux_timerfd_curval(struct timerfd *tfd, struct itimerspec *ots)
-{
- struct timespec cts;
-
- linux_timerfd_clocktime(tfd, &cts);
- *ots = tfd->tfd_time;
- if (ots->it_value.tv_sec != 0 || ots->it_value.tv_nsec != 0) {
- timespecsub(&ots->it_value, &cts, &ots->it_value);
- if (ots->it_value.tv_sec < 0 ||
- (ots->it_value.tv_sec == 0 &&
- ots->it_value.tv_nsec == 0)) {
- ots->it_value.tv_sec = 0;
- ots->it_value.tv_nsec = 1;
- }
- }
-}
-
-static int
-linux_timerfd_gettime_common(struct thread *td, int fd, struct itimerspec *ots)
-{
- struct timerfd *tfd;
- struct file *fp;
int error;
- error = fget(td, fd, &cap_read_rights, &fp);
+ error = linux_to_native_clockid(&clockid, args->clockid);
if (error != 0)
return (error);
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL) {
- error = EINVAL;
- goto out;
- }
-
- mtx_lock(&tfd->tfd_lock);
- linux_timerfd_curval(tfd, ots);
- mtx_unlock(&tfd->tfd_lock);
-out:
- fdrop(fp, td);
- return (error);
+ return (kern_timerfd_create(td, clockid, args->flags));
}
int
@@ -919,84 +630,14 @@
struct itimerspec ots;
int error;
- error = linux_timerfd_gettime_common(td, args->fd, &ots);
+ error = kern_timerfd_gettime(td, args->fd, &ots);
if (error != 0)
return (error);
- error = native_to_linux_itimerspec(&lots, &ots);
- if (error == 0)
- error = copyout(&lots, args->old_value, sizeof(lots));
- return (error);
-}
-
-#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
-int
-linux_timerfd_gettime64(struct thread *td, struct linux_timerfd_gettime64_args *args)
-{
- struct l_itimerspec64 lots;
- struct itimerspec ots;
- int error;
- error = linux_timerfd_gettime_common(td, args->fd, &ots);
- if (error != 0)
- return (error);
- error = native_to_linux_itimerspec64(&lots, &ots);
+ error = native_to_linux_itimerspec(&lots, &ots);
if (error == 0)
error = copyout(&lots, args->old_value, sizeof(lots));
- return (error);
-}
-#endif
-
-static int
-linux_timerfd_settime_common(struct thread *td, int fd, int flags,
- struct itimerspec *nts, struct itimerspec *oval)
-{
- struct timespec cts, ts;
- struct timerfd *tfd;
- struct timeval tv;
- struct file *fp;
- int error;
-
- if ((flags & ~LINUX_TFD_SETTIME_FLAGS) != 0)
- return (EINVAL);
-
- error = fget(td, fd, &cap_write_rights, &fp);
- if (error != 0)
- return (error);
- tfd = fp->f_data;
- if (fp->f_type != DTYPE_LINUXTFD || tfd == NULL) {
- error = EINVAL;
- goto out;
- }
-
- mtx_lock(&tfd->tfd_lock);
- if (!timespecisset(&nts->it_value))
- timespecclear(&nts->it_interval);
- if (oval != NULL)
- linux_timerfd_curval(tfd, oval);
-
- bcopy(nts, &tfd->tfd_time, sizeof(*nts));
- tfd->tfd_count = 0;
- if (timespecisset(&nts->it_value)) {
- linux_timerfd_clocktime(tfd, &cts);
- ts = nts->it_value;
- if ((flags & LINUX_TFD_TIMER_ABSTIME) == 0) {
- timespecadd(&tfd->tfd_time.it_value, &cts,
- &tfd->tfd_time.it_value);
- } else {
- timespecsub(&ts, &cts, &ts);
- }
- TIMESPEC_TO_TIMEVAL(&tv, &ts);
- callout_reset(&tfd->tfd_callout, tvtohz(&tv),
- linux_timerfd_expire, tfd);
- tfd->tfd_canceled = false;
- } else {
- tfd->tfd_canceled = true;
- callout_stop(&tfd->tfd_callout);
- }
- mtx_unlock(&tfd->tfd_lock);
-out:
- fdrop(fp, td);
return (error);
}
@@ -1004,7 +645,7 @@
linux_timerfd_settime(struct thread *td, struct linux_timerfd_settime_args *args)
{
struct l_itimerspec lots;
- struct itimerspec nts, ots, *pots;
+ struct itimerspec nts, ots;
int error;
error = copyin(args->new_value, &lots, sizeof(lots));
@@ -1013,23 +654,43 @@
error = linux_to_native_itimerspec(&nts, &lots);
if (error != 0)
return (error);
- pots = (args->old_value != NULL ? &ots : NULL);
- error = linux_timerfd_settime_common(td, args->fd, args->flags,
- &nts, pots);
+ if (args->old_value == NULL)
+ error = kern_timerfd_settime(td, args->fd, args->flags, &nts, NULL);
+ else
+ error = kern_timerfd_settime(td, args->fd, args->flags, &nts, &ots);
if (error == 0 && args->old_value != NULL) {
error = native_to_linux_itimerspec(&lots, &ots);
if (error == 0)
error = copyout(&lots, args->old_value, sizeof(lots));
}
+
return (error);
}
#if defined(__i386__) || (defined(__amd64__) && defined(COMPAT_LINUX32))
+int
+linux_timerfd_gettime64(struct thread *td, struct linux_timerfd_gettime64_args *args)
+{
+ struct l_itimerspec64 lots;
+ struct itimerspec ots;
+ int error;
+
+ error = kern_timerfd_gettime(td, args->fd, &ots);
+ if (error != 0)
+ return (error);
+
+ error = native_to_linux_itimerspec64(&lots, &ots);
+ if (error == 0)
+ error = copyout(&lots, args->old_value, sizeof(lots));
+
+ return (error);
+}
+
int
linux_timerfd_settime64(struct thread *td, struct linux_timerfd_settime64_args *args)
{
struct l_itimerspec64 lots;
- struct itimerspec nts, ots, *pots;
+ struct itimerspec nts, ots;
int error;
error = copyin(args->new_value, &lots, sizeof(lots));
@@ -1038,50 +699,16 @@
error = linux_to_native_itimerspec64(&nts, &lots);
if (error != 0)
return (error);
- pots = (args->old_value != NULL ? &ots : NULL);
- error = linux_timerfd_settime_common(td, args->fd, args->flags,
- &nts, pots);
+ if (args->old_value == NULL)
+ error = kern_timerfd_settime(td, args->fd, args->flags, &nts, NULL);
+ else
+ error = kern_timerfd_settime(td, args->fd, args->flags, &nts, &ots);
if (error == 0 && args->old_value != NULL) {
error = native_to_linux_itimerspec64(&lots, &ots);
if (error == 0)
error = copyout(&lots, args->old_value, sizeof(lots));
}
+
return (error);
}
#endif
-
-static void
-linux_timerfd_expire(void *arg)
-{
- struct timespec cts, ts;
- struct timeval tv;
- struct timerfd *tfd;
-
- tfd = (struct timerfd *)arg;
-
- linux_timerfd_clocktime(tfd, &cts);
- if (timespeccmp(&cts, &tfd->tfd_time.it_value, >=)) {
- if (timespecisset(&tfd->tfd_time.it_interval))
- timespecadd(&tfd->tfd_time.it_value,
- &tfd->tfd_time.it_interval,
- &tfd->tfd_time.it_value);
- else
- /* single shot timer */
- timespecclear(&tfd->tfd_time.it_value);
- if (timespecisset(&tfd->tfd_time.it_value)) {
- timespecsub(&tfd->tfd_time.it_value, &cts, &ts);
- TIMESPEC_TO_TIMEVAL(&tv, &ts);
- callout_reset(&tfd->tfd_callout, tvtohz(&tv),
- linux_timerfd_expire, tfd);
- }
- tfd->tfd_count++;
- KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0);
- selwakeup(&tfd->tfd_sel);
- wakeup(&tfd->tfd_count);
- } else if (timespecisset(&tfd->tfd_time.it_value)) {
- timespecsub(&tfd->tfd_time.it_value, &cts, &ts);
- TIMESPEC_TO_TIMEVAL(&tv, &ts);
- callout_reset(&tfd->tfd_callout, tvtohz(&tv),
- linux_timerfd_expire, tfd);
- }
-}
diff --git a/sys/conf/files b/sys/conf/files
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -3913,6 +3913,7 @@
kern/sys_procdesc.c standard
kern/sys_process.c standard
kern/sys_socket.c standard
+kern/sys_timerfd.c standard
kern/syscalls.c standard
kern/sysv_ipc.c standard
kern/sysv_msg.c optional sysvmsg
diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c
--- a/sys/kern/init_sysent.c
+++ b/sys/kern/init_sysent.c
@@ -645,4 +645,7 @@
{ .sy_narg = 0, .sy_call = (sy_call_t *)sys_sched_getcpu, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 581 = sched_getcpu */
{ .sy_narg = AS(swapoff_args), .sy_call = (sy_call_t *)sys_swapoff, .sy_auevent = AUE_SWAPOFF, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 582 = swapoff */
{ .sy_narg = AS(kqueuex_args), .sy_call = (sy_call_t *)sys_kqueuex, .sy_auevent = AUE_KQUEUE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 583 = kqueuex */
+ { .sy_narg = AS(timerfd_create_args), .sy_call = (sy_call_t *)sys_timerfd_create, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 584 = timerfd_create */
+ { .sy_narg = AS(timerfd_gettime_args), .sy_call = (sy_call_t *)sys_timerfd_gettime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 585 = timerfd_gettime */
+ { .sy_narg = AS(timerfd_settime_args), .sy_call = (sy_call_t *)sys_timerfd_settime, .sy_auevent = AUE_TIMERFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 586 = timerfd_settime */
};
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -4939,8 +4939,8 @@
return ("proc");
case DTYPE_EVENTFD:
return ("eventfd");
- case DTYPE_LINUXTFD:
- return ("ltimer");
+ case DTYPE_TIMERFD:
+ return ("timerfd");
default:
return ("unkn");
}
diff --git a/sys/kern/kern_tc.c b/sys/kern/kern_tc.c
--- a/sys/kern/kern_tc.c
+++ b/sys/kern/kern_tc.c
@@ -36,6 +36,7 @@
#include <sys/systm.h>
#include <sys/timeffc.h>
#include <sys/timepps.h>
+#include <sys/timerfd.h>
#include <sys/timetc.h>
#include <sys/timex.h>
#include <sys/vdso.h>
@@ -1307,6 +1308,7 @@
/* Avoid rtc_generation == 0, since td_rtcgen == 0 is special. */
atomic_add_rel_int(&rtc_generation, 2);
+ timerfd_jumped();
sleepq_chains_remove_matching(sleeping_on_old_rtc);
if (timestepwarnings) {
nanotime(&taft);
diff --git a/sys/kern/sys_timerfd.c b/sys/kern/sys_timerfd.c
new file mode 100644
--- /dev/null
+++ b/sys/kern/sys_timerfd.c
@@ -0,0 +1,631 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2014 Dmitry Chagin <dchagin@FreeBSD.org>
+ * Copyright (c) 2023 Jake Freeland <jfree@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/callout.h>
+#include <sys/fcntl.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/filio.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+#include <sys/selinfo.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/sysent.h>
+#include <sys/sysproto.h>
+#include <sys/timerfd.h>
+#include <sys/timespec.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+
+#include <security/audit/audit.h>
+
+#ifdef COMPAT_FREEBSD32
+#include <compat/freebsd32/freebsd32.h>
+#include <compat/freebsd32/freebsd32_proto.h>
+#endif
+
+static MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures");
+static LIST_HEAD(, timerfd) timerfd_head;
+static struct unrhdr64 tfdino_unr;
+
+#define TFD_NOJUMP 0 /* Realtime clock has not jumped. */
+#define TFD_READ 1 /* Jumped, tfd has been read since. */
+#define TFD_ZREAD 2 /* Jumped backwards, CANCEL_ON_SET=false. */
+#define TFD_CANCELED 4 /* Jumped, CANCEL_ON_SET=true. */
+#define TFD_JUMPED (TFD_ZREAD | TFD_CANCELED)
+
+struct timerfd {
+ /* User specified. */
+ struct itimerspec tfd_time; /* tfd timer */
+ clockid_t tfd_clockid; /* timing base */
+ int tfd_flags; /* creation flags */
+ int tfd_timflags; /* timer flags */
+
+ /* Used internally. */
+ timerfd_t tfd_count; /* expiration count since last read */
+ bool tfd_expired; /* true upon initial expiration */
+ struct mtx tfd_lock; /* mtx lock */
+ struct callout tfd_callout; /* expiration notification */
+ struct selinfo tfd_sel; /* I/O alerts */
+ struct timespec tfd_boottim; /* cached boottime */
+ int tfd_jumped; /* timer jump status */
+ LIST_ENTRY(timerfd) entry; /* entry in list */
+
+ /* For stat(2). */
+ ino_t tfd_ino; /* inode number */
+ struct timespec tfd_atim; /* time of last read */
+ struct timespec tfd_mtim; /* time of last settime */
+ struct timespec tfd_birthtim; /* creation time */
+};
+
+static void
+timerfd_init(void *data)
+{
+ new_unrhdr64(&tfdino_unr, 1);
+}
+
+SYSINIT(timerfd, SI_SUB_VFS, SI_ORDER_ANY, timerfd_init, NULL);
+
+static inline void
+timerfd_getboottime(struct timespec *ts)
+{
+ struct timeval tv;
+ getboottime(&tv);
+ TIMEVAL_TO_TIMESPEC(&tv, ts);
+}
+
+/*
+ * Call when a discontinuous jump has occured in CLOCK_REALTIME and
+ * update timerfd's cached boottime. A jump can be triggered using
+ * functions like clock_settime(2) or settimeofday(2).
+ *
+ * Timer is marked TFD_CANCELED if TFD_TIMER_CANCEL_ON_SET is set
+ * and the realtime clock jumps.
+ * Timer is marked TFD_ZREAD if TFD_TIMER_CANCEL_ON_SET is not set,
+ * but the realtime clock jumps backwards.
+ */
+void
+timerfd_jumped(void)
+{
+ struct timerfd *tfd;
+ struct timespec boottime, diff;
+
+ timerfd_getboottime(&boottime);
+ LIST_FOREACH(tfd, &timerfd_head, entry) {
+ mtx_lock(&tfd->tfd_lock);
+ if (tfd->tfd_clockid != CLOCK_REALTIME ||
+ (tfd->tfd_timflags & TFD_TIMER_ABSTIME) == 0 ||
+ timespeccmp(&boottime, &tfd->tfd_boottim, ==)) {
+ mtx_unlock(&tfd->tfd_lock);
+ continue;
+ }
+
+ if (callout_active(&tfd->tfd_callout)) {
+ if ((tfd->tfd_timflags & TFD_TIMER_CANCEL_ON_SET) != 0)
+ tfd->tfd_jumped = TFD_CANCELED;
+ else if (timespeccmp(&boottime, &tfd->tfd_boottim, <))
+ tfd->tfd_jumped = TFD_ZREAD;
+
+ /*
+ * Do not reschedule callout when
+ * inside interval time loop.
+ */
+ if (!tfd->tfd_expired) {
+ timespecsub(&boottime,
+ &tfd->tfd_boottim, &diff);
+ timespecsub(&tfd->tfd_time.it_value,
+ &diff, &tfd->tfd_time.it_value);
+ if (callout_stop(&tfd->tfd_callout) == 1) {
+ callout_schedule_sbt(&tfd->tfd_callout,
+ tstosbt(tfd->tfd_time.it_value),
+ 0, C_ABSOLUTE);
+ }
+ }
+ }
+
+ tfd->tfd_boottim = boottime;
+ mtx_unlock(&tfd->tfd_lock);
+ }
+}
+
+static int
+timerfd_read(struct file *fp, struct uio *uio, struct ucred *active_cred,
+ int flags, struct thread *td)
+{
+ struct timerfd *tfd = fp->f_data;
+ timerfd_t count;
+ int error = 0;
+
+ if (uio->uio_resid < sizeof(timerfd_t))
+ return (EINVAL);
+
+ mtx_lock(&tfd->tfd_lock);
+retry:
+ getnanotime(&tfd->tfd_atim);
+ if ((tfd->tfd_jumped & TFD_JUMPED) != 0) {
+ if (tfd->tfd_jumped == TFD_CANCELED)
+ error = ECANCELED;
+ tfd->tfd_jumped = TFD_READ;
+ tfd->tfd_count = 0;
+ mtx_unlock(&tfd->tfd_lock);
+ return (error);
+ } else {
+ tfd->tfd_jumped = TFD_NOJUMP;
+ }
+ if (tfd->tfd_count == 0) {
+ if ((fp->f_flag & FNONBLOCK) != 0) {
+ mtx_unlock(&tfd->tfd_lock);
+ return (EAGAIN);
+ }
+ td->td_rtcgen = atomic_load_acq_int(&rtc_generation);
+ error = mtx_sleep(&tfd->tfd_count, &tfd->tfd_lock,
+ PCATCH, "tfdrd", 0);
+ if (error == 0) {
+ goto retry;
+ } else {
+ mtx_unlock(&tfd->tfd_lock);
+ return (error);
+ }
+ }
+
+ count = tfd->tfd_count;
+ tfd->tfd_count = 0;
+ mtx_unlock(&tfd->tfd_lock);
+ error = uiomove(&count, sizeof(timerfd_t), uio);
+
+ return (error);
+}
+
+static int
+timerfd_ioctl(struct file *fp, u_long cmd, void *data,
+ struct ucred *active_cred, struct thread *td)
+{
+ switch (cmd) {
+ case FIOASYNC:
+ if (*(int *)data != 0)
+ atomic_set_int(&fp->f_flag, FASYNC);
+ else
+ atomic_clear_int(&fp->f_flag, FASYNC);
+ return (0);
+ case FIONBIO:
+ if (*(int *)data != 0)
+ atomic_set_int(&fp->f_flag, FNONBLOCK);
+ else
+ atomic_clear_int(&fp->f_flag, FNONBLOCK);
+ return (0);
+ }
+ return (ENOTTY);
+}
+
+static int
+timerfd_poll(struct file *fp, int events, struct ucred *active_cred,
+ struct thread *td)
+{
+ struct timerfd *tfd = fp->f_data;
+ int revents = 0;
+
+ mtx_lock(&tfd->tfd_lock);
+ if ((events & (POLLIN | POLLRDNORM)) != 0 &&
+ tfd->tfd_count > 0 && tfd->tfd_jumped != TFD_READ)
+ revents |= events & (POLLIN | POLLRDNORM);
+ if (revents == 0)
+ selrecord(td, &tfd->tfd_sel);
+ mtx_unlock(&tfd->tfd_lock);
+
+ return (revents);
+}
+
+static void
+filt_timerfddetach(struct knote *kn)
+{
+ struct timerfd *tfd = kn->kn_hook;
+
+ mtx_lock(&tfd->tfd_lock);
+ knlist_remove(&tfd->tfd_sel.si_note, kn, 1);
+ mtx_unlock(&tfd->tfd_lock);
+}
+
+static int
+filt_timerfdread(struct knote *kn, long hint)
+{
+ struct timerfd *tfd = kn->kn_hook;
+
+ return (tfd->tfd_count > 0);
+}
+
+static struct filterops timerfd_rfiltops = {
+ .f_isfd = 1,
+ .f_detach = filt_timerfddetach,
+ .f_event = filt_timerfdread,
+};
+
+static int
+timerfd_kqfilter(struct file *fp, struct knote *kn)
+{
+ struct timerfd *tfd = fp->f_data;
+
+ if (kn->kn_filter != EVFILT_READ)
+ return (EINVAL);
+
+ kn->kn_fop = &timerfd_rfiltops;
+ kn->kn_hook = tfd;
+ knlist_add(&tfd->tfd_sel.si_note, kn, 0);
+
+ return (0);
+}
+
+static int
+timerfd_stat(struct file *fp, struct stat *sb, struct ucred *active_cred)
+{
+ struct timerfd *tfd = fp->f_data;
+
+ bzero(sb, sizeof(*sb));
+ sb->st_nlink = fp->f_count - 1;
+ sb->st_uid = fp->f_cred->cr_uid;
+ sb->st_gid = fp->f_cred->cr_gid;
+ sb->st_blksize = PAGE_SIZE;
+
+ mtx_lock(&tfd->tfd_lock);
+ sb->st_ino = tfd->tfd_ino;
+ sb->st_atim = tfd->tfd_atim;
+ sb->st_mtim = tfd->tfd_mtim;
+ sb->st_birthtim = tfd->tfd_birthtim;
+ mtx_unlock(&tfd->tfd_lock);
+
+ return (0);
+}
+
+static int
+timerfd_close(struct file *fp, struct thread *td)
+{
+ struct timerfd *tfd = fp->f_data;
+
+ callout_drain(&tfd->tfd_callout);
+ seldrain(&tfd->tfd_sel);
+ knlist_destroy(&tfd->tfd_sel.si_note);
+ mtx_destroy(&tfd->tfd_lock);
+ LIST_REMOVE(tfd, entry);
+ free(tfd, M_TIMERFD);
+ fp->f_ops = &badfileops;
+
+ return (0);
+}
+
+static int
+timerfd_fill_kinfo(struct file *fp, struct kinfo_file *kif,
+ struct filedesc *fdp)
+{
+
+ struct timerfd *tfd = fp->f_data;
+
+ kif->kf_type = KF_TYPE_TIMERFD;
+ mtx_lock(&tfd->tfd_lock);
+ kif->kf_un.kf_timerfd.kf_timerfd_clockid = tfd->tfd_clockid;
+ kif->kf_un.kf_timerfd.kf_timerfd_flags = tfd->tfd_flags;
+ kif->kf_un.kf_timerfd.kf_timerfd_addr = (uintptr_t)tfd;
+ mtx_unlock(&tfd->tfd_lock);
+
+ return (0);
+}
+
+static struct fileops timerfdops = {
+ .fo_read = timerfd_read,
+ .fo_write = invfo_rdwr,
+ .fo_truncate = invfo_truncate,
+ .fo_ioctl = timerfd_ioctl,
+ .fo_poll = timerfd_poll,
+ .fo_kqfilter = timerfd_kqfilter,
+ .fo_stat = timerfd_stat,
+ .fo_close = timerfd_close,
+ .fo_chmod = invfo_chmod,
+ .fo_chown = invfo_chown,
+ .fo_sendfile = invfo_sendfile,
+ .fo_fill_kinfo = timerfd_fill_kinfo,
+ .fo_flags = DFLAG_PASSABLE,
+};
+
+static void
+timerfd_curval(struct timerfd *tfd, struct itimerspec *old_value)
+{
+ struct timespec curr_value;
+
+ *old_value = tfd->tfd_time;
+ if (timespecisset(&tfd->tfd_time.it_value)) {
+ nanouptime(&curr_value);
+ timespecsub(&tfd->tfd_time.it_value, &curr_value,
+ &old_value->it_value);
+ }
+}
+
+static void
+timerfd_expire(void *arg)
+{
+ struct timerfd *tfd = (struct timerfd *)arg;
+ struct timespec uptime;
+
+ ++tfd->tfd_count;
+ tfd->tfd_expired = true;
+ if (timespecisset(&tfd->tfd_time.it_interval)) {
+ /* Count missed events. */
+ nanouptime(&uptime);
+ if (timespeccmp(&uptime, &tfd->tfd_time.it_value, >)) {
+ timespecsub(&uptime, &tfd->tfd_time.it_value, &uptime);
+ tfd->tfd_count += tstosbt(uptime) /
+ tstosbt(tfd->tfd_time.it_interval);
+ }
+ timespecadd(&tfd->tfd_time.it_value,
+ &tfd->tfd_time.it_interval, &tfd->tfd_time.it_value);
+ callout_schedule_sbt(&tfd->tfd_callout,
+ tstosbt(tfd->tfd_time.it_value),
+ 0, C_ABSOLUTE);
+ } else {
+ /* Single shot timer. */
+ callout_deactivate(&tfd->tfd_callout);
+ timespecclear(&tfd->tfd_time.it_value);
+ }
+
+ wakeup(&tfd->tfd_count);
+ selwakeup(&tfd->tfd_sel);
+ KNOTE_LOCKED(&tfd->tfd_sel.si_note, 0);
+}
+
+int
+kern_timerfd_create(struct thread *td, int clockid, int flags)
+{
+ struct file *fp;
+ struct timerfd *tfd;
+ int error, fd, fflags = 0;
+
+ AUDIT_ARG_VALUE(clockid);
+ AUDIT_ARG_FFLAGS(flags);
+
+ if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ return (EINVAL);
+ if ((flags & ~(TFD_CLOEXEC | TFD_NONBLOCK)) != 0)
+ return (EINVAL);
+ if ((flags & TFD_CLOEXEC) != 0)
+ fflags |= O_CLOEXEC;
+
+ tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO);
+ if (tfd == NULL)
+ return (ENOMEM);
+ tfd->tfd_clockid = (clockid_t)clockid;
+ tfd->tfd_flags = flags;
+ tfd->tfd_ino = alloc_unr64(&tfdino_unr);
+ mtx_init(&tfd->tfd_lock, "timerfd", NULL, MTX_DEF);
+ callout_init_mtx(&tfd->tfd_callout, &tfd->tfd_lock, 0);
+ knlist_init_mtx(&tfd->tfd_sel.si_note, &tfd->tfd_lock);
+ timerfd_getboottime(&tfd->tfd_boottim);
+ getnanotime(&tfd->tfd_birthtim);
+ LIST_INSERT_HEAD(&timerfd_head, tfd, entry);
+
+ error = falloc(td, &fp, &fd, fflags);
+ if (error != 0)
+ return (error);
+ fflags = FREAD;
+ if ((flags & TFD_NONBLOCK) != 0)
+ fflags |= FNONBLOCK;
+
+ finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops);
+ fdrop(fp, td);
+
+ td->td_retval[0] = fd;
+ return (0);
+}
+
+int
+kern_timerfd_gettime(struct thread *td, int fd, struct itimerspec *curr_value)
+{
+ struct file *fp;
+ struct timerfd *tfd;
+ int error;
+
+ error = fget(td, fd, &cap_write_rights, &fp);
+ if (error != 0)
+ return (error);
+ tfd = fp->f_data;
+ if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+
+ mtx_lock(&tfd->tfd_lock);
+ timerfd_curval(tfd, curr_value);
+ mtx_unlock(&tfd->tfd_lock);
+
+ fdrop(fp, td);
+ return (0);
+}
+
+int
+kern_timerfd_settime(struct thread *td, int fd, int flags,
+ const struct itimerspec *new_value, struct itimerspec *old_value)
+{
+ struct file *fp;
+ struct timerfd *tfd;
+ struct timespec ts;
+ int error = 0;
+
+ if ((flags & ~(TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)) != 0)
+ return (EINVAL);
+ if (!timespecvalid_interval(&new_value->it_value) ||
+ !timespecvalid_interval(&new_value->it_interval))
+ return (EINVAL);
+
+ error = fget(td, fd, &cap_write_rights, &fp);
+ if (error != 0)
+ return (error);
+ tfd = fp->f_data;
+ if (tfd == NULL || fp->f_type != DTYPE_TIMERFD) {
+ fdrop(fp, td);
+ return (EINVAL);
+ }
+
+ mtx_lock(&tfd->tfd_lock);
+ getnanotime(&tfd->tfd_mtim);
+ tfd->tfd_timflags = flags;
+
+ /* Store old itimerspec, if applicable. */
+ if (old_value != NULL)
+ timerfd_curval(tfd, old_value);
+
+ /* Set new expiration. */
+ tfd->tfd_time = *new_value;
+ if (timespecisset(&tfd->tfd_time.it_value)) {
+ if ((flags & TFD_TIMER_ABSTIME) == 0) {
+ nanouptime(&ts);
+ timespecadd(&tfd->tfd_time.it_value, &ts,
+ &tfd->tfd_time.it_value);
+ } else if (tfd->tfd_clockid == CLOCK_REALTIME) {
+ /* ECANCELED if unread jump is pending. */
+ if (tfd->tfd_jumped == TFD_CANCELED)
+ error = ECANCELED;
+ /* Convert from CLOCK_REALTIME to CLOCK_BOOTTIME. */
+ timespecsub(&tfd->tfd_time.it_value, &tfd->tfd_boottim,
+ &tfd->tfd_time.it_value);
+ }
+ callout_reset_sbt(&tfd->tfd_callout,
+ tstosbt(tfd->tfd_time.it_value),
+ 0, timerfd_expire, tfd, C_ABSOLUTE);
+ } else {
+ callout_stop(&tfd->tfd_callout);
+ }
+ tfd->tfd_count = 0;
+ tfd->tfd_expired = false;
+ tfd->tfd_jumped = TFD_NOJUMP;
+ mtx_unlock(&tfd->tfd_lock);
+
+ fdrop(fp, td);
+ return (error);
+}
+
+int
+sys_timerfd_create(struct thread *td, struct timerfd_create_args *uap)
+{
+ return (kern_timerfd_create(td, uap->clockid, uap->flags));
+}
+
+int
+sys_timerfd_gettime(struct thread *td, struct timerfd_gettime_args *uap)
+{
+ struct itimerspec curr_value;
+ int error;
+
+ error = kern_timerfd_gettime(td, uap->fd, &curr_value);
+ if (error == 0)
+ error = copyout(&curr_value, uap->curr_value,
+ sizeof(curr_value));
+
+ return (error);
+}
+
+int
+sys_timerfd_settime(struct thread *td, struct timerfd_settime_args *uap)
+{
+ struct itimerspec new_value, old_value;
+ int error;
+
+ error = copyin(uap->new_value, &new_value, sizeof(new_value));
+ if (error != 0)
+ return (error);
+ if (uap->old_value == NULL) {
+ error = kern_timerfd_settime(td, uap->fd, uap->flags,
+ &new_value, NULL);
+ } else {
+ error = kern_timerfd_settime(td, uap->fd, uap->flags,
+ &new_value, &old_value);
+ if (error == 0)
+ error = copyout(&old_value, uap->old_value,
+ sizeof(old_value));
+ }
+ return (error);
+}
+
+#ifdef COMPAT_FREEBSD32
+int
+freebsd32_timerfd_gettime(struct thread *td,
+ struct freebsd32_timerfd_gettime_args *uap)
+{
+ struct itimerspec curr_value;
+ struct itimerspec32 curr_value32;
+ int error;
+
+ error = kern_timerfd_gettime(td, uap->fd, &curr_value);
+ if (error == 0) {
+ CP(curr_value, curr_value32, it_value.tv_sec);
+ CP(curr_value, curr_value32, it_value.tv_nsec);
+ CP(curr_value, curr_value32, it_interval.tv_sec);
+ CP(curr_value, curr_value32, it_interval.tv_nsec);
+ error = copyout(&curr_value32, uap->curr_value,
+ sizeof(curr_value32));
+ }
+
+ return (error);
+}
+
+int
+freebsd32_timerfd_settime(struct thread *td,
+ struct freebsd32_timerfd_settime_args *uap)
+{
+ struct itimerspec new_value, old_value;
+ struct itimerspec32 new_value32, old_value32;
+ int error;
+
+ error = copyin(uap->new_value, &new_value32, sizeof(new_value32));
+ if (error != 0)
+ return (error);
+ CP(new_value32, new_value, it_value.tv_sec);
+ CP(new_value32, new_value, it_value.tv_nsec);
+ CP(new_value32, new_value, it_interval.tv_sec);
+ CP(new_value32, new_value, it_interval.tv_nsec);
+ if (uap->old_value == NULL) {
+ error = kern_timerfd_settime(td, uap->fd, uap->flags,
+ &new_value, NULL);
+ } else {
+ error = kern_timerfd_settime(td, uap->fd, uap->flags,
+ &new_value, &old_value);
+ if (error == 0) {
+ CP(old_value, old_value32, it_value.tv_sec);
+ CP(old_value, old_value32, it_value.tv_nsec);
+ CP(old_value, old_value32, it_interval.tv_sec);
+ CP(old_value, old_value32, it_interval.tv_nsec);
+ error = copyout(&old_value32, uap->old_value,
+ sizeof(old_value32));
+ }
+ }
+ return (error);
+}
+#endif
diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c
--- a/sys/kern/syscalls.c
+++ b/sys/kern/syscalls.c
@@ -590,4 +590,7 @@
"sched_getcpu", /* 581 = sched_getcpu */
"swapoff", /* 582 = swapoff */
"kqueuex", /* 583 = kqueuex */
+ "timerfd_create", /* 584 = timerfd_create */
+ "timerfd_gettime", /* 585 = timerfd_gettime */
+ "timerfd_settime", /* 586 = timerfd_settime */
};
diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master
--- a/sys/kern/syscalls.master
+++ b/sys/kern/syscalls.master
@@ -3313,5 +3313,25 @@
u_int flags
);
}
+584 AUE_TIMERFD STD|CAPENABLED {
+ int timerfd_create(
+ int clockid,
+ int flags
+ );
+ }
+585 AUE_TIMERFD STD|CAPENABLED {
+ int timerfd_gettime(
+ int fd,
+ _Out_ _Contains_long_timet_ struct itimerspec *curr_value
+ );
+ }
+586 AUE_TIMERFD STD|CAPENABLED {
+ int timerfd_settime(
+ int fd,
+ int flags,
+ _In_ _Contains_long_timet_ const struct itimerspec *new_value,
+ _Out_opt_ _Contains_long_timet_ struct itimerspec *old_value
+ );
+ }
; vim: syntax=off
diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c
--- a/sys/kern/systrace_args.c
+++ b/sys/kern/systrace_args.c
@@ -3424,6 +3424,32 @@
*n_args = 1;
break;
}
+ /* timerfd_create */
+ case 584: {
+ struct timerfd_create_args *p = params;
+ iarg[a++] = p->clockid; /* int */
+ iarg[a++] = p->flags; /* int */
+ *n_args = 2;
+ break;
+ }
+ /* timerfd_gettime */
+ case 585: {
+ struct timerfd_gettime_args *p = params;
+ iarg[a++] = p->fd; /* int */
+ uarg[a++] = (intptr_t)p->curr_value; /* struct itimerspec * */
+ *n_args = 2;
+ break;
+ }
+ /* timerfd_settime */
+ case 586: {
+ struct timerfd_settime_args *p = params;
+ iarg[a++] = p->fd; /* int */
+ iarg[a++] = p->flags; /* int */
+ uarg[a++] = (intptr_t)p->new_value; /* const struct itimerspec * */
+ uarg[a++] = (intptr_t)p->old_value; /* struct itimerspec * */
+ *n_args = 4;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9151,6 +9177,51 @@
break;
};
break;
+ /* timerfd_create */
+ case 584:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* timerfd_gettime */
+ case 585:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "userland struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
+ /* timerfd_settime */
+ case 586:
+ switch (ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "userland const struct itimerspec *";
+ break;
+ case 3:
+ p = "userland struct itimerspec *";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11109,6 +11180,21 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* timerfd_create */
+ case 584:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* timerfd_gettime */
+ case 585:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
+ /* timerfd_settime */
+ case 586:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
diff --git a/sys/sys/file.h b/sys/sys/file.h
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -70,7 +70,7 @@
#define DTYPE_DEV 11 /* Device specific fd type */
#define DTYPE_PROCDESC 12 /* process descriptor */
#define DTYPE_EVENTFD 13 /* eventfd */
-#define DTYPE_LINUXTFD 14 /* emulation timerfd type */
+#define DTYPE_TIMERFD 14 /* timerfd */
#ifdef _KERNEL
diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h
--- a/sys/sys/syscall.h
+++ b/sys/sys/syscall.h
@@ -521,4 +521,7 @@
#define SYS_sched_getcpu 581
#define SYS_swapoff 582
#define SYS_kqueuex 583
-#define SYS_MAXSYSCALL 584
+#define SYS_timerfd_create 584
+#define SYS_timerfd_gettime 585
+#define SYS_timerfd_settime 586
+#define SYS_MAXSYSCALL 587
diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk
--- a/sys/sys/syscall.mk
+++ b/sys/sys/syscall.mk
@@ -426,4 +426,7 @@
fspacectl.o \
sched_getcpu.o \
swapoff.o \
- kqueuex.o
+ kqueuex.o \
+ timerfd_create.o \
+ timerfd_gettime.o \
+ timerfd_settime.o
diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h
--- a/sys/sys/sysproto.h
+++ b/sys/sys/sysproto.h
@@ -1858,6 +1858,20 @@
struct kqueuex_args {
char flags_l_[PADL_(u_int)]; u_int flags; char flags_r_[PADR_(u_int)];
};
+struct timerfd_create_args {
+ char clockid_l_[PADL_(int)]; int clockid; char clockid_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
+struct timerfd_gettime_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char curr_value_l_[PADL_(struct itimerspec *)]; struct itimerspec * curr_value; char curr_value_r_[PADR_(struct itimerspec *)];
+};
+struct timerfd_settime_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+ char new_value_l_[PADL_(const struct itimerspec *)]; const struct itimerspec * new_value; char new_value_r_[PADR_(const struct itimerspec *)];
+ char old_value_l_[PADL_(struct itimerspec *)]; struct itimerspec * old_value; char old_value_r_[PADR_(struct itimerspec *)];
+};
int sys_exit(struct thread *, struct exit_args *);
int sys_fork(struct thread *, struct fork_args *);
int sys_read(struct thread *, struct read_args *);
@@ -2254,6 +2268,9 @@
int sys_sched_getcpu(struct thread *, struct sched_getcpu_args *);
int sys_swapoff(struct thread *, struct swapoff_args *);
int sys_kqueuex(struct thread *, struct kqueuex_args *);
+int sys_timerfd_create(struct thread *, struct timerfd_create_args *);
+int sys_timerfd_gettime(struct thread *, struct timerfd_gettime_args *);
+int sys_timerfd_settime(struct thread *, struct timerfd_settime_args *);
#ifdef COMPAT_43
@@ -3224,6 +3241,9 @@
#define SYS_AUE_sched_getcpu AUE_NULL
#define SYS_AUE_swapoff AUE_SWAPOFF
#define SYS_AUE_kqueuex AUE_KQUEUE
+#define SYS_AUE_timerfd_create AUE_TIMERFD
+#define SYS_AUE_timerfd_gettime AUE_TIMERFD
+#define SYS_AUE_timerfd_settime AUE_TIMERFD
#undef PAD_
#undef PADL_
diff --git a/sys/sys/timerfd.h b/sys/sys/timerfd.h
new file mode 100644
--- /dev/null
+++ b/sys/sys/timerfd.h
@@ -0,0 +1,66 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2023 Jake Freeland <jfree@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _SYS_TIMERFD_H_
+#define _SYS_TIMERFD_H_
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/proc.h>
+#include <sys/timespec.h>
+
+typedef uint64_t timerfd_t;
+
+/* Creation flags. */
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC O_CLOEXEC
+
+/* Timer flags. */
+#define TFD_TIMER_ABSTIME 0x01
+#define TFD_TIMER_CANCEL_ON_SET 0x02
+
+#ifndef _KERNEL
+
+__BEGIN_DECLS
+int timerfd_create(int clockid, int flags);
+int timerfd_gettime(int fd, struct itimerspec *curr_value);
+int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
+ struct itimerspec *old_value);
+__END_DECLS
+
+#else /* _KERNEL */
+
+int kern_timerfd_create(struct thread *td, int clockid, int flags);
+int kern_timerfd_gettime(struct thread *td, int fd,
+ struct itimerspec *curr_value);
+int kern_timerfd_settime(struct thread *td, int fd, int flags,
+ const struct itimerspec *new_value, struct itimerspec *old_value);
+void timerfd_jumped(void);
+
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_TIMERFD_H_ */
diff --git a/sys/sys/user.h b/sys/sys/user.h
--- a/sys/sys/user.h
+++ b/sys/sys/user.h
@@ -265,6 +265,7 @@
#define KF_TYPE_PROCDESC 11
#define KF_TYPE_DEV 12
#define KF_TYPE_EVENTFD 13
+#define KF_TYPE_TIMERFD 14
#define KF_TYPE_UNKNOWN 255
#define KF_VTYPE_VNON 0
@@ -446,6 +447,11 @@
uint32_t kf_eventfd_spareint[3];
uint64_t kf_eventfd_addr;
} kf_eventfd;
+ struct {
+ uint32_t kf_timerfd_clockid;
+ uint32_t kf_timerfd_flags;
+ uint64_t kf_timerfd_addr;
+ } kf_timerfd;
struct {
uint64_t kf_kqueue_addr;
int32_t kf_kqueue_count;

File Metadata

Mime Type
text/plain
Expires
Thu, Feb 27, 6:13 PM (20 h, 31 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16872253
Default Alt Text
D38459.diff (48 KB)

Event Timeline