Module Name:    src
Committed By:   christos
Date:           Sun Jan 31 04:40:01 UTC 2016

Modified Files:
        src/sys/kern: kern_event.c
        src/sys/sys: event.h

Log Message:
PR/50730: Benny Siegert: Go kqueue test panics kernel.
- use a marker knote from the stack instead of allocating and freeing on
  each scan.
- add more KASSERTS
- introduce a KN_BUSY bit that indicates that the knote is currently being
  scanned, so that knote_detach does not end up deleting it when the file
  descriptor gets closed and we don't end up using/trashing free memory from
  the scan.


To generate a diff of this commit:
cvs rdiff -u -r1.84 -r1.85 src/sys/kern/kern_event.c
cvs rdiff -u -r1.25 -r1.26 src/sys/sys/event.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.84 src/sys/kern/kern_event.c:1.85
--- src/sys/kern/kern_event.c:1.84	Tue Dec  8 09:52:06 2015
+++ src/sys/kern/kern_event.c	Sat Jan 30 23:40:01 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: kern_event.c,v 1.84 2015/12/08 14:52:06 christos Exp $	*/
+/*	$NetBSD: kern_event.c,v 1.85 2016/01/31 04:40:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 2008, 2009 The NetBSD Foundation, Inc.
@@ -58,7 +58,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.84 2015/12/08 14:52:06 christos Exp $");
+__KERNEL_RCSID(0, "$NetBSD: kern_event.c,v 1.85 2016/01/31 04:40:01 christos Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -986,6 +986,7 @@ kqueue_register(struct kqueue *kq, struc
 			kev->data = 0;
 			kn->kn_kevent = *kev;
 
+			KASSERT(kn->kn_fop != NULL);
 			/*
 			 * apply reference count to knote structure, and
 			 * do not release it at the end of this routine.
@@ -1043,6 +1044,7 @@ kqueue_register(struct kqueue *kq, struc
 		 * support events, and the attach routine is
 		 * broken and does not return an error.
 		 */
+		KASSERT(kn->kn_fop != NULL);
 		KASSERT(kn->kn_fop->f_event != NULL);
 		KERNEL_LOCK(1, NULL);			/* XXXSMP */
 		rv = (*kn->kn_fop->f_event)(kn, 0);
@@ -1150,7 +1152,7 @@ kqueue_scan(file_t *fp, size_t maxevents
 	struct kqueue	*kq;
 	struct kevent	*kevp;
 	struct timespec	ats, sleepts;
-	struct knote	*kn, *marker;
+	struct knote	*kn, *marker, morker;
 	size_t		count, nkev, nevents;
 	int		timeout, error, rv;
 	filedesc_t	*fdp;
@@ -1178,7 +1180,8 @@ kqueue_scan(file_t *fp, size_t maxevents
 		timeout = 0;
 	}	
 
-	marker = kmem_zalloc(sizeof(*marker), KM_SLEEP);
+	memset(&morker, 0, sizeof(morker));
+	marker = &morker;
 	marker->kn_status = KN_MARKER;
 	mutex_spin_enter(&kq->kq_lock);
  retry:
@@ -1219,30 +1222,35 @@ kqueue_scan(file_t *fp, size_t maxevents
 				kn = TAILQ_NEXT(kn, kn_tqe);
 			}
 			kq_check(kq);
-			TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
 			kq->kq_count--;
+			TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
 			kn->kn_status &= ~KN_QUEUED;
+			kn->kn_status |= KN_BUSY;
 			kq_check(kq);
 			if (kn->kn_status & KN_DISABLED) {
+				kn->kn_status &= ~KN_BUSY;
 				/* don't want disabled events */
 				continue;
 			}
 			if ((kn->kn_flags & EV_ONESHOT) == 0) {
 				mutex_spin_exit(&kq->kq_lock);
+				KASSERT(kn->kn_fop != NULL);
 				KASSERT(kn->kn_fop->f_event != NULL);
 				KERNEL_LOCK(1, NULL);		/* XXXSMP */
 				rv = (*kn->kn_fop->f_event)(kn, 0);
 				KERNEL_UNLOCK_ONE(NULL);	/* XXXSMP */
 				mutex_spin_enter(&kq->kq_lock);
 				/* Re-poll if note was re-enqueued. */
-				if ((kn->kn_status & KN_QUEUED) != 0)
+				if ((kn->kn_status & KN_QUEUED) != 0) {
+					kn->kn_status &= ~KN_BUSY;
 					continue;
+				}
 				if (rv == 0) {
 					/*
 					 * non-ONESHOT event that hasn't
 					 * triggered again, so de-queue.
 					 */
-					kn->kn_status &= ~KN_ACTIVE;
+					kn->kn_status &= ~(KN_ACTIVE|KN_BUSY);
 					continue;
 				}
 			}
@@ -1253,22 +1261,24 @@ kqueue_scan(file_t *fp, size_t maxevents
 				/* delete ONESHOT events after retrieval */
 				mutex_spin_exit(&kq->kq_lock);
 				mutex_enter(&fdp->fd_lock);
+				kn->kn_status &= ~KN_BUSY;
 				knote_detach(kn, fdp, true);
 				mutex_spin_enter(&kq->kq_lock);
 			} else if (kn->kn_flags & EV_CLEAR) {
 				/* clear state after retrieval */
 				kn->kn_data = 0;
 				kn->kn_fflags = 0;
-				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE);
+				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE|KN_BUSY);
 			} else if (kn->kn_flags & EV_DISPATCH) {
 				kn->kn_status |= KN_DISABLED;
-				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE);
+				kn->kn_status &= ~(KN_QUEUED|KN_ACTIVE|KN_BUSY);
 			} else {
 				/* add event back on list */
 				kq_check(kq);
+				kn->kn_status |= KN_QUEUED;
+				kn->kn_status &= ~KN_BUSY;
 				TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
 				kq->kq_count++;
-				kn->kn_status |= KN_QUEUED;
 				kq_check(kq);
 			}
 			if (nkev == kevcnt) {
@@ -1292,8 +1302,6 @@ kqueue_scan(file_t *fp, size_t maxevents
 	}
  done:
  	mutex_spin_exit(&kq->kq_lock);
-	if (marker != NULL)
-		kmem_free(marker, sizeof(*marker));
 	if (nkev != 0) {
 		/* copyout remaining events */
 		error = (*keops->keo_put_events)(keops->keo_private,
@@ -1514,6 +1522,7 @@ knote(struct klist *list, long hint)
 	struct knote *kn, *tmpkn;
 
 	SLIST_FOREACH_SAFE(kn, list, kn_selnext, tmpkn) {
+		KASSERT(kn->kn_fop != NULL);
 		KASSERT(kn->kn_fop->f_event != NULL);
 		if ((*kn->kn_fop->f_event)(kn, hint))
 			knote_activate(kn);
@@ -1555,8 +1564,10 @@ knote_detach(struct knote *kn, filedesc_
 	KASSERT((kn->kn_status & KN_MARKER) == 0);
 	KASSERT(mutex_owned(&fdp->fd_lock));
 
+	KASSERT(kn->kn_fop != NULL);
 	/* Remove from monitored object. */
 	if (dofop) {
+		KASSERT(kn->kn_fop->f_detach != NULL);
 		KERNEL_LOCK(1, NULL);		/* XXXSMP */
 		(*kn->kn_fop->f_detach)(kn);
 		KERNEL_UNLOCK_ONE(NULL);	/* XXXSMP */
@@ -1571,14 +1582,17 @@ knote_detach(struct knote *kn, filedesc_
 	SLIST_REMOVE(list, kn, knote, kn_link);
 
 	/* Remove from kqueue. */
-	/* XXXAD should verify not in use by kqueue_scan. */
+again:
 	mutex_spin_enter(&kq->kq_lock);
 	if ((kn->kn_status & KN_QUEUED) != 0) {
 		kq_check(kq);
+		kq->kq_count--;
 		TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
 		kn->kn_status &= ~KN_QUEUED;
-		kq->kq_count--;
 		kq_check(kq);
+	} else if (kn->kn_status & KN_BUSY) {
+		mutex_spin_exit(&kq->kq_lock);
+		goto again;
 	}
 	mutex_spin_exit(&kq->kq_lock);
 
@@ -1607,8 +1621,8 @@ knote_enqueue(struct knote *kn)
 	}
 	if ((kn->kn_status & (KN_ACTIVE | KN_QUEUED)) == KN_ACTIVE) {
 		kq_check(kq);
-		TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
 		kn->kn_status |= KN_QUEUED;
+		TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
 		kq->kq_count++;
 		kq_check(kq);
 		cv_broadcast(&kq->kq_cv);
@@ -1632,8 +1646,8 @@ knote_activate(struct knote *kn)
 	kn->kn_status |= KN_ACTIVE;
 	if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0) {
 		kq_check(kq);
-		TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
 		kn->kn_status |= KN_QUEUED;
+		TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
 		kq->kq_count++;
 		kq_check(kq);
 		cv_broadcast(&kq->kq_cv);

Index: src/sys/sys/event.h
diff -u src/sys/sys/event.h:1.25 src/sys/sys/event.h:1.26
--- src/sys/sys/event.h:1.25	Tue Dec  8 09:52:06 2015
+++ src/sys/sys/event.h	Sat Jan 30 23:40:01 2016
@@ -1,4 +1,4 @@
-/*	$NetBSD: event.h,v 1.25 2015/12/08 14:52:06 christos Exp $	*/
+/*	$NetBSD: event.h,v 1.26 2016/01/31 04:40:01 christos Exp $	*/
 
 /*-
  * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org>
@@ -196,6 +196,7 @@ struct knote {
 #define	KN_DISABLED	0x04U			/* event is disabled */
 #define	KN_DETACHED	0x08U			/* knote is detached */
 #define	KN_MARKER	0x10U			/* is a marker */
+#define KN_BUSY		0x20U			/* is being scanned */
 
 #define	kn_id		kn_kevent.ident
 #define	kn_filter	kn_kevent.filter

Reply via email to