Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/thorpej-futex]: src/sys Native implementation of the Linux timerfd API.



details:   https://anonhg.NetBSD.org/src/rev/a12e0a746e7a
branches:  thorpej-futex
changeset: 947496:a12e0a746e7a
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Mon Dec 14 16:53:36 2020 +0000

description:
Native implementation of the Linux timerfd API.

diffstat:

 sys/kern/files.kern      |    3 +-
 sys/kern/sys_timerfd.c   |  689 +++++++++++++++++++++++++++++++++++++++++++++++
 sys/kern/syscalls.master |   12 +-
 sys/sys/Makefile         |    4 +-
 sys/sys/file.h           |    7 +-
 sys/sys/timerfd.h        |   65 ++++
 6 files changed, 771 insertions(+), 9 deletions(-)

diffs (truncated from 861 to 300 lines):

diff -r f4232c43417c -r a12e0a746e7a sys/kern/files.kern
--- a/sys/kern/files.kern       Mon Dec 14 16:01:38 2020 +0000
+++ b/sys/kern/files.kern       Mon Dec 14 16:53:36 2020 +0000
@@ -1,4 +1,4 @@
-#      $NetBSD: files.kern,v 1.53.2.2 2020/12/14 16:00:51 thorpej Exp $
+#      $NetBSD: files.kern,v 1.53.2.3 2020/12/14 16:53:36 thorpej Exp $
 
 #
 # kernel sources
@@ -174,6 +174,7 @@
 file   kern/sys_sig.c                  kern
 file   kern/sys_sched.c                kern
 file   kern/sys_socket.c               kern
+file   kern/sys_timerfd.c              kern
 file   kern/syscalls.c                 syscall_debug | kdtrace_hooks
 file   kern/sysv_ipc.c                 sysvshm | sysvsem | sysvmsg
 file   kern/sysv_msg.c                 sysvmsg
diff -r f4232c43417c -r a12e0a746e7a sys/kern/sys_timerfd.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/kern/sys_timerfd.c    Mon Dec 14 16:53:36 2020 +0000
@@ -0,0 +1,689 @@
+/*     $NetBSD: sys_timerfd.c,v 1.1.2.1 2020/12/14 16:53:36 thorpej Exp $      */
+
+/*-
+ * Copyright (c) 2020 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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>
+__KERNEL_RCSID(0, "$NetBSD: sys_timerfd.c,v 1.1.2.1 2020/12/14 16:53:36 thorpej Exp $");
+
+/*
+ * timerfd
+ *
+ * Timerfd objects are similar to POSIX timers, except they are associated
+ * with a file descriptor rather than a process.  Timerfd objects are
+ * created with the timerfd_create(2) system call, similar to timer_create(2).
+ * The timerfd analogues for timer_gettime(2) and timer_settime(2) are
+ * timerfd_gettime(2) and timerfd_settime(2), respectively.
+ *
+ * When a timerfd object's timer fires, an internal counter is incremented.
+ * When this timer is non-zero, the descriptor associated with the timerfd
+ * object is "readable".  Note that this is slightly different than the
+ * POSIX timer "overrun" counter, which only increments if the timer fires
+ * again while the notification signal is already pending.  Thus, we are
+ * responsible for incrementing the "overrun" counter each time the timerfd
+ * timer fires.
+ *
+ * This implementation is API compatible with the Linux timerfd interface.
+ */
+
+#include <sys/types.h>
+#include <sys/condvar.h>
+#include <sys/file.h>
+#include <sys/filedesc.h>
+#include <sys/kauth.h>
+#include <sys/mutex.h>
+#include <sys/poll.h>
+#include <sys/proc.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/syscallargs.h>
+#include <sys/timerfd.h>
+#include <sys/uio.h>
+
+/* N.B. all timerfd state is protected by itimer_lock() */
+struct timerfd {
+       struct itimer   tfd_itimer;
+       kcondvar_t      tfd_read_wait;
+       kcondvar_t      tfd_restart_wait;
+       struct selinfo  tfd_read_sel;
+       int64_t         tfd_nwaiters;
+       bool            tfd_cancel_on_set;
+       bool            tfd_cancelled;
+       bool            tfd_restarting;
+
+       /*
+        * Information kept for stat(2).
+        */
+       struct timespec tfd_btime;      /* time created */
+       struct timespec tfd_mtime;      /* last timerfd_settime() */
+       struct timespec tfd_atime;      /* last read */
+};
+
+static void    timerfd_wake(struct timerfd *);
+
+static inline uint64_t
+timerfd_fire_count(const struct timerfd * const tfd)
+{
+       return (unsigned int)tfd->tfd_itimer.it_overruns;
+}
+
+static inline bool
+timerfd_is_readable(const struct timerfd * const tfd)
+{
+       return tfd->tfd_itimer.it_overruns != 0 || tfd->tfd_cancelled;
+}
+
+/*
+ * timerfd_fire:
+ *
+ *     Called when the timerfd's timer fires.
+ *
+ *     Called from a callout with itimer lock held.
+ */
+static void
+timerfd_fire(struct itimer * const it)
+{
+       struct timerfd * const tfd =
+           container_of(it, struct timerfd, tfd_itimer);
+
+       it->it_overruns++;
+       timerfd_wake(tfd);
+}
+
+/*
+ * timerfd_realtime_changed:
+ *
+ *     Called when CLOCK_REALTIME is changed with clock_settime()
+ *     or settimeofday().
+ *
+ *     Called with itimer lock held.
+ */
+static void
+timerfd_realtime_changed(struct itimer * const it)
+{
+       struct timerfd * const tfd =
+           container_of(it, struct timerfd, tfd_itimer);
+
+       /* Should only be called when timer is armed. */
+       KASSERT(timespecisset(&it->it_time.it_value));
+
+       if (tfd->tfd_cancel_on_set) {
+               tfd->tfd_cancelled = true;
+               timerfd_wake(tfd);
+       }
+}
+
+static const struct itimer_ops timerfd_itimer_monotonic_ops = {
+       .ito_fire = timerfd_fire,
+};
+
+static const struct itimer_ops timerfd_itimer_realtime_ops = {
+       .ito_fire = timerfd_fire,
+       .ito_realtime_changed = timerfd_realtime_changed,
+};
+
+/*
+ * timerfd_create:
+ *
+ *     Create a timerfd object.
+ */
+static struct timerfd *
+timerfd_create(clockid_t const clock_id, int const flags)
+{
+       struct timerfd * const tfd = kmem_zalloc(sizeof(*tfd), KM_SLEEP);
+
+       KASSERT(clock_id == CLOCK_REALTIME || clock_id == CLOCK_MONOTONIC);
+
+       cv_init(&tfd->tfd_read_wait, "tfdread");
+       cv_init(&tfd->tfd_restart_wait, "tfdrstrt");
+       selinit(&tfd->tfd_read_sel);
+       getnanotime(&tfd->tfd_btime);
+
+       /* Caller deals with TFD_CLOEXEC and TFD_NONBLOCK. */
+
+       itimer_lock();
+       itimer_init(&tfd->tfd_itimer,
+           clock_id == CLOCK_REALTIME ? &timerfd_itimer_realtime_ops
+                                      : &timerfd_itimer_monotonic_ops,
+           clock_id, NULL);
+       itimer_unlock();
+
+       return tfd;
+}
+
+/*
+ * timerfd_destroy:
+ *
+ *     Destroy a timerfd object.
+ */
+static void
+timerfd_destroy(struct timerfd * const tfd)
+{
+
+       KASSERT(tfd->tfd_nwaiters == 0);
+       KASSERT(tfd->tfd_restarting == false);
+
+       itimer_lock();
+       itimer_poison(&tfd->tfd_itimer);
+       itimer_fini(&tfd->tfd_itimer);  /* drops itimer lock */
+
+       cv_destroy(&tfd->tfd_read_wait);
+       cv_destroy(&tfd->tfd_restart_wait);
+
+       seldestroy(&tfd->tfd_read_sel);
+
+       kmem_free(tfd, sizeof(*tfd));
+}
+
+/*
+ * timerfd_wait:
+ *
+ *     Block on a timerfd.  Handles non-blocking, as well as
+ *     the restart cases.
+ */
+static int
+timerfd_wait(struct timerfd * const tfd, int const fflag)
+{
+       int error;
+
+       if (fflag & FNONBLOCK) {
+               return EAGAIN;
+       }
+
+       /*
+        * We're going to block.  If there is a restart in-progress,
+        * wait for that to complete first.
+        */
+       while (tfd->tfd_restarting) {
+               cv_wait(&tfd->tfd_restart_wait, &itimer_mutex);
+       }
+
+       tfd->tfd_nwaiters++;
+       KASSERT(tfd->tfd_nwaiters > 0);
+       error = cv_wait_sig(&tfd->tfd_read_wait, &itimer_mutex);
+       tfd->tfd_nwaiters--;
+       KASSERT(tfd->tfd_nwaiters >= 0);
+
+       /*
+        * If a restart was triggered while we were asleep, we need
+        * to return ERESTART if no other error was returned.  If we
+        * are the last waiter coming out of the restart drain, clear
+        * the condition.
+        */
+       if (tfd->tfd_restarting) {
+               if (error == 0) {
+                       error = ERESTART;
+               }
+               if (tfd->tfd_nwaiters == 0) {
+                       tfd->tfd_restarting = false;
+                       cv_broadcast(&tfd->tfd_restart_wait);
+               }
+       }
+
+       return error;
+}
+
+/*
+ * timerfd_wake:
+ *
+ *     Wake LWPs blocked on a timerfd.
+ */
+static void
+timerfd_wake(struct timerfd * const tfd)
+{
+
+       if (tfd->tfd_nwaiters) {
+               cv_broadcast(&tfd->tfd_read_wait);
+       }
+       selnotify(&tfd->tfd_read_sel, POLLIN | POLLRDNORM, NOTE_SUBMIT);
+}
+
+/*
+ * timerfd file operations
+ */
+
+static int
+timerfd_fop_read(file_t * const fp, off_t * const offset,
+    struct uio * const uio, kauth_cred_t const cred, int const flags)
+{
+       struct timerfd * const tfd = fp->f_timerfd;
+       struct itimer * const it = &tfd->tfd_itimer;
+       int const fflag = fp->f_flag;
+       uint64_t return_value;
+       int error;



Home | Main Index | Thread Index | Old Index