Page MenuHomeFreeBSD

Activate triggered kevents when they are enabled
ClosedPublic

Authored by markj on Feb 17 2016, 4:23 AM.
Tags
None
Referenced Files
Unknown Object (File)
Sat, Dec 7, 8:00 AM
Unknown Object (File)
Nov 20 2024, 10:02 PM
Unknown Object (File)
Nov 18 2024, 7:42 AM
Unknown Object (File)
Nov 5 2024, 8:35 AM
Unknown Object (File)
Oct 29 2024, 6:16 PM
Unknown Object (File)
Oct 14 2024, 5:17 AM
Unknown Object (File)
Sep 24 2024, 5:17 PM
Unknown Object (File)
Sep 24 2024, 4:34 PM
Subscribers

Details

Summary

r274560 modified kevent_register() to avoid testing knotes for disabled
kevents, presumably as a micro-optimization. However, this check is done
before possibly enabling the disabled kevent with EV_ENABLE, which means
that the knote will not be activated by the kevent_register() call even
though the event is enabled when this call returns.

Note that a knote for disabled kevent may be activated independently of
a kevent_register() call; the problem occurs only when the event trigger
condition does not undergo a level transition while the event is
disabled.

This change also modifies an existing kqueue test to exercise this case.

Test Plan

kqueue tests pass.

This little program demonstrates the problem: https://people.freebsd.org/~markj/prs/206368/repro.c

Without the fix, the second kevent(2) call hangs.

Diff Detail

Repository
rS FreeBSD src repository - subversion
Lint
Lint Passed
Unit
No Test Coverage
Build Status
Buildable 2552
Build 2569: arc lint + arc unit

Event Timeline

markj retitled this revision from to Activate triggered kevents when they are enabled.
markj edited the test plan for this revision. (Show Details)
markj updated this object.
markj added a reviewer: kib.
markj added a subscriber: ngie.
ngie added a reviewer: ngie.
This revision is now accepted and ready to land.Feb 17 2016, 5:21 AM
hiren added a reviewer: hiren.
hiren added a subscriber: hiren.

Thanks for picking this up.

kib edited edge metadata.

Note that this works in very round-about way. The KN_DISABLED flag is still set while if (event) KNOTE_ACTIVATE(kn); is done. As result, the knote is not enqueued. But later, where the check for EV_ENABLE && KN_DISABLED is done, the active knote is put on queue and waiters are woken up. I am not sure how to restructure the code to make it less puzzling.

Also, should the case of EV_ENABLE and EV_DISABLE both set, checked somewhere ?

In D5307#113191, @kib wrote:

Note that this works in very round-about way. The KN_DISABLED flag is still set while if (event) KNOTE_ACTIVATE(kn); is done. As result, the knote is not enqueued. But later, where the check for EV_ENABLE && KN_DISABLED is done, the active knote is put on queue and waiters are woken up. I am not sure how to restructure the code to make it less puzzling.

Do you see a problem with checking for EV_ENABLE before calling kn_fop->f_event()? That is, something like the following:

if ((kev->flags & EV_DISABLE) != 0)
    kn->kn_status |= KN_DISABLED;
else if ((kev->flags & EV_ENABLE) != 0)
    kn->kn_status &= ~KN_DISABLED;

if ((kn->kn_status & KN_DISABLED) == 0)
    event = kn->kn_op->f_event(kn, 0);
else
    event = 0;

KQ_LOCK(kq);
if (event)
    KNOTE_ACTIVATE(kn, 1);
else if ((kn->kn_status & (KN_ACTIVE | KN_QUEUED | KN_DISABLED)) == KN_ACTIVE)
    knote_enqueue(kn);
kn->status &= ~(KN_INFLUX | KN_SCAN);
KN_LIST_UNLOCK(kn);
KQ_UNLOCK_FLUX(kq);

In particular, we must take care to enqueue an active knote when transitioning from disabled to enabled. In this snippet we now do so before clearing KN_INFLUX and before releasing the knote list lock, but I don't see any problems with that.

Also, should the case of EV_ENABLE and EV_DISABLE both set, checked somewhere ?

I was wondering about this too. I note that the combination is currently harmless - it seems that specifying EV_ENABLE|EV_DISABLE is equivalent to just EV_ENABLE. Hypothetical future refactoring may introduce in a bug in this case though, so it should probably be handled explicitly. The beginning of kevent_register() seems like the right place for this check.

In D5307#113329, @markj wrote:

Do you see a problem with checking for EV_ENABLE before calling kn_fop->f_event()? That is, something like the following:

I like this proposal.

markj edited edge metadata.
  • Check for the combination of EV_ENABLE and EV_DISABLE
  • Simplify queueing of active knotes in kqueue_register()
This revision now requires review to proceed.Feb 18 2016, 10:00 PM
kib edited edge metadata.
kib added inline comments.
sys/kern/kern_event.c
1125

Move this before initialization ? No need to zero out bunch of vars when not used due to error.

1338

If you replace KNOTE_ACTIVE() there with kn->kn_status |= KN_ACTIVE; and remove 'else' in the next line, then I think that the code would work the same way, with less indirections for reader to look up.

This revision is now accepted and ready to land.Feb 19 2016, 12:09 AM
markj added inline comments.
sys/kern/kern_event.c
1338

Indeed, I find that a bit clearer.

This revision was automatically updated to reflect the committed changes.
markj marked an inline comment as done.