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 eventfd(2) API.
details: https://anonhg.NetBSD.org/src/rev/5421f0424673
branches: thorpej-futex
changeset: 1024917:5421f0424673
user: thorpej <thorpej%NetBSD.org@localhost>
date: Mon Dec 14 16:00:51 2020 +0000
description:
Native implementation of the Linux eventfd(2) API.
diffstat:
sys/kern/files.kern | 3 +-
sys/kern/sys_eventfd.c | 583 +++++++++++++++++++++++++++++++++++++++++++++++
sys/kern/syscalls.master | 4 +-
sys/sys/Makefile | 4 +-
sys/sys/eventfd.h | 57 ++++
sys/sys/file.h | 7 +-
6 files changed, 651 insertions(+), 7 deletions(-)
diffs (truncated from 739 to 300 lines):
diff -r 177c581c3ba7 -r 5421f0424673 sys/kern/files.kern
--- a/sys/kern/files.kern Mon Dec 14 14:37:44 2020 +0000
+++ b/sys/kern/files.kern Mon Dec 14 16:00:51 2020 +0000
@@ -1,4 +1,4 @@
-# $NetBSD: files.kern,v 1.53.2.1 2020/12/14 14:38:13 thorpej Exp $
+# $NetBSD: files.kern,v 1.53.2.2 2020/12/14 16:00:51 thorpej Exp $
#
# kernel sources
@@ -157,6 +157,7 @@
file kern/subr_xcall.c kern
file kern/sys_aio.c aio
file kern/sys_descrip.c kern
+file kern/sys_eventfd.c kern
file kern/sys_futex.c kern
file kern/sys_generic.c kern
file kern/sys_getrandom.c kern
diff -r 177c581c3ba7 -r 5421f0424673 sys/kern/sys_eventfd.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/kern/sys_eventfd.c Mon Dec 14 16:00:51 2020 +0000
@@ -0,0 +1,583 @@
+/* $NetBSD: sys_eventfd.c,v 1.1.2.1 2020/12/14 16:00:51 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_eventfd.c,v 1.1.2.1 2020/12/14 16:00:51 thorpej Exp $");
+
+/*
+ * eventfd
+ *
+ * Eventfd objects present a simple counting object associated with a
+ * file descriptor. Writes and reads to this file descriptor increment
+ * and decrement the count, respectively. When the count is non-zero,
+ * the descriptor is considered "readable", and when less than the max
+ * value (EVENTFD_MAXVAL), is considered "writable".
+ *
+ * This implementation is API compatible with the Linux eventfd(2)
+ * interface.
+ */
+
+#include <sys/types.h>
+#include <sys/condvar.h>
+#include <sys/eventfd.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/uio.h>
+
+struct eventfd {
+ kmutex_t efd_lock;
+ kcondvar_t efd_read_wait;
+ kcondvar_t efd_write_wait;
+ kcondvar_t efd_restart_wait;
+ struct selinfo efd_read_sel;
+ struct selinfo efd_write_sel;
+ eventfd_t efd_val;
+ int64_t efd_nwaiters;
+ bool efd_restarting;
+ bool efd_has_read_waiters;
+ bool efd_has_write_waiters;
+ bool efd_is_semaphore;
+
+ /*
+ * Information kept for stat(2).
+ */
+ struct timespec efd_btime; /* time created */
+ struct timespec efd_mtime; /* last write */
+ struct timespec efd_atime; /* last read */
+};
+
+#define EVENTFD_MAXVAL (UINT64_MAX - 1)
+
+/*
+ * eventfd_create:
+ *
+ * Create an eventfd object.
+ */
+static struct eventfd *
+eventfd_create(unsigned int const val, int const flags)
+{
+ struct eventfd * const efd = kmem_zalloc(sizeof(*efd), KM_SLEEP);
+
+ mutex_init(&efd->efd_lock, MUTEX_DEFAULT, IPL_NONE);
+ cv_init(&efd->efd_read_wait, "efdread");
+ cv_init(&efd->efd_write_wait, "efdwrite");
+ cv_init(&efd->efd_restart_wait, "efdrstrt");
+ selinit(&efd->efd_read_sel);
+ selinit(&efd->efd_write_sel);
+ efd->efd_val = val;
+ efd->efd_is_semaphore = !!(flags & EFD_SEMAPHORE);
+ getnanotime(&efd->efd_btime);
+
+ /* Caller deals with EFD_CLOEXEC and EFD_NONBLOCK. */
+
+ return efd;
+}
+
+/*
+ * eventfd_destroy:
+ *
+ * Destroy an eventfd object.
+ */
+static void
+eventfd_destroy(struct eventfd * const efd)
+{
+
+ KASSERT(efd->efd_nwaiters == 0);
+ KASSERT(efd->efd_restarting == false);
+ KASSERT(efd->efd_has_read_waiters == false);
+ KASSERT(efd->efd_has_write_waiters == false);
+
+ cv_destroy(&efd->efd_read_wait);
+ cv_destroy(&efd->efd_write_wait);
+ cv_destroy(&efd->efd_restart_wait);
+
+ seldestroy(&efd->efd_read_sel);
+ seldestroy(&efd->efd_write_sel);
+
+ mutex_destroy(&efd->efd_lock);
+}
+
+/*
+ * eventfd_wait:
+ *
+ * Block on an eventfd. Handles non-blocking, as well as
+ * the restart cases.
+ */
+static int
+eventfd_wait(struct eventfd * const efd, int const fflag, bool const is_write)
+{
+ kcondvar_t *waitcv;
+ 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 (efd->efd_restarting) {
+ cv_wait(&efd->efd_restart_wait, &efd->efd_lock);
+ }
+
+ if (is_write) {
+ efd->efd_has_write_waiters = true;
+ waitcv = &efd->efd_write_wait;
+ } else {
+ efd->efd_has_read_waiters = true;
+ waitcv = &efd->efd_read_wait;
+ }
+
+ efd->efd_nwaiters++;
+ KASSERT(efd->efd_nwaiters > 0);
+ error = cv_wait_sig(waitcv, &efd->efd_lock);
+ efd->efd_nwaiters--;
+ KASSERT(efd->efd_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 (efd->efd_restarting) {
+ if (error == 0) {
+ error = ERESTART;
+ }
+ if (efd->efd_nwaiters == 0) {
+ efd->efd_restarting = false;
+ cv_broadcast(&efd->efd_restart_wait);
+ }
+ }
+
+ return error;
+}
+
+/*
+ * eventfd_wake:
+ *
+ * Wake LWPs block on an eventfd.
+ */
+static void
+eventfd_wake(struct eventfd * const efd, bool const is_write)
+{
+ kcondvar_t *waitcv = NULL;
+ struct selinfo *sel;
+ int pollev;
+
+ if (is_write) {
+ if (efd->efd_has_read_waiters) {
+ waitcv = &efd->efd_read_wait;
+ efd->efd_has_read_waiters = false;
+ }
+ sel = &efd->efd_read_sel;
+ pollev = POLLIN | POLLRDNORM;
+ } else {
+ if (efd->efd_has_write_waiters) {
+ waitcv = &efd->efd_write_wait;
+ efd->efd_has_write_waiters = false;
+ }
+ sel = &efd->efd_write_sel;
+ pollev = POLLOUT | POLLWRNORM;
+ }
+ if (waitcv != NULL) {
+ cv_broadcast(waitcv);
+ }
+ selnotify(sel, pollev, NOTE_SUBMIT);
+}
+
+/*
+ * eventfd file operations
+ */
+
+static int
+eventfd_fop_read(file_t * const fp, off_t * const offset,
+ struct uio * const uio, kauth_cred_t const cred, int const flags)
+{
+ struct eventfd * const efd = fp->f_eventfd;
+ int const fflag = fp->f_flag;
+ eventfd_t return_value;
+ int error;
+
+ if (uio->uio_resid < sizeof(eventfd_t)) {
+ return EINVAL;
+ }
+
+ mutex_enter(&efd->efd_lock);
+
+ while (efd->efd_val == 0) {
+ if ((error = eventfd_wait(efd, fflag, false)) != 0) {
+ mutex_exit(&efd->efd_lock);
+ return error;
+ }
+ }
+
+ if (efd->efd_is_semaphore) {
+ return_value = 1;
+ efd->efd_val--;
+ } else {
+ return_value = efd->efd_val;
+ efd->efd_val = 0;
+ }
+
+ getnanotime(&efd->efd_atime);
+ eventfd_wake(efd, false);
+
+ /* XXX Should we unlock before the uiomove()? */
+
+ error = uiomove(&return_value, sizeof(return_value), uio);
+
+ /* XXX Should we restore eventfd state if uiomove() fails? */
+
+ mutex_exit(&efd->efd_lock);
+
+ return error;
+}
+
+static int
+eventfd_fop_write(file_t * const fp, off_t * const offset,
+ struct uio * const uio, kauth_cred_t const cred, int const flags)
+{
+ struct eventfd * const efd = fp->f_eventfd;
+ int const fflag = fp->f_flag;
+ eventfd_t write_value;
Home |
Main Index |
Thread Index |
Old Index