On 16/06/20(Tue) 06:18, Todd C. Miller wrote:
> On Tue, 16 Jun 2020 12:48:58 +0200, Martin Pieuchot wrote:
> 
> > The diff below implements DragonFly's approach of adding a new kind of
> > filter, EVFILT_EXCEPT, to report such conditions.  This extends the
> > existing kqueue interface which is questionable.  On the one hand this
> > allows userland programs to use kevent(2) to check for this conditions.
> > One the other hand this is not supported by any other BSD and thus non
> > standard.
> 
> Actually, it looks like macOS uses EVFILT_EXCEPT too.  They were
> the first OS to implement poll in terms of kqueue as far as I know.
> I don't think there is a problem extended kqueue with EVFILT_EXCEPT.
> 
> > In the tree there's two poll handlers that set the POLLPRI & POLLRDBAND
> > bits as illustrated by the diff below.
> >
> > Do we see value in this new type of filter?  Should I document it and
> > put it in?  Or should I restrict it to the __EV_POLL for now?  In the
> > latter case should we pick a different name and/or prefix it?
> 
> I think EVFILT_EXCEPT should be exposed to userland.  It is not our
> own invention and two other OSes support it.

Updated diff below addresses multiples comments and implements the
common functionalities of EVFILT_EXCEPT supported by both DragonFly
and xnu.  Changes compared to the previous version include:

- Introduction of NOTE_OOB which should be set in `fflags' according to
  other implementations
- Set `kn_data' to the value of `so_oobmark' for sockets.  This matches
  xnu behavior, the arbitrary value was also questioned by visa@.
- Adds a missing break pointed out by anton@
- Set NOTE_OOB for pty as well, this isn't supported by DragonFly nor
  xnu but I don't see the point of introducing something different.
- Document the new filter

Note that the last available version of Xnu available on github, from
late 2018, has the following behavior which this diff doesn't implement:

        If the read direction of the socket has shutdown, then
        the filter also sets EV_EOF in flags, and returns the
        socket error (if any) in fflags

ok?

Index: sys/kern/kern_event.c
===================================================================
RCS file: /cvs/src/sys/kern/kern_event.c,v
retrieving revision 1.139
diff -u -p -r1.139 kern_event.c
--- sys/kern/kern_event.c       15 Jun 2020 15:42:11 -0000      1.139
+++ sys/kern/kern_event.c       17 Jun 2020 09:35:42 -0000
@@ -158,6 +158,7 @@ const struct filterops *const sysfilt_op
        &sig_filtops,                   /* EVFILT_SIGNAL */
        &timer_filtops,                 /* EVFILT_TIMER */
        &file_filtops,                  /* EVFILT_DEVICE */
+       &file_filtops,                  /* EVFILT_EXCEPT */
 };
 
 void
Index: sys/kern/tty_pty.c
===================================================================
RCS file: /cvs/src/sys/kern/tty_pty.c,v
retrieving revision 1.100
diff -u -p -r1.100 tty_pty.c
--- sys/kern/tty_pty.c  15 Jun 2020 15:29:40 -0000      1.100
+++ sys/kern/tty_pty.c  17 Jun 2020 09:34:02 -0000
@@ -107,6 +107,7 @@ void        filt_ptcrdetach(struct knote *);
 int    filt_ptcread(struct knote *, long);
 void   filt_ptcwdetach(struct knote *);
 int    filt_ptcwrite(struct knote *, long);
+int    filt_ptcexcept(struct knote *, long);
 
 static struct pt_softc **ptyarralloc(int);
 static int check_pty(int);
@@ -719,6 +720,25 @@ filt_ptcwrite(struct knote *kn, long hin
        return (kn->kn_data > 0);
 }
 
+int
+filt_ptcexcept(struct knote *kn, long hint)
+{
+       struct pt_softc *pti = (struct pt_softc *)kn->kn_hook;
+       struct tty *tp;
+
+       tp = pti->pt_tty;
+       kn->kn_data = 0;
+
+       /* If in packet or user control mode, check for data. */
+       if (((pti->pt_flags & PF_PKT) && pti->pt_send) ||
+           ((pti->pt_flags & PF_UCNTL) && pti->pt_ucntl)) {
+               kn->kn_fflags |= NOTE_OOB;
+               kn->kn_data = 1;
+       }
+
+       return (kn->kn_data > 0);
+}
+
 const struct filterops ptcread_filtops = {
        .f_flags        = FILTEROP_ISFD,
        .f_attach       = NULL,
@@ -733,6 +753,13 @@ const struct filterops ptcwrite_filtops 
        .f_event        = filt_ptcwrite,
 };
 
+const struct filterops ptcexcept_filtops = {
+       .f_flags        = FILTEROP_ISFD,
+       .f_attach       = NULL,
+       .f_detach       = filt_ptcrdetach,
+       .f_event        = filt_ptcexcept,
+};
+
 int
 ptckqfilter(dev_t dev, struct knote *kn)
 {
@@ -748,6 +775,10 @@ ptckqfilter(dev_t dev, struct knote *kn)
        case EVFILT_WRITE:
                klist = &pti->pt_selw.si_note;
                kn->kn_fop = &ptcwrite_filtops;
+               break;
+       case EVFILT_EXCEPT:
+               klist = &pti->pt_selr.si_note;
+               kn->kn_fop = &ptcexcept_filtops;
                break;
        default:
                return (EINVAL);
Index: sys/kern/uipc_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.245
diff -u -p -r1.245 uipc_socket.c
--- sys/kern/uipc_socket.c      15 Jun 2020 15:29:40 -0000      1.245
+++ sys/kern/uipc_socket.c      17 Jun 2020 09:34:56 -0000
@@ -71,6 +71,7 @@ int   filt_soread(struct knote *kn, long h
 void   filt_sowdetach(struct knote *kn);
 int    filt_sowrite(struct knote *kn, long hint);
 int    filt_solisten(struct knote *kn, long hint);
+int    filt_soexcept(struct knote *kn, long hint);
 
 const struct filterops solisten_filtops = {
        .f_flags        = FILTEROP_ISFD,
@@ -93,6 +94,12 @@ const struct filterops sowrite_filtops =
        .f_event        = filt_sowrite,
 };
 
+const struct filterops soexcept_filtops = {
+       .f_flags        = FILTEROP_ISFD,
+       .f_attach       = NULL,
+       .f_detach       = filt_sordetach,
+       .f_event        = filt_soexcept,
+};
 
 #ifndef SOMINCONN
 #define SOMINCONN 80
@@ -2026,6 +2033,10 @@ soo_kqfilter(struct file *fp, struct kno
                kn->kn_fop = &sowrite_filtops;
                sb = &so->so_snd;
                break;
+       case EVFILT_EXCEPT:
+               kn->kn_fop = &soexcept_filtops;
+               sb = &so->so_rcv;
+               break;
        default:
                return (EINVAL);
        }
@@ -2141,6 +2152,25 @@ filt_solisten(struct knote *kn, long hin
                sounlock(so, s);
 
        return (kn->kn_data != 0);
+}
+
+int
+filt_soexcept(struct knote *kn, long hint)
+{
+       struct socket *so = kn->kn_fp->f_data;
+       int s, rv = 0;
+
+       if ((hint & NOTE_SUBMIT) == 0)
+               s = solock(so);
+       if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
+               kn->kn_fflags |= NOTE_OOB;
+               kn->kn_data -= so->so_oobmark;
+               rv = 1;
+       }
+       if ((hint & NOTE_SUBMIT) == 0)
+               sounlock(so, s);
+
+       return (rv);
 }
 
 #ifdef DDB
Index: sys/sys/event.h
===================================================================
RCS file: /cvs/src/sys/sys/event.h,v
retrieving revision 1.43
diff -u -p -r1.43 event.h
--- sys/sys/event.h     15 Jun 2020 15:42:11 -0000      1.43
+++ sys/sys/event.h     17 Jun 2020 09:29:39 -0000
@@ -39,6 +39,7 @@
 #define EVFILT_SIGNAL          (-6)    /* attached to struct process */
 #define EVFILT_TIMER           (-7)    /* timers */
 #define EVFILT_DEVICE          (-8)    /* devices */
+#define EVFILT_EXCEPT          (-9)    /* exceptional conditions */
 
 #define EVFILT_SYSCOUNT                8
 
@@ -85,6 +86,12 @@ struct kevent {
  */
 #define NOTE_LOWAT     0x0001                  /* low water mark */
 #define NOTE_EOF       0x0002                  /* return on EOF */
+
+/*
+ * data/hint flags for EVFILT_EXCEPT, shared with userspace and with
+ * EVFILT_{READ|WRITE}
+ */
+#define NOTE_OOB       0x0004                  /* OOB data on a socket */
 
 /*
  * data/hint flags for EVFILT_VNODE, shared with userspace
Index: lib/libc/sys/kqueue.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/kqueue.2,v
retrieving revision 1.40
diff -u -p -r1.40 kqueue.2
--- lib/libc/sys/kqueue.2       31 May 2020 03:49:44 -0000      1.40
+++ lib/libc/sys/kqueue.2       17 Jun 2020 09:26:15 -0000
@@ -310,6 +310,13 @@ enabled and there is any data to read;
 .Fa data
 contains the number of bytes available.
 .El
+.It Dv EVFILT_EXCEPT
+Takes a descriptor as the identifier, and returns whenever one of the
+specified exceptional conditions has occured on the descriptor.
+Conditions are specified in
+.Fa fflags .
+Currently, a filter can monitor the reception of out-of-band data with
+.Dv NOTE_OOB .
 .It Dv EVFILT_WRITE
 Takes a descriptor as the identifier, and returns whenever
 it is possible to write to the descriptor.

Reply via email to