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
This commit is contained in:
John-Mark Gurney 2014-11-16 01:18:41 +00:00
parent d61d836c6b
commit 2c30bc1fcf
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=274560
2 changed files with 40 additions and 17 deletions

View File

@ -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 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
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 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
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 @@ kqueue_register(struct kqueue *kq, struct kevent *kev, struct thread *td, int wa
* 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) &&

View File

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