> 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;
> 
> 

Reply via email to