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/16f706ada170
branches: thorpej-futex
changeset: 982885:16f706ada170
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 fb1d498d105f -r 16f706ada170 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 fb1d498d105f -r 16f706ada170 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