> Date: Thu, 2 Nov 2017 14:29:30 +0200 > From: Paul Irofti <p...@irofti.net> > > Hi, > > Here is a backport from FreeBSD to support EVFILT_USER events. > > %------------------------------------------------------------------------------- > EVFILT_USER Establishes a user event identified by ident which is > not associated with any kernel mechanism but is trig- > gered by user level code. The lower 24 bits of the > fflags may be used for user defined flags and manipu- > lated using the following: > > NOTE_FFNOP Ignore the input fflags. > > NOTE_FFAND Bitwise AND fflags. > > NOTE_FFOR Bitwise OR fflags. > > NOTE_FFCOPY Copy fflags. > > NOTE_FFCTRLMASK Control mask for fflags. > > NOTE_FFLAGSMASK User defined flag mask for > fflags. > > A user event is triggered for output with the follow- > ing: > > NOTE_TRIGGER Cause the event to be triggered. > > On return, fflags contains the users defined flags in > the lower 24 bits. > %------------------------------------------------------------------------------- > > I can split the diff in two if needed: first add the touch > functionality and then the EVFILT_USER support on top. > > Thoughts?
I think you need a very good motivation for introducing such functionality in OpenBSD. > Index: sys/event.h > =================================================================== > RCS file: /cvs/src/sys/sys/event.h,v > retrieving revision 1.26 > diff -u -p -u -p -r1.26 event.h > --- sys/event.h 26 Jun 2017 09:32:32 -0000 1.26 > +++ sys/event.h 2 Nov 2017 11:58:11 -0000 > @@ -38,8 +38,9 @@ > #define EVFILT_PROC (-5) /* attached to struct process */ > #define EVFILT_SIGNAL (-6) /* attached to struct process */ > #define EVFILT_TIMER (-7) /* timers */ > +#define EVFILT_USER (-8) /* User events */ > > -#define EVFILT_SYSCOUNT 7 > +#define EVFILT_SYSCOUNT 8 > > #define EV_SET(kevp_, a, b, c, d, e, f) do { \ > struct kevent *kevp = (kevp_); \ > @@ -80,6 +81,25 @@ struct kevent { > #define EV_ERROR 0x4000 /* error, data contains errno */ > > /* > + * data/hint flags/masks for EVFILT_USER, shared with userspace > + * > + * On input, the top two bits of fflags specifies how the lower twenty four > + * bits should be applied to the stored value of fflags. > + * > + * On output, the top two bits will always be set to NOTE_FFNOP and the > + * remaining twenty four bits will contain the stored fflags value. > + */ > +#define NOTE_FFNOP 0x00000000 /* ignore input fflags */ > +#define NOTE_FFAND 0x40000000 /* AND fflags */ > +#define NOTE_FFOR 0x80000000 /* OR fflags */ > +#define NOTE_FFCOPY 0xc0000000 /* copy fflags */ > +#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations > */ > +#define NOTE_FFLAGSMASK 0x00ffffff > + > +#define NOTE_TRIGGER 0x01000000 /* Cause the event to be > + triggered for output. */ > + > +/* > * hint flag for in-kernel use - must not equal any existing note > */ > #ifdef _KERNEL > @@ -142,11 +162,22 @@ SLIST_HEAD(klist, knote); > */ > #define NOTE_SIGNAL 0x08000000 > > +/* > + * Hint values for the optional f_touch event filter. If f_touch is not set > + * to NULL and f_isfd is zero the f_touch filter will be called with the type > + * argument set to EVENT_REGISTER during a kevent() system call. It is also > + * called under the same conditions with the type argument set to > EVENT_PROCESS > + * when the event has been triggered. > + */ > +#define EVENT_REGISTER 1 > +#define EVENT_PROCESS 2 > + > struct filterops { > int f_isfd; /* true if ident == filedescriptor */ > int (*f_attach)(struct knote *kn); > void (*f_detach)(struct knote *kn); > int (*f_event)(struct knote *kn, long hint); > + void (*f_touch)(struct knote *kn, struct kevent *kev, long type); > }; > > struct knote { > @@ -164,6 +195,7 @@ struct knote { > } kn_ptr; > const struct filterops *kn_fop; > void *kn_hook; > + int kn_hookid; > #define KN_ACTIVE 0x01 /* event has been triggered */ > #define KN_QUEUED 0x02 /* event is on queue */ > #define KN_DISABLED 0x04 /* event is disabled */ > Index: kern/kern_event.c > =================================================================== > RCS file: /cvs/src/sys/kern/kern_event.c,v > retrieving revision 1.81 > diff -u -p -u -p -r1.81 kern_event.c > --- kern/kern_event.c 11 Oct 2017 08:06:56 -0000 1.81 > +++ kern/kern_event.c 2 Nov 2017 11:58:11 -0000 > @@ -2,6 +2,7 @@ > > /*- > * Copyright (c) 1999,2000,2001 Jonathan Lemon <jle...@freebsd.org> > + * Copyright (c) 2009 Apple, Inc. > * All rights reserved. > * > * Redistribution and use in source and binary forms, with or without > @@ -98,6 +99,11 @@ int filt_timerattach(struct knote *kn); > void filt_timerdetach(struct knote *kn); > int filt_timer(struct knote *kn, long hint); > void filt_seltruedetach(struct knote *kn); > +int filt_userattach(struct knote *kn); > +void filt_userdetach(struct knote *kn); > +int filt_user(struct knote *kn, long hint); > +void filt_usertouch(struct knote *kn, struct kevent *kev, long type); > + > > struct filterops kqread_filtops = > { 1, NULL, filt_kqdetach, filt_kqueue }; > @@ -107,6 +113,9 @@ struct filterops file_filtops = > { 1, filt_fileattach, NULL, NULL }; > struct filterops timer_filtops = > { 0, filt_timerattach, filt_timerdetach, filt_timer }; > +struct filterops user_filtops = > + { 0, filt_userattach, filt_userdetach, filt_user, filt_usertouch }; > + > > struct pool knote_pool; > struct pool kqueue_pool; > @@ -137,6 +146,7 @@ struct filterops *sysfilt_ops[] = { > &proc_filtops, /* EVFILT_PROC */ > &sig_filtops, /* EVFILT_SIGNAL */ > &timer_filtops, /* EVFILT_TIMER */ > + &user_filtops, /* EVFILT_USER */ > }; > > void KQREF(struct kqueue *); > @@ -383,6 +393,93 @@ filt_timer(struct knote *kn, long hint) > return (kn->kn_data != 0); > } > > +int > +filt_userattach(struct knote *kn) > +{ > + > + /* > + * EVFILT_USER knotes are not attached to anything in the kernel. > + */ > + kn->kn_hook = NULL; > + if (kn->kn_fflags & NOTE_TRIGGER) > + kn->kn_hookid = 1; > + else > + kn->kn_hookid = 0; > + return (0); > +} > + > +void > +filt_userdetach(__unused struct knote *kn) > +{ > + > + /* > + * EVFILT_USER knotes are not attached to anything in the kernel. > + */ > +} > + > +int > +filt_user(struct knote *kn, __unused long hint) > +{ > + > + return (kn->kn_hookid); > +} > + > +void > +filt_usertouch(struct knote *kn, struct kevent *kev, long type) > +{ > + int ffctrl; > + > + switch (type) { > + case EVENT_REGISTER: > + if (kev->fflags & NOTE_TRIGGER) > + kn->kn_hookid = 1; > + > + ffctrl = kev->fflags & NOTE_FFCTRLMASK; > + kev->fflags &= NOTE_FFLAGSMASK; > + switch (ffctrl) { > + case NOTE_FFNOP: > + break; > + > + case NOTE_FFAND: > + kn->kn_sfflags &= kev->fflags; > + break; > + > + case NOTE_FFOR: > + kn->kn_sfflags |= kev->fflags; > + break; > + > + case NOTE_FFCOPY: > + kn->kn_sfflags = kev->fflags; > + break; > + > + default: > + /* XXX Return error? */ > + break; > + } > + kn->kn_sdata = kev->data; > + if (kev->flags & EV_CLEAR) { > + kn->kn_hookid = 0; > + kn->kn_data = 0; > + kn->kn_fflags = 0; > + } > + break; > + > + case EVENT_PROCESS: > + *kev = kn->kn_kevent; > + kev->fflags = kn->kn_sfflags; > + kev->data = kn->kn_sdata; > + if (kn->kn_flags & EV_CLEAR) { > + kn->kn_hookid = 0; > + kn->kn_data = 0; > + kn->kn_fflags = 0; > + } > + break; > + > + default: > + panic("filt_usertouch() - invalid type (%ld)", type); > + break; > + } > +} > > /* > * filt_seltrue: > @@ -556,7 +653,7 @@ kqueue_register(struct kqueue *kq, struc > struct filterops *fops = NULL; > struct file *fp = NULL; > struct knote *kn = NULL; > - int s, error = 0; > + int s, error = 0, event; > > if (kev->filter < 0) { > if (kev->filter + EVFILT_SYSCOUNT < 0) > @@ -603,17 +700,11 @@ kqueue_register(struct kqueue *kq, struc > } > } > > - if (kn == NULL && ((kev->flags & EV_ADD) == 0)) { > - error = ENOENT; > - goto done; > - } > - > /* > * kn now contains the matching knote, or NULL if no match > */ > - if (kev->flags & EV_ADD) { > - > - if (kn == NULL) { > + if (kn == NULL) { > + if (kev->flags & EV_ADD) { > kn = knote_alloc(); > if (kn == NULL) { > error = ENOMEM; > @@ -640,28 +731,46 @@ kqueue_register(struct kqueue *kq, struc > knote_drop(kn, p, fdp); > goto done; > } > + goto done_ev_add; > } else { > - /* > - * The user may change some filter values after the > - * initial EV_ADD, but doing so will not reset any > - * filters which have already been triggered. > - */ > - kn->kn_sfflags = kev->fflags; > - kn->kn_sdata = kev->data; > - kn->kn_kevent.udata = kev->udata; > + /* No matching knote and the EV_ADD flag is not set. */ > + error = ENOENT; > + goto done; > } > - > - s = splhigh(); > - if (kn->kn_fop->f_event(kn, 0)) > - KNOTE_ACTIVATE(kn); > - splx(s); > - > - } else if (kev->flags & EV_DELETE) { > + } > + if (kev->flags & EV_DELETE) { > kn->kn_fop->f_detach(kn); > knote_drop(kn, p, p->p_fd); > goto done; > } > > + /* > + * The user may change some filter values after the > + * initial EV_ADD, but doing so will not reset any > + * filters which have already been triggered. > + */ > + kn->kn_kevent.udata = kev->udata; > + if (!fops->f_isfd && fops->f_touch != NULL) { > + fops->f_touch(kn, kev, EVENT_REGISTER); > + } else { > + kn->kn_sfflags = kev->fflags; > + kn->kn_sdata = kev->data; > + } > + > + /* > + * We can get here with kn->kn_knlist == NULL. This can happen when > + * the initial attach event decides that the event is "completed" > + * already. i.e. filt_procattach is called on a zombie process. It > + * will call filt_proc which will remove it from the list, and NULL > + * kn_knlist. > + */ > +done_ev_add: > + event = kn->kn_fop->f_event(kn, 0); > + s = splhigh(); > + if (event) > + KNOTE_ACTIVATE(kn); > + splx(s); > + > if ((kev->flags & EV_DISABLE) && > ((kn->kn_status & KN_DISABLED) == 0)) { > s = splhigh(); > @@ -691,7 +800,7 @@ kqueue_scan(struct kqueue *kq, int maxev > struct kevent *kevp; > struct timeval atv, rtv, ttv; > struct knote *kn, marker; > - int s, count, timeout, nkev = 0, error = 0; > + int s, count, timeout, nkev = 0, error = 0, touch; > struct kevent kev[KQ_NEVENTS]; > > count = maxevents; > @@ -782,7 +891,12 @@ start: > kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE); > continue; > } > - *kevp = kn->kn_kevent; > + touch = (!kn->kn_fop->f_isfd && > + kn->kn_fop->f_touch != NULL); > + if (touch) > + kn->kn_fop->f_touch(kn, kevp, EVENT_PROCESS); > + else > + *kevp = kn->kn_kevent; > kevp++; > nkev++; > if (kn->kn_flags & EV_ONESHOT) { > @@ -793,8 +907,14 @@ start: > s = splhigh(); > } else if (kn->kn_flags & (EV_CLEAR | EV_DISPATCH)) { > if (kn->kn_flags & EV_CLEAR) { > - kn->kn_data = 0; > - kn->kn_fflags = 0; > + /* > + * Manually clear knotes who weren't > + * 'touch'ed. > + */ > + if (touch == 0) { > + kn->kn_data = 0; > + kn->kn_fflags = 0; > + } > } > if (kn->kn_flags & EV_DISPATCH) > kn->kn_status |= KN_DISABLED; > >