Skip site navigation (1)Skip section navigation (2)
Date:      Sun, 16 Nov 2014 01:18:41 +0000 (UTC)
From:      John-Mark Gurney <jmg@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r274560 - in head/sys: kern sys
Message-ID:  <201411160118.sAG1IfUh048213@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jmg
Date: Sun Nov 16 01:18:41 2014
New Revision: 274560
URL: https://svnweb.freebsd.org/changeset/base/274560

Log:
  prevent doing filter ops locking for staticly compiled filter ops...
  This significantly reduces lock contention when adding/removing knotes
  on busy multi-kq system...  Next step is to cache these references per
  kq.. i.e. kq refs it once and keeps a local ref count so that the same
  refs don't get accessed by many cpus...
  
  only allocate a knote when we might use it...
  
  Add a new flag, _FORCEONESHOT..  This allows a thread to force the
  delivery of another event in a safe manner, say waking up an idle http
  connection to force it to be reaped...
  
  If we are _DISABLE'ing a knote, don't bother to call f_event on it, it's
  disabled, so won't be delivered anyways..
  
  Tested by:	adrian

Modified:
  head/sys/kern/kern_event.c
  head/sys/sys/event.h

Modified: head/sys/kern/kern_event.c
==============================================================================
--- head/sys/kern/kern_event.c	Sun Nov 16 01:00:39 2014	(r274559)
+++ head/sys/kern/kern_event.c	Sun Nov 16 01:18:41 2014	(r274560)
@@ -281,19 +281,20 @@ MTX_SYSINIT(kqueue_filterops, &filterops
 	MTX_DEF);
 static struct {
 	struct filterops *for_fop;
+	int for_nolock;
 	int for_refcnt;
 } sysfilt_ops[EVFILT_SYSCOUNT] = {
-	{ &file_filtops },			/* EVFILT_READ */
-	{ &file_filtops },			/* EVFILT_WRITE */
+	{ &file_filtops, 1 },			/* EVFILT_READ */
+	{ &file_filtops, 1 },			/* EVFILT_WRITE */
 	{ &null_filtops },			/* EVFILT_AIO */
-	{ &file_filtops },			/* EVFILT_VNODE */
-	{ &proc_filtops },			/* EVFILT_PROC */
-	{ &sig_filtops },			/* EVFILT_SIGNAL */
-	{ &timer_filtops },			/* EVFILT_TIMER */
-	{ &file_filtops },			/* EVFILT_PROCDESC */
-	{ &fs_filtops },			/* EVFILT_FS */
+	{ &file_filtops, 1 },			/* EVFILT_VNODE */
+	{ &proc_filtops, 1 },			/* EVFILT_PROC */
+	{ &sig_filtops, 1 },			/* EVFILT_SIGNAL */
+	{ &timer_filtops, 1 },			/* EVFILT_TIMER */
+	{ &file_filtops, 1 },			/* EVFILT_PROCDESC */
+	{ &fs_filtops, 1 },			/* EVFILT_FS */
 	{ &null_filtops },			/* EVFILT_LIO */
-	{ &user_filtops },			/* EVFILT_USER */
+	{ &user_filtops, 1 },			/* EVFILT_USER */
 	{ &null_filtops },			/* EVFILT_SENDFILE */
 };
 
@@ -465,6 +466,10 @@ knote_fork(struct knlist *list, int pid)
 	list->kl_lock(list->kl_lockarg);
 
 	SLIST_FOREACH(kn, &list->kl_list, kn_selnext) {
+		/*
+		 * XXX - Why do we skip the kn if it is _INFLUX?  Does this
+		 * mean we will not properly wake up some notes?
+		 */
 		if ((kn->kn_status & KN_INFLUX) == KN_INFLUX)
 			continue;
 		kq = kn->kn_kq;
@@ -1002,6 +1007,9 @@ kqueue_fo_find(int filt)
 	if (filt > 0 || filt + EVFILT_SYSCOUNT < 0)
 		return NULL;
 
+	if (sysfilt_ops[~filt].for_nolock)
+		return sysfilt_ops[~filt].for_fop;
+
 	mtx_lock(&filterops_lock);
 	sysfilt_ops[~filt].for_refcnt++;
 	if (sysfilt_ops[~filt].for_fop == NULL)
@@ -1018,6 +1026,9 @@ kqueue_fo_release(int filt)
 	if (filt > 0 || filt + EVFILT_SYSCOUNT < 0)
 		return;
 
+	if (sysfilt_ops[~filt].for_nolock)
+		return;
+
 	mtx_lock(&filterops_lock);
 	KASSERT(sysfilt_ops[~filt].for_refcnt > 0,
 	    ("filter object refcount not valid on release"));
@@ -1051,7 +1062,10 @@ kqueue_register(struct kqueue *kq, struc
 	if (fops == NULL)
 		return EINVAL;
 
-	tkn = knote_alloc(waitok);		/* prevent waiting with locks */
+	if (kev->flags & EV_ADD)
+		tkn = knote_alloc(waitok);	/* prevent waiting with locks */
+	else
+		tkn = NULL;
 
 findkn:
 	if (fops->f_isfd) {
@@ -1162,7 +1176,7 @@ findkn:
 			kev->data = 0;
 			kn->kn_kevent = *kev;
 			kn->kn_kevent.flags &= ~(EV_ADD | EV_DELETE |
-			    EV_ENABLE | EV_DISABLE);
+			    EV_ENABLE | EV_DISABLE | EV_FORCEONESHOT);
 			kn->kn_status = KN_INFLUX|KN_DETACHED;
 
 			error = knote_attach(kn, kq);
@@ -1195,6 +1209,11 @@ findkn:
 		goto done;
 	}
 
+	if (kev->flags & EV_FORCEONESHOT) {
+		kn->kn_flags |= EV_ONESHOT;
+		KNOTE_ACTIVATE(kn, 1);
+	}
+
 	/*
 	 * The user may change some filter values after the initial EV_ADD,
 	 * but doing so will not reset any filter which has already been
@@ -1219,18 +1238,21 @@ findkn:
 	 * kn_knlist.
 	 */
 done_ev_add:
-	event = kn->kn_fop->f_event(kn, 0);
+	if ((kev->flags & EV_DISABLE) &&
+	    ((kn->kn_status & KN_DISABLED) == 0)) {
+		kn->kn_status |= KN_DISABLED;
+	}
+
+	if ((kn->kn_status & KN_DISABLED) == 0)
+		event = kn->kn_fop->f_event(kn, 0);
+	else
+		event = 0;
 	KQ_LOCK(kq);
 	if (event)
 		KNOTE_ACTIVATE(kn, 1);
 	kn->kn_status &= ~(KN_INFLUX | KN_SCAN);
 	KN_LIST_UNLOCK(kn);
 
-	if ((kev->flags & EV_DISABLE) &&
-	    ((kn->kn_status & KN_DISABLED) == 0)) {
-		kn->kn_status |= KN_DISABLED;
-	}
-
 	if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) {
 		kn->kn_status &= ~KN_DISABLED;
 		if ((kn->kn_status & KN_ACTIVE) &&

Modified: head/sys/sys/event.h
==============================================================================
--- head/sys/sys/event.h	Sun Nov 16 01:00:39 2014	(r274559)
+++ head/sys/sys/event.h	Sun Nov 16 01:18:41 2014	(r274560)
@@ -69,6 +69,7 @@ struct kevent {
 #define EV_DELETE	0x0002		/* delete event from kq */
 #define EV_ENABLE	0x0004		/* enable event */
 #define EV_DISABLE	0x0008		/* disable event (not reported) */
+#define EV_FORCEONESHOT	0x0100		/* enable _ONESHOT and force trigger */
 
 /* flags */
 #define EV_ONESHOT	0x0010		/* only report one occurrence */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201411160118.sAG1IfUh048213>