release processing KN only when kq_scan has >0 max events. Because calling kevent with =0 max events doesn't mean all events are processed. This results in a subtle race of pre-releasing unprocessed knotes.

This commit is contained in:
Oscar Zhao 2020-05-05 04:14:30 -04:00
parent d535232e92
commit 61067bc214

View File

@ -2044,6 +2044,7 @@ kqueue_register(struct kqueue *kq, struct kevq *kevq, struct kevent *kev, struct
KQ_UNLOCK(kq); KQ_UNLOCK(kq);
knote_drop(kn, td); knote_drop(kn, td);
CTR3(KTR_KQ, "kqueue_register: kq %p deleted kn %p, fd %d", kq, kn, kev->ident);
goto done; goto done;
} }
@ -3068,11 +3069,6 @@ kqueue_scan(struct kevq *kevq, int maxevents, struct kevent_copyops *k_ops,
error = 0; error = 0;
haskqglobal = 0; haskqglobal = 0;
KEVQ_LOCK(kevq);
/* release processing knotes first */
kevq_rel_proc_kn(kevq);
KEVQ_UNLOCK(kevq);
// it's important that this is done before activate // it's important that this is done before activate
if (maxevents == 0) if (maxevents == 0)
goto done_nl; goto done_nl;
@ -3082,6 +3078,11 @@ kqueue_scan(struct kevq *kevq, int maxevents, struct kevent_copyops *k_ops,
kevq_activate(kevq, td); kevq_activate(kevq, td);
} }
KEVQ_LOCK(kevq);
/* release processing knotes first */
kevq_rel_proc_kn(kevq);
KEVQ_UNLOCK(kevq);
/* adjust max events according to the target frequency */ /* adjust max events according to the target frequency */
if ((kq->kq_flags & KQ_FLAG_MULTI) && kq->kq_tfreq > 0 && kevq->kevq_avg_lat > 0) { if ((kq->kq_flags & KQ_FLAG_MULTI) && kq->kq_tfreq > 0 && kevq->kevq_avg_lat > 0) {
/* expected events per syscall /* expected events per syscall
@ -4199,7 +4200,7 @@ knote_activate(struct knote *kn)
KQ_NOTOWNED(kq); KQ_NOTOWNED(kq);
CTR2(KTR_KQ, "knote_activate: kn %p, flags %d", kn, kn->kn_status); CTR3(KTR_KQ, "knote_activate: kn %p, fd %d, flags %d", kn, kn->kn_id, kn->kn_status);
KN_FLUX_NOTOWNED(kn); KN_FLUX_NOTOWNED(kn);
KASSERT(kn_in_flux(kn), ("knote %p not in flux", kn)); KASSERT(kn_in_flux(kn), ("knote %p not in flux", kn));