Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111063201
D38459.id116829.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
34 KB
Referenced Files
None
Subscribers
None
D38459.id116829.diff
View Options
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-specific. */
/*
* Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the
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
@@ -55,6 +55,7 @@
#include <sys/syscallsubr.h>
#include <sys/timespec.h>
#include <sys/eventfd.h>
+#include <sys/timerfd.h>
#ifdef COMPAT_LINUX32
#include <machine/../linux32/linux.h>
@@ -108,55 +109,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)
{
@@ -637,7 +589,7 @@
bzero(&ae, sizeof(ae));
ae.initval = args->initval;
- return (kern_specialfd(td, SPECIALFD_EVENTFD, &ae));
+ return (kern_eventfd(td, &ae));
}
#endif
@@ -661,261 +613,20 @@
bzero(&ae, sizeof(ae));
ae.flags = flags;
ae.initval = args->initval;
- return (kern_specialfd(td, SPECIALFD_EVENTFD, &ae));
+ return (kern_eventfd(td, &ae));
}
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 (timerfd_create_file(td, clockid, args->flags));
}
int
@@ -925,7 +636,7 @@
struct itimerspec ots;
int error;
- error = linux_timerfd_gettime_common(td, args->fd, &ots);
+ error = timerfd_gettime_common(td, args->fd, &ots);
if (error != 0)
return (error);
error = native_to_linux_itimerspec(&lots, &ots);
@@ -942,7 +653,7 @@
struct itimerspec ots;
int error;
- error = linux_timerfd_gettime_common(td, args->fd, &ots);
+ error = timerfd_gettime_common(td, args->fd, &ots);
if (error != 0)
return (error);
error = native_to_linux_itimerspec64(&lots, &ots);
@@ -952,60 +663,6 @@
}
#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);
-}
-
int
linux_timerfd_settime(struct thread *td, struct linux_timerfd_settime_args *args)
{
@@ -1020,8 +677,7 @@
if (error != 0)
return (error);
pots = (args->old_value != NULL ? &ots : NULL);
- error = linux_timerfd_settime_common(td, args->fd, args->flags,
- &nts, pots);
+ error = timerfd_settime_common(td, args->fd, args->flags, &nts, pots);
if (error == 0 && args->old_value != NULL) {
error = native_to_linux_itimerspec(&lots, &ots);
if (error == 0)
@@ -1045,8 +701,7 @@
if (error != 0)
return (error);
pots = (args->old_value != NULL ? &ots : NULL);
- error = linux_timerfd_settime_common(td, args->fd, args->flags,
- &nts, pots);
+ error = timerfd_settime_common(td, args->fd, args->flags, &nts, pots);
if (error == 0 && args->old_value != NULL) {
error = native_to_linux_itimerspec64(&lots, &ots);
if (error == 0)
@@ -1055,39 +710,3 @@
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
@@ -3941,6 +3941,7 @@
kern/subr_witness.c optional witness
kern/sys_capability.c standard
kern/sys_eventfd.c standard
+kern/sys_timerfd.c standard
kern/sys_generic.c standard
kern/sys_getrandom.c standard
kern/sys_pipe.c standard
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/sys_eventfd.c b/sys/kern/sys_eventfd.c
--- a/sys/kern/sys_eventfd.c
+++ b/sys/kern/sys_eventfd.c
@@ -106,15 +106,24 @@
};
int
-eventfd_create_file(struct thread *td, struct file *fp, uint32_t initval,
- int flags)
+eventfd_create_file(struct thread *td, uint32_t initval, int flags)
{
struct eventfd *efd;
- int fflags;
+ struct file *fp;
+ int error, fd, fflags = 0;
AUDIT_ARG_FFLAGS(flags);
AUDIT_ARG_VALUE(initval);
+ if ((flags & ~(EFD_CLOEXEC | EFD_NONBLOCK | EFD_SEMAPHORE)) != 0)
+ return (EINVAL);
+ if ((flags & EFD_CLOEXEC) != 0)
+ fflags |= O_CLOEXEC;
+
+ error = falloc_noinstall(td, &fp);
+ if (error != 0)
+ return (error);
+
efd = malloc(sizeof(*efd), M_EVENTFD, M_WAITOK | M_ZERO);
efd->efd_flags = flags;
efd->efd_count = initval;
@@ -124,8 +133,13 @@
fflags = FREAD | FWRITE;
if ((flags & EFD_NONBLOCK) != 0)
fflags |= FNONBLOCK;
+
finit(fp, fflags, DTYPE_EVENTFD, efd, &eventfdops);
+ error = finstall(td, fp, &fd, fflags, NULL);
+ if (error == 0)
+ td->td_retval[0] = fd;
+ fdrop(fp, td);
return (0);
}
diff --git a/sys/kern/sys_generic.c b/sys/kern/sys_generic.c
--- a/sys/kern/sys_generic.c
+++ b/sys/kern/sys_generic.c
@@ -56,6 +56,7 @@
#include <sys/socketvar.h>
#include <sys/uio.h>
#include <sys/eventfd.h>
+#include <sys/timerfd.h>
#include <sys/kernel.h>
#include <sys/ktr.h>
#include <sys/limits.h>
@@ -936,64 +937,99 @@
}
int
-kern_specialfd(struct thread *td, int type, void *arg)
+kern_eventfd(struct thread *td, const void *args)
{
- struct file *fp;
- struct specialfd_eventfd *ae;
- int error, fd, fflags;
+ struct specialfd_eventfd efd;
+ int error;
- fflags = 0;
- error = falloc_noinstall(td, &fp);
+ error = copyin(args, &efd, sizeof(efd));
if (error != 0)
return (error);
- switch (type) {
- case SPECIALFD_EVENTFD:
- ae = arg;
- if ((ae->flags & EFD_CLOEXEC) != 0)
- fflags |= O_CLOEXEC;
- error = eventfd_create_file(td, fp, ae->initval, ae->flags);
- break;
- default:
- error = EINVAL;
- break;
- }
+ return (eventfd_create_file(td, efd.initval, efd.flags));
+}
+int
+kern_timerfd_create(struct thread *td, const void *args)
+{
+ struct specialfd_timerfd_create tfd;
+ int error;
+
+ error = copyin(args, &tfd, sizeof(tfd));
+ if (error != 0)
+ return (error);
+
+ return (timerfd_create_file(td, tfd.clockid, tfd.flags));
+}
+
+int
+kern_timerfd_gettime(struct thread *td, const void *args)
+{
+ struct specialfd_timerfd_gettime tfd;
+ struct itimerspec cts;
+ int error;
+
+ error = copyin(args, &tfd, sizeof(tfd));
+ if (error != 0)
+ return (error);
+
+ error = timerfd_gettime_common(td, tfd.fd, &cts);
if (error == 0)
- error = finstall(td, fp, &fd, fflags, NULL);
- fdrop(fp, td);
- if (error == 0)
- td->td_retval[0] = fd;
+ error = copyout(&cts, tfd.curr_value, sizeof(cts));
+
return (error);
}
int
-sys___specialfd(struct thread *td, struct __specialfd_args *args)
+kern_timerfd_settime(struct thread *td, const void *args)
{
- struct specialfd_eventfd ae;
+ struct specialfd_timerfd_settime tfd;
+ struct itimerspec nts, ots, *pots;
int error;
+ error = copyin(args, &tfd, sizeof(tfd));
+ if (error != 0)
+ return (error);
+ error = copyin(tfd.new_value, &nts, sizeof(nts));
+ if (error != 0)
+ return (error);
+ pots = (tfd.old_value != NULL ? &ots : NULL);
+ error = timerfd_settime_common(td, tfd.fd, tfd.flags, &nts, pots);
+ if (error == 0 && tfd.old_value != NULL)
+ error = copyout(&ots, tfd.old_value, sizeof(ots));
+
+ return (error);
+}
+
+int
+sys___specialfd(struct thread *td, struct __specialfd_args *args)
+{
+ size_t arg_size;
+ int (*specialfd_func)(struct thread *, const void *);
+
switch (args->type) {
case SPECIALFD_EVENTFD:
- if (args->len != sizeof(struct specialfd_eventfd)) {
- error = EINVAL;
- break;
- }
- error = copyin(args->req, &ae, sizeof(ae));
- if (error != 0)
- break;
- if ((ae.flags & ~(EFD_CLOEXEC | EFD_NONBLOCK |
- EFD_SEMAPHORE)) != 0) {
- error = EINVAL;
- break;
- }
- error = kern_specialfd(td, args->type, &ae);
+ arg_size = sizeof(struct specialfd_eventfd);
+ specialfd_func = kern_eventfd;
break;
- default:
- error = EINVAL;
+ case SPECIALFD_TIMERFD_CREATE:
+ arg_size = sizeof(struct specialfd_timerfd_create);
+ specialfd_func = kern_timerfd_create;
+ break;
+ case SPECIALFD_TIMERFD_SETTIME:
+ arg_size = sizeof(struct specialfd_timerfd_settime);
+ specialfd_func = kern_timerfd_settime;
+ break;
+ case SPECIALFD_TIMERFD_GETTIME:
+ arg_size = sizeof(struct specialfd_timerfd_gettime);
+ specialfd_func = kern_timerfd_gettime;
break;
}
- return (error);
+
+ if (args->len != arg_size)
+ return (EINVAL);
+
+ return (specialfd_func(td, args->req));
}
int
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,449 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2007 Roman Divacky
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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/selinfo.h>
+#include <sys/timerfd.h>
+#include <sys/timespec.h>
+#include <sys/uio.h>
+#include <sys/user.h>
+
+#include <security/audit/audit.h>
+
+MALLOC_DEFINE(M_TIMERFD, "timerfd", "timerfd structures");
+
+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;
+ int tfd_flags;
+};
+
+static void timerfd_expire(void *);
+static void timerfd_curval(struct timerfd *, struct itimerspec *);
+
+int
+timerfd_create_file(struct thread *td, int clockid, int flags)
+{
+ struct timerfd *tfd;
+ struct file *fp;
+ int error, fd, fflags = 0;
+
+ if (clockid != CLOCK_REALTIME && clockid != CLOCK_MONOTONIC)
+ return (EINVAL);
+ if ((flags & ~TFD_CREATE_FLAGS) != 0)
+ return (EINVAL);
+ if ((flags & TFD_CLOEXEC) != 0)
+ fflags |= O_CLOEXEC;
+
+ error = falloc_noinstall(td, &fp);
+ if (error != 0)
+ return (error);
+
+ AUDIT_ARG_FFLAGS(flags);
+ AUDIT_ARG_VALUE(clockid);
+
+ tfd = malloc(sizeof(*tfd), M_TIMERFD, M_WAITOK | M_ZERO);
+ tfd->tfd_clockid = (clockid_t) 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 ((flags & O_NONBLOCK) != 0)
+ fflags |= FNONBLOCK;
+
+ finit(fp, fflags, DTYPE_TIMERFD, tfd, &timerfdops);
+ error = finstall(td, fp, &fd, fflags, NULL);
+ if (error == 0)
+ td->td_retval[0] = fd;
+
+ fdrop(fp, td);
+ return (0);
+}
+
+static int
+timerfd_close(struct file *fp, struct thread *td)
+{
+ struct timerfd *tfd;
+
+ tfd = fp->f_data;
+ if (fp->f_type != DTYPE_TIMERFD || 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_TIMERFD);
+
+ 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_TIMERFD || 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_TIMERFD || 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_TIMERFD || 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_TIMERFD)
+ 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)
+{
+
+ 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 void
+timerfd_clocktime(struct timerfd *tfd, struct timespec *ts)
+{
+
+ if (tfd->tfd_clockid == CLOCK_REALTIME)
+ getnanotime(ts);
+ else /* CLOCK_MONOTONIC */
+ getnanouptime(ts);
+}
+
+static void
+timerfd_curval(struct timerfd *tfd, struct itimerspec *ots)
+{
+ struct timespec cts;
+
+ 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;
+ }
+ }
+}
+
+int
+timerfd_gettime_common(struct thread *td, int fd, struct itimerspec *cts)
+{
+ struct timerfd *tfd;
+ struct file *fp;
+ int error;
+
+ error = fget(td, fd, &cap_read_rights, &fp);
+ if (error != 0)
+ return (error);
+ tfd = fp->f_data;
+ if (fp->f_type != DTYPE_TIMERFD || tfd == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ mtx_lock(&tfd->tfd_lock);
+ timerfd_curval(tfd, cts);
+ mtx_unlock(&tfd->tfd_lock);
+
+out:
+ fdrop(fp, td);
+ return (error);
+}
+
+int
+timerfd_settime_common(struct thread *td, int fd, int flags,
+ struct itimerspec *nts, struct itimerspec *ots)
+{
+ struct timespec cts, ts;
+ struct timerfd *tfd;
+ struct timeval tv;
+ struct file *fp;
+ int error;
+
+ if ((flags & ~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_TIMERFD || tfd == NULL) {
+ error = EINVAL;
+ goto out;
+ }
+
+ mtx_lock(&tfd->tfd_lock);
+ if (!timespecisset(&nts->it_value))
+ timespecclear(&nts->it_interval);
+ if (ots != NULL)
+ timerfd_curval(tfd, ots);
+
+ bcopy(nts, &tfd->tfd_time, sizeof(*nts));
+ tfd->tfd_count = 0;
+ if (timespecisset(&nts->it_value)) {
+ timerfd_clocktime(tfd, &cts);
+ ts = nts->it_value;
+ if ((flags & 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),
+ 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);
+}
+
+static void
+timerfd_expire(void *arg)
+{
+ struct timespec cts, ts;
+ struct timeval tv;
+ struct timerfd *tfd;
+
+ tfd = (struct timerfd *)arg;
+
+ 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),
+ 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),
+ timerfd_expire, tfd);
+ }
+}
diff --git a/sys/sys/eventfd.h b/sys/sys/eventfd.h
--- a/sys/sys/eventfd.h
+++ b/sys/sys/eventfd.h
@@ -38,8 +38,7 @@
#ifdef _KERNEL
-int eventfd_create_file(struct thread *td, struct file *fp, uint32_t initval,
- int flags);
+int eventfd_create_file(struct thread *td, uint32_t initval, int flags);
#else
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/specialfd.h b/sys/sys/specialfd.h
--- a/sys/sys/specialfd.h
+++ b/sys/sys/specialfd.h
@@ -32,6 +32,9 @@
enum specialfd_type {
SPECIALFD_EVENTFD = 1,
+ SPECIALFD_TIMERFD_CREATE = 2,
+ SPECIALFD_TIMERFD_SETTIME = 3,
+ SPECIALFD_TIMERFD_GETTIME = 4
};
struct specialfd_eventfd {
@@ -39,4 +42,21 @@
int flags;
};
+struct specialfd_timerfd_create {
+ int clockid;
+ int flags;
+};
+
+struct specialfd_timerfd_gettime {
+ int fd;
+ struct itimerspec *curr_value;
+};
+
+struct specialfd_timerfd_settime {
+ int fd;
+ int flags;
+ const struct itimerspec *new_value;
+ struct itimerspec *old_value;
+};
+
#endif /* !_SYS_SPECIALFD_H_ */
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -321,7 +321,10 @@
int kern_statat(struct thread *td, int flag, int fd, const char *path,
enum uio_seg pathseg, struct stat *sbp,
void (*hook)(struct vnode *vp, struct stat *sbp));
-int kern_specialfd(struct thread *td, int type, void *arg);
+int kern_eventfd(struct thread *td, const void *args);
+int kern_timerfd_create(struct thread *td, const void *args);
+int kern_timerfd_settime(struct thread *td, const void *args);
+int kern_timerfd_gettime(struct thread *td, const void *args);
int kern_statfs(struct thread *td, const char *path, enum uio_seg pathseg,
struct statfs *buf);
int kern_symlinkat(struct thread *td, const char *path1, int fd,
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,68 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * 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/malloc.h>
+#include <sys/timespec.h>
+
+typedef uint64_t timerfd_t;
+
+#define TFD_NONBLOCK O_NONBLOCK
+#define TFD_CLOEXEC O_CLOEXEC
+
+#define TFD_TIMER_ABSTIME (1 << 0)
+#define TFD_TIMER_CANCEL_ON_SET (1 << 1)
+
+#define TFD_SHARED_FCNTL_FLAGS (TFD_NONBLOCK | TFD_CLOEXEC)
+#define TFD_CREATE_FLAGS TFD_SHARED_FCNTL_FLAGS
+#define TFD_SETTIME_FLAGS (TFD_TIMER_ABSTIME | TFD_TIMER_CANCEL_ON_SET)
+
+#ifdef _KERNEL
+
+MALLOC_DECLARE(M_TIMERFD);
+
+int timerfd_create_file(struct thread *td, int clockid, int flags);
+int timerfd_gettime_common(struct thread *td, int fd, struct itimerspec *cts);
+int timerfd_settime_common(struct thread *td, int fd, int flags,
+ struct itimerspec *nts, struct itimerspec *ots);
+
+#else
+
+__BEGIN_DECLS
+int timerfd_create(int clockid, int flags);
+int timerfd_settime(int fd, int flags, const struct itimerspec *new_value,
+ struct itimerspec *old_value);
+int timerfd_gettime(int fd, struct itimerspec *curr_value);
+__END_DECLS
+
+#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
@@ -444,6 +445,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, 10:02 PM (17 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16874835
Default Alt Text
D38459.id116829.diff (34 KB)
Attached To
Mode
D38459: timerfd: Add native support for Linux's timerfd
Attached
Detach File
Event Timeline
Log In to Comment