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_ */

Reply via email to