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>