summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvisa <visa@openbsd.org>2020-12-18 16:16:14 +0000
committervisa <visa@openbsd.org>2020-12-18 16:16:14 +0000
commit38cb82050136815712a7a732c8ec6a27c0b758a1 (patch)
tree681d321b628b3d9cdc3d55763542db0e779ed8e3
parentMake knote_{activate,remove}() internal to kern_event.c. (diff)
downloadwireguard-openbsd-38cb82050136815712a7a732c8ec6a27c0b758a1.tar.xz
wireguard-openbsd-38cb82050136815712a7a732c8ec6a27c0b758a1.zip
Add fd close notification for kqueue-based poll() and select()
When the file descriptor of an __EV_POLL-flagged knote is closed, post EBADF through the kqueue instance to the caller of kqueue_scan(). This lets kqueue-based poll() and select() preserve their current behaviour of returning EBADF when a polled file descriptor is closed concurrently. OK mpi@
-rw-r--r--sys/kern/kern_event.c45
-rw-r--r--sys/sys/eventvar.h3
2 files changed, 40 insertions, 8 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index a17e996e2ad..46c5f92d27c 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: kern_event.c,v 1.151 2020/12/18 16:10:57 visa Exp $ */
+/* $OpenBSD: kern_event.c,v 1.152 2020/12/18 16:16:14 visa Exp $ */
/*-
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -96,7 +96,7 @@ void knote_dequeue(struct knote *kn);
int knote_acquire(struct knote *kn);
void knote_release(struct knote *kn);
void knote_activate(struct knote *kn);
-void knote_remove(struct proc *p, struct knlist *list);
+void knote_remove(struct proc *p, struct knlist *list, int purge);
void filt_kqdetach(struct knote *kn);
int filt_kqueue(struct knote *kn, long hint);
@@ -506,8 +506,14 @@ kqpoll_init(void)
struct proc *p = curproc;
struct filedesc *fdp;
- if (p->p_kq != NULL)
+ if (p->p_kq != NULL) {
+ /*
+ * Clear any pending error that was raised after
+ * previous scan.
+ */
+ p->p_kq->kq_error = 0;
return;
+ }
p->p_kq = kqueue_alloc(p->p_fd);
p->p_kq_serial = arc4random();
@@ -964,6 +970,15 @@ retry:
}
s = splhigh();
+
+ if (kq->kq_error != 0) {
+ /* Deliver the pending error. */
+ error = kq->kq_error;
+ kq->kq_error = 0;
+ splx(s);
+ goto done;
+ }
+
if (kq->kq_count == 0) {
/*
* Successive loops are only necessary if there are more
@@ -1175,10 +1190,10 @@ kqueue_purge(struct proc *p, struct kqueue *kq)
KERNEL_ASSERT_LOCKED();
for (i = 0; i < kq->kq_knlistsize; i++)
- knote_remove(p, &kq->kq_knlist[i]);
+ knote_remove(p, &kq->kq_knlist[i], 1);
if (kq->kq_knhashmask != 0) {
for (i = 0; i < kq->kq_knhashmask + 1; i++)
- knote_remove(p, &kq->kq_knhash[i]);
+ knote_remove(p, &kq->kq_knhash[i], 1);
}
}
@@ -1358,9 +1373,10 @@ knote(struct klist *list, long hint)
* remove all knotes from a specified knlist
*/
void
-knote_remove(struct proc *p, struct knlist *list)
+knote_remove(struct proc *p, struct knlist *list, int purge)
{
struct knote *kn;
+ struct kqueue *kq;
int s;
while ((kn = SLIST_FIRST(list)) != NULL) {
@@ -1371,6 +1387,21 @@ knote_remove(struct proc *p, struct knlist *list)
}
splx(s);
kn->kn_fop->f_detach(kn);
+
+ /*
+ * Notify poll(2) and select(2) when a monitored
+ * file descriptor is closed.
+ */
+ if (!purge && (kn->kn_flags & __EV_POLL) != 0) {
+ kq = kn->kn_kq;
+ s = splhigh();
+ if (kq->kq_error == 0) {
+ kq->kq_error = EBADF;
+ kqueue_wakeup(kq);
+ }
+ splx(s);
+ }
+
knote_drop(kn, p);
}
}
@@ -1401,7 +1432,7 @@ knote_fdclose(struct proc *p, int fd)
continue;
list = &kq->kq_knlist[fd];
- knote_remove(p, list);
+ knote_remove(p, list, 0);
}
KERNEL_UNLOCK();
}
diff --git a/sys/sys/eventvar.h b/sys/sys/eventvar.h
index 44338c5e38a..e2eaa7a6280 100644
--- a/sys/sys/eventvar.h
+++ b/sys/sys/eventvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: eventvar.h,v 1.9 2020/04/07 13:27:52 visa Exp $ */
+/* $OpenBSD: eventvar.h,v 1.10 2020/12/18 16:16:14 visa Exp $ */
/*-
* Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org>
@@ -59,6 +59,7 @@ struct kqueue {
#define KQ_SEL 0x01
#define KQ_SLEEP 0x02
#define KQ_DYING 0x04
+ int kq_error; /* pending error */
};
#endif /* !_SYS_EVENTVAR_H_ */