aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn-Mark Gurney <jmg@FreeBSD.org>2014-11-16 01:18:41 +0000
committerJohn-Mark Gurney <jmg@FreeBSD.org>2014-11-16 01:18:41 +0000
commit2c30bc1fcfd4b11dc9dc663ec5a5f5c466d00615 (patch)
treec9eb370b66ee486fed9d9d7877b3e0129e7a8ecf
parentd61d836c6b5004fc55a25b8556d34585dde18483 (diff)
downloadsrc-2c30bc1fcfd4b11dc9dc663ec5a5f5c466d00615.tar.gz
src-2c30bc1fcfd4b11dc9dc663ec5a5f5c466d00615.zip
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
Notes
Notes: svn path=/head/; revision=274560
-rw-r--r--sys/kern/kern_event.c56
-rw-r--r--sys/sys/event.h1
2 files changed, 40 insertions, 17 deletions
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
index 55bffe77a9a6..e01f12c7d1b2 100644
--- a/sys/kern/kern_event.c
+++ b/sys/kern/kern_event.c
@@ -281,19 +281,20 @@ MTX_SYSINIT(kqueue_filterops, &filterops_lock, "protect sysfilt_ops",
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, struct kevent *kev, struct thread *td, int wa
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) &&
diff --git a/sys/sys/event.h b/sys/sys/event.h
index c712f76ac34c..089c9b3874f0 100644
--- a/sys/sys/event.h
+++ b/sys/sys/event.h
@@ -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 */