summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvisa <visa@openbsd.org>2020-01-18 08:59:48 +0000
committervisa <visa@openbsd.org>2020-01-18 08:59:48 +0000
commit94dbc69abff2bc146c3ff0d3117d0f13249b4178 (patch)
tree58ab50453725fc046ef1e0478be00aa59ebe5322
parentClear mount operation argument flags from mnt_flag after mount. (diff)
downloadwireguard-openbsd-94dbc69abff2bc146c3ff0d3117d0f13249b4178.tar.xz
wireguard-openbsd-94dbc69abff2bc146c3ff0d3117d0f13249b4178.zip
Make klist_invalidate() more careful and general. Acquire knotes before
changing them, to synchronize with kqueue_register() and kqueue_scan(). Detach the knotes from the original knote list, change the filterops to one that always indicates EOF condition, and activate in one-shot mode. The detaching allows the original knote list head to be deleted after klist_invalidate() returns. The knotes are activated to make the EOF condition visible to the event subscribers as soon as possible. As the knotes are detached from the list, klist_invalidate() does not have to wait for userspace to process them. The use of the special filterops minimizes the need to handle klist invalidation in actual implementations of filterops. Tested by Greg Steuck OK mpi@
-rw-r--r--sys/kern/kern_event.c39
1 files changed, 35 insertions, 4 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 828b2ffa06c..b84a674e38b 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_event.c,v 1.120 2020/01/13 13:57:19 visa Exp $ */
+/* $OpenBSD: kern_event.c,v 1.121 2020/01/18 08:59:48 visa Exp $ */
/*-
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -465,6 +465,27 @@ seltrue_kqfilter(dev_t dev, struct knote *kn)
return (0);
}
+static int
+filt_dead(struct knote *kn, long hint)
+{
+ kn->kn_flags |= (EV_EOF | EV_ONESHOT);
+ kn->kn_data = 0;
+ return (1);
+}
+
+static void
+filt_deaddetach(struct knote *kn)
+{
+ /* Nothing to do */
+}
+
+static const struct filterops dead_filtops = {
+ .f_isfd = 1,
+ .f_attach = NULL,
+ .f_detach = filt_deaddetach,
+ .f_event = filt_dead,
+};
+
int
sys_kqueue(struct proc *p, void *v, register_t *retval)
{
@@ -1315,8 +1336,18 @@ klist_invalidate(struct klist *list)
{
struct knote *kn;
- SLIST_FOREACH(kn, list, kn_selnext) {
- kn->kn_status |= KN_DETACHED;
- kn->kn_flags |= EV_EOF | EV_ONESHOT;
+ /*
+ * NET_LOCK() must not be held because it can block another thread
+ * in f_event with a knote acquired.
+ */
+ NET_ASSERT_UNLOCKED();
+
+ while ((kn = SLIST_FIRST(list)) != NULL) {
+ if (knote_acquire(kn) == 0)
+ continue;
+ kn->kn_fop->f_detach(kn);
+ kn->kn_fop = &dead_filtops;
+ knote_activate(kn);
+ knote_release(kn);
}
}