diff options
author | 2020-12-18 16:16:14 +0000 | |
---|---|---|
committer | 2020-12-18 16:16:14 +0000 | |
commit | 38cb82050136815712a7a732c8ec6a27c0b758a1 (patch) | |
tree | 681d321b628b3d9cdc3d55763542db0e779ed8e3 | |
parent | Make knote_{activate,remove}() internal to kern_event.c. (diff) | |
download | wireguard-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.c | 45 | ||||
-rw-r--r-- | sys/sys/eventvar.h | 3 |
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_ */ |