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:
parent
d06e06b5c3
commit
39fa4746e5
@ -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) &&
|
||||
|
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user