Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111051961
D38459.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
48 KB
Referenced Files
None
Subscribers
None
D38459.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D38459: timerfd: Add native support for Linux's timerfd
Attached
Detach File
Event Timeline
Log In to Comment