Only process EV_DISPATCH and EV_ONESHOT when we get a valid event

If the copyout callback for a filter indicates that the event should be ignored/dropped, we should not process EV_DISPATCH or EV_ONESHOT, as this would lead to missing actual events in the future.

Some filters (e.g. EVFILT_PROC) do specifically want to process EV_ONESHOT even when dropping an event, so this also introduces a special filter ID (EVFILT_DROP_POSTPROCESS) that enables the old behavior for individual events. Additionally, rather than just using `0` as a special filter ID, this commit introduces EVFILT_DROP (to be clear about its meaning).
This commit is contained in:
Ariel Abreu 2022-02-25 16:12:17 -05:00
parent bcef9ea04b
commit 76d8aa3dce
No known key found for this signature in database
GPG Key ID: D67AE16CCEA85B70
7 changed files with 36 additions and 29 deletions

View File

@ -62,7 +62,7 @@ knote_release(struct knote *kn)
if (/*kn->kn_flags & KNFL_KNOTE_DELETED*/ 1) {
dbg_printf("freeing knote at %p (delayed)", kn);
LIST_INSERT_HEAD(&kn->kn_kq->kq_tofree, kn, kn_entries2free);
kn->kev.filter = 0;
kn->kev.filter = EVFILT_DROP;
} else {
dbg_puts("this should never happen");
}

View File

@ -75,6 +75,9 @@ struct eventfd {
#define KNFL_PASSIVE_SOCKET (0x01) /* Socket is in listen(2) mode */
#define KNFL_REGULAR_FILE (0x02) /* File descriptor is a regular file */
#define KNFL_KNOTE_DELETED (0x10) /* The knote object is no longer valid */
#define EVFILT_DROP 0
#define EVFILT_DROP_POSTPROCESS (EVFILT_SYSCOUNT + 1)
struct knote {
struct kevent_internal_s kev;

View File

@ -86,7 +86,7 @@ evfilt_machport_copyout(struct kevent64_s *dst, struct knote *src, void *ptr)
if (reply.header.code == 0xdead) {
// server indicated there was actually no event available to read right now;
// drop the event
dst->filter = 0;
dst->filter = EVFILT_DROP;
return 0;
}
@ -123,15 +123,13 @@ evfilt_machport_knote_create(struct filter *filt, struct knote *kn)
ev.events = kn->data.events;
ev.data.ptr = kn;
kn->kdata.kn_dupfd = eventfd(0, EFD_CLOEXEC);
int status = _dserver_rpc_kqchan_mach_port_open_4libkqueue(port, (void*)kn->kev.ext[0], kn->kev.ext[1], kn->kev.fflags, &kn->kdata.kn_dupfd);
if (status < 0) {
dbg_printf("evfilt_machport_open: %s", strerror(-status));
return (-1);
}
dbg_printf("evfilt_machport_open: listening to FD %d", kn->kdata.kn_dupfd);
dbg_printf("evfilt_machport_open: listening to FD %d for events %d", kn->kdata.kn_dupfd, ev.events);
fcntl(kn->kdata.kn_dupfd, F_SETFD, FD_CLOEXEC);
@ -209,6 +207,8 @@ evfilt_machport_knote_enable(struct filter *filt, struct knote *kn)
ev.events = kn->data.events;
ev.data.ptr = kn;
dbg_printf("enabling machport knote with ID=%llu for events %d", kn->kev.ident, ev.events);
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_ADD, kn->kdata.kn_dupfd, &ev) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);
@ -219,6 +219,7 @@ evfilt_machport_knote_enable(struct filter *filt, struct knote *kn)
int
evfilt_machport_knote_disable(struct filter *filt, struct knote *kn)
{
dbg_printf("disable machport knote with ID=%llu", kn->kev.ident);
if (epoll_ctl(kn->kn_epollfd, EPOLL_CTL_DEL, kn->kdata.kn_dupfd, NULL) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);

View File

@ -193,45 +193,47 @@ linux_kevent_copyout(struct kqueue *kq, int nready,
struct filter *filt;
struct knote *kn;
int i, nret, rv;
struct kevent64_s* event;
nret = nready;
for (i = 0; i < nready; i++) {
ev = &epevt_get()[i];
kn = (struct knote *) ev->data.ptr;
event = eventlist;
if (kn->kev.filter == 0) {
dbg_puts("kevent copyout for zero filter, discarding!");
nret--;
continue;
}
filt = &kq->kq_filt[~(kn->kev.filter)];
rv = filt->kf_copyout(eventlist, kn, ev);
rv = filt->kf_copyout(event, kn, ev);
if (slowpath(rv < 0)) {
dbg_puts("knote_copyout failed");
/* XXX-FIXME: hard to handle this without losing events */
abort();
}
/*
* Certain flags cause the associated knote to be deleted
* or disabled.
*/
if (eventlist->flags & EV_DISPATCH)
knote_disable(filt, kn); //FIXME: Error checking
if (eventlist->flags & EV_ONESHOT) {
knote_delete(filt, kn); //FIXME: Error checking
}
/* If an empty kevent structure is returned, the event is discarded. */
/* TODO: add these semantics to windows + solaris platform.c */
/* EV_DELETE is an internal trick to not have this event passed to the app.
* Typically used along with EV_ONESHOT - see linux/proc.c
*/
if (fastpath(eventlist->filter != 0) && !(eventlist->flags & EV_DELETE)) {
if (fastpath(event->filter != EVFILT_DROP && event->filter != EVFILT_DROP_POSTPROCESS)) {
eventlist++;
} else {
dbg_puts("spurious wakeup, discarding event");
nret--;
if (event->filter != EVFILT_DROP_POSTPROCESS) {
continue;
}
}
/*
* Certain flags cause the associated knote to be deleted
* or disabled.
*/
if (event->flags & EV_DISPATCH)
knote_disable(filt, kn); //FIXME: Error checking
if (event->flags & EV_ONESHOT) {
knote_delete(filt, kn); //FIXME: Error checking
}
}

View File

@ -94,7 +94,7 @@ evfilt_proc_copyout(struct kevent64_s *dst, struct knote *src, void *ptr)
if (reply.header.code == 0xdead) {
// server indicated there was actually no event available to read right now;
// drop the event
dst->filter = 0;
dst->filter = EVFILT_DROP;
return 0;
}
@ -155,7 +155,7 @@ evfilt_proc_copyout(struct kevent64_s *dst, struct knote *src, void *ptr)
// don't deliver it to the program.
if (!(src->kev.fflags & NOTE_FORK))
dst->filter = 0; // drop event
dst->filter = EVFILT_DROP; // drop event
}
else if (dst->fflags & NOTE_EXIT)
{
@ -163,11 +163,12 @@ evfilt_proc_copyout(struct kevent64_s *dst, struct knote *src, void *ptr)
dst->flags |= EV_EOF | EV_ONESHOT;
// NOTE_EXIT is always announced so that we can
// remove the knote. Add EV_DELETE to avoid passing
// the event to the application if it is not interested
// in this event
// remove the knote. Avoid passing the event to
// the application if it is not interested in
// this event.
// We use EVFILT_DROP_POSTPROCESS to force the EV_ONESHOT to be processed.
if (!(src->kev.fflags & NOTE_EXIT))
dst->flags |= EV_DELETE;
dst->filter = EVFILT_DROP_POSTPROCESS; // drop event
}
return (0);

View File

@ -64,7 +64,7 @@ evfilt_read_copyout(struct kevent64_s *dst, struct knote *src, void *ptr)
dst->data = get_eof_offset(src->kev.ident);
if (dst->data == 0) {
dst->filter = 0; /* Will cause the kevent to be discarded */
dst->filter = EVFILT_DROP; /* Will cause the kevent to be discarded */
if (epoll_ctl(src->kn_epollfd, EPOLL_CTL_DEL, src->kdata.kn_eventfd, NULL) < 0) {
dbg_perror("epoll_ctl(2)");
return (-1);

View File

@ -174,7 +174,7 @@ evfilt_vnode_copyout(struct kevent64_s *dst, struct knote *src, void *ptr UNUSED
dbg_printf("inotify event: %s", inotify_event_dump(evt));
if (evt->mask & IN_IGNORED) {
/* TODO: possibly return error when fs is unmounted */
dst->filter = 0;
dst->filter = EVFILT_DROP;
return (0);
}
@ -184,7 +184,7 @@ scriptors reference the same file.
*/
if (evt->mask & IN_CLOSE_WRITE || evt->mask & IN_CLOSE_NOWRITE) {
src->kn_flags |= EV_ONESHOT; /* KLUDGE: causes the knote to be deleted */
dst->filter = 0; /* KLUDGE: causes the event to be discarded */
dst->filter = EVFILT_DROP_POSTPROCESS; /* KLUDGE: causes the event to be discarded */
return (0);
}