Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F146183800
D37102.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
23 KB
Referenced Files
None
Subscribers
None
D37102.diff
View Options
diff --git a/lib/libc/sys/kqueue.2 b/lib/libc/sys/kqueue.2
--- a/lib/libc/sys/kqueue.2
+++ b/lib/libc/sys/kqueue.2
@@ -145,6 +145,15 @@
and
.Fa eventlist .
.Pp
+The special value
+.Va KQUEUE_FD_ANON
+may be given as the
+.Va fd
+argument to
+.Fn kevent .
+In this case, an anonymous temporary queue is used, and no longer exists
+after the call returns.
+.Pp
The
.Fn EV_SET
macro is provided for ease of initializing a
@@ -262,6 +271,9 @@
See
.Sx RETURN VALUES
below.
+.It Dv EV_ANON
+This is used for anonymous operations that are not associated with a particular
+kqueue descriptor.
.It Dv EV_KEEPUDATA
Causes
.Fn kevent
@@ -662,6 +674,46 @@
On return,
.Va fflags
contains the users defined flags in the lower 24 bits.
+.It Dv EVFILT_USERMEM
+Waits for a value to change at an address in user memory. The address
+is provided in
+.Va ident .
+The value it points to is compared with an expected value provided in
+.Va data ,
+and the event is triggered immediately if they are not equal. Otherwise, it
+must be triggered explicitly using
+.Va NOTE_TRIGGER
+by any thread that wants to advertise that it has changed the value. The event
+can be triggered by any process that has the same memory object mapped, even at
+a different address, unless
+.Va NOTE_USERMEM_PRIVATE
+is added to
+.Va fflags
+to prevent inter-process events.
+When
+.Va NOTE_TRIGGER
+is used in
+.Va fflags ,
+.Va flags
+should be set to
+.Va EV_ANON ,
+because this operation wakes waiters not only in the given kqueue object.
+.Bl -tag -width "Dv NOTE_UMTX_WAIT_PRIVATE"
+.It Dv NOTE_USERMEM_INT
+.Va ident
+points to an object the size of an integer.
+.It Dv NOTE_USERMEM_LONG
+.Va ident
+points to an object the size of a long.
+.It Dv NOTE_TRIGGER
+Cause the event to be triggered. The number of waiters to wake up should
+be provided in
+.Va data .
+.It Dv NOTE_USERMEM_PRIVATE
+.Va ident
+is in a private namespace that is not shared with other processes that map
+the containing memory object. Can be ORed with the preceding values.
+.El
.El
.Sh CANCELLATION BEHAVIOUR
If
diff --git a/share/man/man9/kqueue.9 b/share/man/man9/kqueue.9
--- a/share/man/man9/kqueue.9
+++ b/share/man/man9/kqueue.9
@@ -212,6 +212,14 @@
that the
.Va knote
was added to.
+.It Va f_anon
+The
+.Va f_anon
+function will be called for
+.Vt kevent
+objects with the
+.Dv EV_ANON
+flag, to perform actions that don't relate to a specific kqueue or knote.
.El
.Pp
The function
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -70,6 +70,7 @@
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
#include <sys/uio.h>
+#include <sys/umtx.h>
#include <sys/user.h>
#ifdef KTRACE
#include <sys/ktrace.h>
@@ -203,6 +204,13 @@
.f_event = filt_user,
.f_touch = filt_usertouch,
};
+static struct filterops usermem_filtops = {
+ .f_attach = filt_usermemattach,
+ .f_detach = filt_usermemdetach,
+ .f_event = filt_usermem,
+ .f_touch = filt_usermemtouch,
+ .f_anon = filt_usermemanon,
+};
static uma_zone_t knote_zone;
static unsigned int __exclusive_cache_line kq_ncallouts;
@@ -361,6 +369,7 @@
{ &user_filtops, 1 }, /* EVFILT_USER */
{ &null_filtops }, /* EVFILT_SENDFILE */
{ &file_filtops, 1 }, /* EVFILT_EMPTY */
+ { &usermem_filtops, 1 }, /* EVFILT_USERMEM */
};
/*
@@ -1293,6 +1302,11 @@
struct file *fp;
int error;
+ if (fd == KQUEUE_FD_ANON) {
+ error = kern_kevent_anonymous(td, nchanges, nevents, k_ops, timeout);
+ return (error);
+ }
+
cap_rights_init_zero(&rights);
if (nchanges > 0)
cap_rights_set_one(&rights, CAP_KQUEUE_CHANGE);
@@ -1372,15 +1386,15 @@
* used to perform one-shot polling, similar to poll() and select().
*/
int
-kern_kevent_anonymous(struct thread *td, int nevents,
- struct kevent_copyops *k_ops)
+kern_kevent_anonymous(struct thread *td, int nchanges, int nevents,
+ struct kevent_copyops *k_ops, const struct timespec *timeout)
{
struct kqueue kq = {};
int error;
kqueue_init(&kq);
kq.kq_refcnt = 1;
- error = kqueue_kevent(&kq, td, nevents, nevents, k_ops, NULL);
+ error = kqueue_kevent(&kq, td, nchanges, nevents, k_ops, timeout);
kqueue_drain(&kq, td);
kqueue_destroy(&kq);
return (error);
@@ -1500,6 +1514,17 @@
if (fops == NULL)
return EINVAL;
+ /* Anonymous actions have no kqueue or knote */
+ if (kev->flags & EV_ANON) {
+ tkn = NULL;
+ if (fops->f_anon == NULL) {
+ error = EINVAL;
+ goto done;
+ }
+ error = fops->f_anon(kev);
+ goto done;
+ }
+
if (kev->flags & EV_ADD) {
/* Reject an invalid flag pair early */
if (kev->flags & EV_KEEPUDATA) {
diff --git a/sys/kern/kern_umtx.c b/sys/kern/kern_umtx.c
--- a/sys/kern/kern_umtx.c
+++ b/sys/kern/kern_umtx.c
@@ -60,6 +60,7 @@
#include <sys/syscallsubr.h>
#include <sys/taskqueue.h>
#include <sys/time.h>
+#include <sys/event.h>
#include <sys/eventhandler.h>
#include <sys/umtx.h>
#include <sys/umtxvar.h>
@@ -598,16 +599,22 @@
umtxq_signal_queue(struct umtx_key *key, int n_wake, int q)
{
struct umtxq_queue *uh;
- struct umtx_q *uq;
+ struct umtx_q *uq, *uq_temp;
int ret;
ret = 0;
UMTXQ_LOCKED_ASSERT(umtxq_getchain(key));
uh = umtxq_queue_lookup(key, q);
if (uh != NULL) {
- while ((uq = TAILQ_FIRST(&uh->head)) != NULL) {
- umtxq_remove_queue(uq, q);
- wakeup(uq);
+ TAILQ_FOREACH_SAFE(uq, &uh->head, uq_link, uq_temp) {
+ if (uq->uq_flags & UQF_KQUEUE) {
+ if (uq->uq_flags & UQF_KQUEUE_ONCE)
+ umtxq_remove_queue(uq, q);
+ KNOTE_LOCKED(&uq->uq_klist, 1);
+ } else {
+ umtxq_remove_queue(uq, q);
+ wakeup(uq);
+ }
if (++ret >= n_wake)
return (ret);
}
@@ -5124,3 +5131,166 @@
if (rb_inact != 0)
(void)umtx_handle_rb(td, rb_inact, NULL, true, compat32);
}
+
+int
+filt_usermemattach(struct knote *kn)
+{
+ struct umtx_q *uq;
+ void *uaddr;
+ u_long tmp;
+ u_int tmp32;
+ int error;
+ bool is_private;
+
+ is_private = (kn->kn_sfflags & NOTE_USERMEM_PRIVATE) != 0;
+
+ uaddr = (void *)kn->kn_kevent.ident;
+
+ /* XXX stricter checking of acceptable fflags */
+ if ((kn->kn_sfflags & NOTE_USERMEM_INT) == 0 &&
+ (kn->kn_sfflags & NOTE_USERMEM_LONG) == 0)
+ return (EINVAL);
+
+ /* Make a new umtx queue object and join the wait list. */
+ uq = umtxq_alloc();
+ if ((error = umtx_key_get(uaddr, TYPE_SIMPLE_WAIT,
+ is_private ? THREAD_SHARE : AUTO_SHARE, &uq->uq_key)) != 0) {
+ goto exit_free;
+ }
+
+ knlist_init_mtx(&uq->uq_klist, &umtxq_getchain(&uq->uq_key)->uc_lock);
+ uq->uq_flags |= UQF_KQUEUE;
+ if (kn->kn_flags & EV_ONESHOT)
+ uq->uq_flags |= UQF_KQUEUE_ONCE;
+ kn->kn_ptr.p_v = uq;
+
+ umtxq_lock(&uq->uq_key);
+ knlist_add(&uq->uq_klist, kn, 1);
+ umtxq_insert(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ /* Fetch the user's value. */
+ if (kn->kn_sfflags & NOTE_USERMEM_INT) {
+ /* XXX also need to use this for _LONG for 32 bit process? */
+ error = fueword32(uaddr, &tmp32);
+ tmp = tmp32;
+ } else {
+ error = fueword(uaddr, &tmp);
+ }
+ if (error != 0) {
+ error = EFAULT;
+ goto exit_dequeue;
+ }
+
+ /* If value doesn't match, trigger immediately. */
+ if (tmp != (u_long) kn->kn_sdata) {
+ kn->kn_hookid = 1;
+ if (uq->uq_flags & UQF_KQUEUE_ONCE) {
+ umtxq_lock(&uq->uq_key);
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+ }
+ }
+
+ /* Otherwise we're now in the queue, waiting. */
+ return (0);
+
+exit_dequeue:
+ umtxq_lock(&uq->uq_key);
+ knlist_remove(&uq->uq_klist, kn, 1);
+ kn->kn_ptr.p_v = NULL;
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ umtx_key_release(&uq->uq_key);
+
+exit_free:
+ umtxq_free(uq);
+
+ return (error);
+}
+
+void
+filt_usermemdetach(struct knote *kn)
+{
+ struct umtx_q *uq;
+
+ uq = kn->kn_ptr.p_v;
+
+ umtxq_lock(&uq->uq_key);
+ knlist_remove(&uq->uq_klist, kn, 1);
+ kn->kn_ptr.p_v = NULL;
+ umtxq_remove(uq);
+ umtxq_unlock(&uq->uq_key);
+
+ umtx_key_release(&uq->uq_key);
+
+ umtxq_free(uq);
+}
+
+int
+filt_usermem(struct knote *kn, long hint)
+{
+ if (hint)
+ kn->kn_hookid = 1;
+
+ return (kn->kn_hookid);
+}
+
+void
+filt_usermemtouch(struct knote *kn, struct kevent *kev, u_long type)
+{
+ switch (type) {
+ case EVENT_REGISTER:
+ /*
+ * Re-adding the same ident.
+ *
+ * XXX If data and fflags haven't changed, there's nothing to
+ * do as we're already in the umtx queue and anyone who changed
+ * the value should have woken us. Otherwise, we'd need to
+ * compare the value, but we can't here: we hold a
+ * non-sleepable lock, but reading userspace memory acquires a
+ * sleepable lock. So in that case, trigger immediately and
+ * let the user deal with it.
+ */
+ if (kev->data != kn->kn_sdata || kev->fflags != kn->kn_sfflags) {
+ struct umtx_q *uq;
+
+ kn->kn_hookid = 1;
+ kn->kn_sdata = kev->data;
+ kn->kn_fflags = kn->kn_sfflags;
+
+ uq = kn->kn_ptr.p_v;
+ if (uq->uq_flags & UQF_KQUEUE_ONCE)
+ umtxq_remove(uq);
+ }
+ break;
+ case EVENT_PROCESS:
+ *kev = kn->kn_kevent;
+ kn->kn_hookid = 0;
+ break;
+ default:
+ panic("filt_usermemtouch() - invalid type (%ld)", type);
+ break;
+ }
+}
+
+int
+filt_usermemanon(struct kevent *kev)
+{
+ int is_private;
+ void *uaddr;
+ int n_wake;
+ int error;
+
+ if (kev->fflags != NOTE_TRIGGER)
+ return (EINVAL);
+
+ is_private = (kev->fflags & NOTE_USERMEM_PRIVATE) != 0;
+ uaddr = (void *)kev->ident;
+ n_wake = kev->data;
+
+ error = kern_umtx_wake(curthread, uaddr, n_wake, is_private);
+
+ return (error);
+}
diff --git a/sys/sys/event.h b/sys/sys/event.h
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -34,6 +34,8 @@
#include <sys/_types.h>
#include <sys/queue.h>
+#define KQUEUE_FD_ANON (-2) /* anonymous temporary kqueue */
+
#define EVFILT_READ (-1)
#define EVFILT_WRITE (-2)
#define EVFILT_AIO (-3) /* attached to aio requests */
@@ -47,7 +49,8 @@
#define EVFILT_USER (-11) /* User events */
#define EVFILT_SENDFILE (-12) /* attached to sendfile requests */
#define EVFILT_EMPTY (-13) /* empty send socket buf */
-#define EVFILT_SYSCOUNT 13
+#define EVFILT_USERMEM (-14) /* attached to value in user memory */
+#define EVFILT_SYSCOUNT 14
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
@@ -145,6 +148,7 @@
#define EV_CLEAR 0x0020 /* clear event state after reporting */
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
#define EV_DISPATCH 0x0080 /* disable event after reporting */
+#define EV_ANON 0x0400 /* anonymous action (not specific kq) */
#define EV_SYSFLAGS 0xF000 /* reserved by system */
#define EV_DROP 0x1000 /* note should be dropped */
@@ -218,6 +222,11 @@
#define NOTE_NSECONDS 0x00000008 /* data is nanoseconds */
#define NOTE_ABSTIME 0x00000010 /* timeout is absolute */
+/* addition flags for EVFILT_USERMEM */
+#define NOTE_USERMEM_INT 0x00000001 /* ident is pointer to int */
+#define NOTE_USERMEM_LONG 0x00000002 /* ident is pointer to long */
+#define NOTE_USERMEM_PRIVATE 0x00000004 /* no inter-process events */
+
struct knote;
SLIST_HEAD(klist, knote);
struct kqueue;
@@ -267,6 +276,7 @@
void (*f_detach)(struct knote *kn);
int (*f_event)(struct knote *kn, long hint);
void (*f_touch)(struct knote *kn, struct kevent *kev, u_long type);
+ int (*f_anon)(struct kevent *kev);
};
/*
diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h
--- a/sys/sys/syscallsubr.h
+++ b/sys/sys/syscallsubr.h
@@ -185,8 +185,8 @@
int kern_jail_set(struct thread *td, struct uio *options, int flags);
int kern_kevent(struct thread *td, int fd, int nchanges, int nevents,
struct kevent_copyops *k_ops, const struct timespec *timeout);
-int kern_kevent_anonymous(struct thread *td, int nevents,
- struct kevent_copyops *k_ops);
+int kern_kevent_anonymous(struct thread *td, int nchanges, int nevents,
+ struct kevent_copyops *k_ops, const struct timespec *timeout);
int kern_kevent_fp(struct thread *td, struct file *fp, int nchanges,
int nevents, struct kevent_copyops *k_ops,
const struct timespec *timeout);
diff --git a/sys/sys/umtx.h b/sys/sys/umtx.h
--- a/sys/sys/umtx.h
+++ b/sys/sys/umtx.h
@@ -138,4 +138,13 @@
__END_DECLS
+#ifdef _KERNEL
+struct knote;
+int filt_usermemattach(struct knote *kn);
+void filt_usermemdetach(struct knote *kn);
+int filt_usermem(struct knote *kn, long hint);
+void filt_usermemtouch(struct knote *kn, struct kevent *touch, u_long type);
+int filt_usermemanon(struct kevent *kev);
+#endif
+
#endif /* !_SYS_UMTX_H_ */
diff --git a/sys/sys/umtxvar.h b/sys/sys/umtxvar.h
--- a/sys/sys/umtxvar.h
+++ b/sys/sys/umtxvar.h
@@ -120,6 +120,8 @@
/* Umtx flags. */
int uq_flags;
#define UQF_UMTXQ 0x0001
+#define UQF_KQUEUE 0x0002
+#define UQF_KQUEUE_ONCE 0x0004
/* Futex bitset mask */
u_int uq_bitset;
@@ -148,6 +150,9 @@
/* The queue we on */
struct umtxq_queue *uq_cur_queue;
+
+ /* List to hold knote, if waiting with kevent. */
+ struct knlist uq_klist;
};
TAILQ_HEAD(umtxq_head, umtx_q);
diff --git a/tests/sys/kqueue/Makefile b/tests/sys/kqueue/Makefile
--- a/tests/sys/kqueue/Makefile
+++ b/tests/sys/kqueue/Makefile
@@ -6,6 +6,7 @@
BINDIR= ${TESTSDIR}
ATF_TESTS_C+= kqueue_peek_signal
+ATF_TESTS_C+= kqueue_usermem
NETBSD_ATF_TESTS_C= proc1_test
# XXX: fails `ke.fflags & NOTE_TRACKERR` invariant
diff --git a/tests/sys/kqueue/kqueue_usermem.c b/tests/sys/kqueue/kqueue_usermem.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kqueue/kqueue_usermem.c
@@ -0,0 +1,320 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * 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>
+#include <sys/event.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/umtx.h> /* only needed to test _umtx_op iterop */
+#include <sys/wait.h>
+
+#include <atf-c.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdlib.h>
+
+ATF_TC_WITHOUT_HEAD(main);
+
+ATF_TC_BODY(main, tc)
+{
+ struct timespec zero_timeout = {0, 0};
+ u_long *shmem;
+ int shmem_fd;
+ int rv;
+
+ shmem_fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0);
+ ATF_REQUIRE(shmem_fd >= 0);
+
+ rv = ftruncate(shmem_fd, sizeof(*shmem));
+ ATF_REQUIRE(rv == 0);
+
+ shmem = mmap(NULL, sizeof(*shmem), PROT_READ | PROT_WRITE,
+ MAP_SHARED, shmem_fd, 0);
+ ATF_REQUIRE(shmem != MAP_FAILED);
+
+ int kq = kqueue();
+ ATF_REQUIRE(kq >= 0);
+
+ struct kevent kev;
+ struct kevent out_kev;
+
+ /* Fires immediately because value is not as expected. */
+ *shmem = 0;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Doesn't fire immediately because value is as expected. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /*
+ * Re-adding, with a different expected value. This causes it to fire
+ * (not because the expected value doesn't match *shmem, but because it
+ * doesn't match what we originally added).
+ */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 43, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it using _umtx_op. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* No event should be received (it was one-shot). */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Add it again, with the right value, this time not one-shot. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake it with the right address. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* And again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken again, it's still there... */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Delete it. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_DELETE,
+ NOTE_USERMEM_LONG, 0, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, now deleted. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add it again, with the right value. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake some other umtx address. */
+ rv = _umtx_op(shmem + 1, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because that was the wrong address. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Try to wake it with the right address. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because it was one-shot. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent to the same kqueue. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1, 0);
+ rv = kevent(kq, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent with another kqueue. */
+ int kq2 = kqueue();
+ ATF_REQUIRE(kq2 >= 0);
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1, 0);
+ rv = kevent(kq2, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add to both kqueues, not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq2, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent using one of them. */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1000, 0);
+ rv = kevent(kq, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken in both kqueues. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+ rv = kevent(kq2, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it with NOTE_TRIGGER sent with KQUEUE_FD_ANON */
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ANON,
+ NOTE_TRIGGER, 1000, 0);
+ rv = kevent(KQUEUE_FD_ANON, &kev, 1, NULL, 0, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Add. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Wake it from another process. */
+ pid_t pid = fork();
+ if (pid == 0) {
+ /* Child wakes. */
+ _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ _Exit(0);
+ } else {
+ /* Parent waits for child. */
+ ATF_REQUIRE(pid > 0);
+ int status;
+ wait(&status);
+ }
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+
+ /* Try to wake it again. */
+ rv = _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Shouldn't be woken, because it was one-shot. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* Add, in process-private namespace. Not woken yet. */
+ *shmem = 42;
+ EV_SET(&kev, (uintptr_t) shmem, EVFILT_USERMEM, EV_ADD | EV_ONESHOT,
+ NOTE_USERMEM_LONG | NOTE_USERMEM_PRIVATE, 42, 0);
+ rv = kevent(kq, &kev, 1, &out_kev, 1, &zero_timeout);
+ ATF_REQUIRE_EQ(0, rv);
+
+ /* Try to wake it from another process. */
+ pid = fork();
+ if (pid == 0) {
+ /* Child wakes. */
+ _umtx_op(shmem, UMTX_OP_WAKE, 1, NULL, NULL);
+ _Exit(0);
+ } else {
+ /* Parent waits for child. */
+ ATF_REQUIRE(pid > 0);
+ int status;
+ wait(&status);
+ }
+
+ /* Shouldn't be woken, because other process couldn't see it. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(0, rv);
+
+ /* This process can see it. */
+ _umtx_op(shmem, UMTX_OP_WAKE_PRIVATE, 1, NULL, NULL);
+
+ /* Should be woken. */
+ rv = kevent(kq, NULL, 0, &out_kev, 1, &zero_timeout);
+ ATF_CHECK_EQ(1, rv);
+ ATF_CHECK_EQ(out_kev.ident, (uintptr_t) shmem);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, main);
+
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Sun, Mar 1, 1:22 PM (15 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29114966
Default Alt Text
D37102.diff (23 KB)
Attached To
Mode
D37102: kqueue: EVFILT_USERMEM
Attached
Detach File
Event Timeline
Log In to Comment