Module Name: src Committed By: jdolecek Date: Sun May 2 19:13:43 UTC 2021
Modified Files: src/sys/kern: kern_event.c src/sys/sys: eventvar.h Log Message: implement fo_restart hook for kqueue descriptors, so that close(2) on the descriptor won't block indefinitely if other thread is currently blocked on the same kqueue in kevent(2) done similarily to pipes and sockets, i.e. using flag on the potentially shared kqueue structure hooked off file_t - this is somewhat suboptimal if the application dup(2)ped the descriptor, but this should be rare enough to not really matter usually this causes the kevent(2) to end up returning EBADF since on the syscall restart the descriptor is not there anymore; if dup(2)ped the kevent(2) call can continue successfully if the closed kqueue descriptor was other than the one used for the kevent(2) call PR kern/46248 by Julian Fagir To generate a diff of this commit: cvs rdiff -u -r1.117 -r1.118 src/sys/kern/kern_event.c cvs rdiff -u -r1.8 -r1.9 src/sys/sys/eventvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/kern_event.c diff -u src/sys/kern/kern_event.c:1.117 src/sys/kern/kern_event.c:1.118 --- src/sys/kern/kern_event.c:1.117 Wed Jan 27 06:59:08 2021 +++ src/sys/kern/kern_event.c Sun May 2 19:13:43 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_event.c,v 1.117 2021/01/27 06:59:08 skrll Exp $ */ +/* $NetBSD: kern_event.c,v 1.118 2021/05/02 19:13:43 jdolecek Exp $ */ /*- * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc. @@ -59,7 +59,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.117 2021/01/27 06:59:08 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.118 2021/05/02 19:13:43 jdolecek Exp $"); #include <sys/param.h> #include <sys/systm.h> @@ -90,6 +90,7 @@ static int kqueue_poll(file_t *, int); static int kqueue_kqfilter(file_t *, struct knote *); static int kqueue_stat(file_t *, struct stat *); static int kqueue_close(file_t *); +static void kqueue_restart(file_t *); static int kqueue_register(struct kqueue *, struct kevent *); static void kqueue_doclose(struct kqueue *, struct klist *, int); @@ -125,7 +126,7 @@ static const struct fileops kqueueops = .fo_stat = kqueue_stat, .fo_close = kqueue_close, .fo_kqfilter = kqueue_kqfilter, - .fo_restart = fnullop_restart, + .fo_restart = kqueue_restart, }; static const struct filterops kqread_filtops = { @@ -501,7 +502,7 @@ filt_kqueue(struct knote *kn, long hint) if (hint != NOTE_SUBMIT) mutex_spin_enter(&kq->kq_lock); - kn->kn_data = kq->kq_count; + kn->kn_data = KQ_COUNT(kq); rv = (kn->kn_data > 0); if (hint != NOTE_SUBMIT) mutex_spin_exit(&kq->kq_lock); @@ -1328,12 +1329,12 @@ static void kqueue_check(const char *func, size_t line, const struct kqueue *kq) { const struct knote *kn; - int count; + u_int count; int nmarker; char buf[128]; KASSERT(mutex_owned(&kq->kq_lock)); - KASSERT(kq->kq_count >= 0); + KASSERT(KQ_COUNT(kq) < UINT_MAX / 2); count = 0; nmarker = 0; @@ -1353,22 +1354,14 @@ kqueue_check(const char *func, size_t li func, line, kq, kn, KN_FMT(buf, kn)); } count++; - if (count > kq->kq_count) { + if (count > KQ_COUNT(kq)) { panic("%s,%zu: kq=%p kq->kq_count(%d) != " "count(%d), nmarker=%d", - func, line, kq, kq->kq_count, count, + func, line, kq, KQ_COUNT(kq), count, nmarker); } } else { nmarker++; -#if 0 - if (nmarker > 10000) { - panic("%s,%zu: kq=%p too many markers: " - "%d != %d, nmarker=%d", - func, line, kq, kq->kq_count, count, - nmarker); - } -#endif } } } @@ -1377,6 +1370,18 @@ kqueue_check(const char *func, size_t li #define kq_check(a) /* nothing */ #endif /* defined(DEBUG) */ +static void +kqueue_restart(file_t *fp) +{ + struct kqueue *kq = fp->f_kqueue; + KASSERT(kq != NULL); + + mutex_spin_enter(&kq->kq_lock); + kq->kq_count |= KQ_RESTART; + cv_broadcast(&kq->kq_cv); + mutex_spin_exit(&kq->kq_lock); +} + /* * Scan through the list of events on fp (for a maximum of maxevents), * returning the results in to ulistp. Timeout is determined by tsp; if @@ -1426,14 +1431,19 @@ kqueue_scan(file_t *fp, size_t maxevents mutex_spin_enter(&kq->kq_lock); retry: kevp = kevbuf; - if (kq->kq_count == 0) { + if (KQ_COUNT(kq) == 0) { if (timeout >= 0) { error = cv_timedwait_sig(&kq->kq_cv, &kq->kq_lock, timeout); if (error == 0) { - if (tsp == NULL || (timeout = - gettimeleft(&ats, &sleepts)) > 0) + if (KQ_COUNT(kq) == 0 && + (kq->kq_count & KQ_RESTART)) { + /* return to clear file reference */ + error = ERESTART; + } else if (tsp == NULL || (timeout = + gettimeleft(&ats, &sleepts)) > 0) { goto retry; + } } else { /* don't restart after signals... */ if (error == ERESTART) @@ -1689,7 +1699,7 @@ kqueue_poll(file_t *fp, int events) revents = 0; if (events & (POLLIN | POLLRDNORM)) { mutex_spin_enter(&kq->kq_lock); - if (kq->kq_count != 0) { + if (KQ_COUNT(kq) != 0) { revents |= events & (POLLIN | POLLRDNORM); } else { selrecord(curlwp, &kq->kq_sel); @@ -1713,7 +1723,7 @@ kqueue_stat(file_t *fp, struct stat *st) kq = fp->f_kqueue; memset(st, 0, sizeof(*st)); - st->st_size = kq->kq_count; + st->st_size = KQ_COUNT(kq); st->st_blksize = sizeof(struct kevent); st->st_mode = S_IFIFO; @@ -1771,7 +1781,7 @@ kqueue_close(file_t *fp) } mutex_exit(&fdp->fd_lock); - KASSERT(kq->kq_count == 0); + KASSERT(KQ_COUNT(kq) == 0); mutex_destroy(&kq->kq_lock); cv_destroy(&kq->kq_cv); seldestroy(&kq->kq_sel); Index: src/sys/sys/eventvar.h diff -u src/sys/sys/eventvar.h:1.8 src/sys/sys/eventvar.h:1.9 --- src/sys/sys/eventvar.h:1.8 Fri Mar 21 21:53:35 2008 +++ src/sys/sys/eventvar.h Sun May 2 19:13:43 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: eventvar.h,v 1.8 2008/03/21 21:53:35 ad Exp $ */ +/* $NetBSD: eventvar.h,v 1.9 2021/05/02 19:13:43 jdolecek Exp $ */ /*- * Copyright (c) 1999,2000 Jonathan Lemon <jle...@freebsd.org> @@ -51,7 +51,9 @@ struct kqueue { filedesc_t *kq_fdp; struct selinfo kq_sel; kcondvar_t kq_cv; - int kq_count; /* number of pending events */ + u_int kq_count; /* number of pending events */ +#define KQ_RESTART 0x80000000 /* force ERESTART */ +#define KQ_COUNT(kq) ((kq)->kq_count & ~KQ_RESTART) }; #endif /* !_SYS_EVENTVAR_H_ */