Last year we added the new EVFILT_EXCEPT filter type to kqueue in
order to report conditions currently available via POLLPRI/POLLRDBAND
in poll(2) and select(2).

This new filter has been implemented in tty and socket by re-using the
existing kqueue's "read" filter.  This has a downside which is the filter
will also trigger if any data is available for reading.

This "feature" makes it impossible to correctly implement poll(2)'s
"empty" condition mode.  If no bit are set in the `events' pollfd
structure we still need to return POLLHUP.  But if the filter triggers
when there's data to read, it means POLLIN not POLLHUP.

So I'd like to change the existing EVFILT_EXCEPT filters to no longer
fire if there is something to read.  Diff below does that and adds a
new filter for FIFOs necessary for poll(2) support.

Ok?

Index: kern/tty_pty.c
===================================================================
RCS file: /cvs/src/sys/kern/tty_pty.c,v
retrieving revision 1.108
diff -u -p -r1.108 tty_pty.c
--- kern/tty_pty.c      8 Feb 2021 09:18:30 -0000       1.108
+++ kern/tty_pty.c      22 Oct 2021 12:49:12 -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);
@@ -670,16 +671,6 @@ filt_ptcread(struct knote *kn, long hint
        tp = pti->pt_tty;
        kn->kn_data = 0;
 
-       if (kn->kn_sfflags & NOTE_OOB) {
-               /* 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 (1);
-               }
-               return (0);
-       }
        if (ISSET(tp->t_state, TS_ISOPEN)) {
                if (!ISSET(tp->t_state, TS_TTSTOP))
                        kn->kn_data = tp->t_outq.c_cc;
@@ -731,6 +722,34 @@ 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;
+
+       if (kn->kn_sfflags & NOTE_OOB) {
+               /* 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 (1);
+               }
+               return (0);
+       }
+       if (!ISSET(tp->t_state, TS_CARR_ON)) {
+               kn->kn_flags |= EV_EOF;
+               if (kn->kn_flags & __EV_POLL)
+                       kn->kn_flags |= __EV_HUP;
+               return (1);
+       }
+
+       return (0);
+}
+
 const struct filterops ptcread_filtops = {
        .f_flags        = FILTEROP_ISFD,
        .f_attach       = NULL,
@@ -749,7 +768,7 @@ const struct filterops ptcexcept_filtops
        .f_flags        = FILTEROP_ISFD,
        .f_attach       = NULL,
        .f_detach       = filt_ptcrdetach,
-       .f_event        = filt_ptcread,
+       .f_event        = filt_ptcexcept,
 };
 
 int
Index: kern/uipc_socket.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.265
diff -u -p -r1.265 uipc_socket.c
--- kern/uipc_socket.c  14 Oct 2021 23:05:10 -0000      1.265
+++ kern/uipc_socket.c  22 Oct 2021 12:49:12 -0000
@@ -78,6 +78,10 @@ int  filt_sowrite(struct knote *kn, long 
 int    filt_sowritemodify(struct kevent *kev, struct knote *kn);
 int    filt_sowriteprocess(struct knote *kn, struct kevent *kev);
 int    filt_sowrite_common(struct knote *kn, struct socket *so);
+int    filt_soexcept(struct knote *kn, long hint);
+int    filt_soexceptmodify(struct kevent *kev, struct knote *kn);
+int    filt_soexceptprocess(struct knote *kn, struct kevent *kev);
+int    filt_soexcept_common(struct knote *kn, struct socket *so);
 int    filt_solisten(struct knote *kn, long hint);
 int    filt_solistenmodify(struct kevent *kev, struct knote *kn);
 int    filt_solistenprocess(struct knote *kn, struct kevent *kev);
@@ -114,9 +118,9 @@ const struct filterops soexcept_filtops 
        .f_flags        = FILTEROP_ISFD,
        .f_attach       = NULL,
        .f_detach       = filt_sordetach,
-       .f_event        = filt_soread,
-       .f_modify       = filt_soreadmodify,
-       .f_process      = filt_soreadprocess,
+       .f_event        = filt_soexcept,
+       .f_modify       = filt_soexceptmodify,
+       .f_process      = filt_soexceptprocess,
 };
 
 #ifndef SOMINCONN
@@ -2089,13 +2093,7 @@ filt_soread_common(struct knote *kn, str
                rv = 0;
        } else
 #endif /* SOCKET_SPLICE */
-       if (kn->kn_sfflags & NOTE_OOB) {
-               if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
-                       kn->kn_fflags |= NOTE_OOB;
-                       kn->kn_data -= so->so_oobmark;
-                       rv = 1;
-               }
-       } else if (so->so_state & SS_CANTRCVMORE) {
+       if (so->so_state & SS_CANTRCVMORE) {
                kn->kn_flags |= EV_EOF;
                if (kn->kn_flags & __EV_POLL) {
                        if (so->so_state & SS_ISDISCONNECTED)
@@ -2227,6 +2225,77 @@ filt_sowriteprocess(struct knote *kn, st
                rv = 1;
        else
                rv = filt_sowrite_common(kn, so);
+       if (rv != 0)
+               knote_submit(kn, kev);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_soexcept_common(struct knote *kn, struct socket *so)
+{
+       int rv = 0;
+
+       soassertlocked(so);
+
+#ifdef SOCKET_SPLICE
+       if (isspliced(so)) {
+               rv = 0;
+       } else
+#endif /* SOCKET_SPLICE */
+       if (kn->kn_sfflags & NOTE_OOB) {
+               if (so->so_oobmark || (so->so_state & SS_RCVATMARK)) {
+                       kn->kn_fflags |= NOTE_OOB;
+                       kn->kn_data -= so->so_oobmark;
+                       rv = 1;
+               }
+       } else if (so->so_state & SS_CANTRCVMORE) {
+               kn->kn_flags |= EV_EOF;
+               if (kn->kn_flags & __EV_POLL) {
+                       if (so->so_state & SS_ISDISCONNECTED)
+                               kn->kn_flags |= __EV_HUP;
+               }
+               kn->kn_fflags = so->so_error;
+               rv = 1;
+       }
+
+       return rv;
+}
+
+int
+filt_soexcept(struct knote *kn, long hint)
+{
+       struct socket *so = kn->kn_fp->f_data;
+
+       return (filt_soexcept_common(kn, so));
+}
+
+int
+filt_soexceptmodify(struct kevent *kev, struct knote *kn)
+{
+       struct socket *so = kn->kn_fp->f_data;
+       int rv, s;
+
+       s = solock(so);
+       knote_modify(kev, kn);
+       rv = filt_soexcept_common(kn, so);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_soexceptprocess(struct knote *kn, struct kevent *kev)
+{
+       struct socket *so = kn->kn_fp->f_data;
+       int rv, s;
+
+       s = solock(so);
+       if (kev != NULL && (kn->kn_flags & EV_ONESHOT))
+               rv = 1;
+       else
+               rv = filt_soexcept_common(kn, so);
        if (rv != 0)
                knote_submit(kn, kev);
        sounlock(so, s);
Index: miscfs/fifofs/fifo_vnops.c
===================================================================
RCS file: /cvs/src/sys/miscfs/fifofs/fifo_vnops.c,v
retrieving revision 1.82
diff -u -p -r1.82 fifo_vnops.c
--- miscfs/fifofs/fifo_vnops.c  15 Oct 2021 06:30:06 -0000      1.82
+++ miscfs/fifofs/fifo_vnops.c  22 Oct 2021 13:06:17 -0000
@@ -112,6 +112,10 @@ int        filt_fifowrite(struct knote *kn, lon
 int    filt_fifowritemodify(struct kevent *kev, struct knote *kn);
 int    filt_fifowriteprocess(struct knote *kn, struct kevent *kev);
 int    filt_fifowrite_common(struct knote *kn, struct socket *so);
+int    filt_fifoexcept(struct knote *kn, long hint);
+int    filt_fifoexceptmodify(struct kevent *kev, struct knote *kn);
+int    filt_fifoexceptprocess(struct knote *kn, struct kevent *kev);
+int    filt_fifoexcept_common(struct knote *kn, struct socket *so);
 
 const struct filterops fiforead_filtops = {
        .f_flags        = FILTEROP_ISFD,
@@ -131,6 +135,15 @@ const struct filterops fifowrite_filtops
        .f_process      = filt_fifowriteprocess,
 };
 
+const struct filterops fifoexcept_filtops = {
+       .f_flags        = FILTEROP_ISFD,
+       .f_attach       = NULL,
+       .f_detach       = filt_fifordetach,
+       .f_event        = filt_fifoexcept,
+       .f_modify       = filt_fifoexceptmodify,
+       .f_process      = filt_fifoexceptprocess,
+};
+
 /*
  * Open called to set up a new instance of a fifo or
  * to find an active instance of a fifo.
@@ -522,6 +535,11 @@ fifo_kqfilter(void *v)
                so = fip->fi_writesock;
                sb = &so->so_snd;
                break;
+       case EVFILT_EXCEPT:
+               ap->a_kn->kn_fop = &fifoexcept_filtops;
+               so = fip->fi_readsock;
+               sb = &so->so_rcv;
+               break;
        default:
                return (EINVAL);
        }
@@ -670,3 +688,63 @@ filt_fifowriteprocess(struct knote *kn, 
 
        return (rv);
 }
+
+int
+filt_fifoexcept_common(struct knote *kn, struct socket *so)
+{
+       int rv = 0;
+
+       soassertlocked(so);
+
+       if (so->so_state & SS_CANTRCVMORE) {
+               kn->kn_flags |= EV_EOF;
+               if (kn->kn_flags & __EV_POLL) {
+                       if (so->so_state & SS_ISDISCONNECTED)
+                               kn->kn_flags |= __EV_HUP;
+               }
+               rv = 1;
+       }
+
+       return (rv);
+}
+
+int
+filt_fifoexcept(struct knote *kn, long hint)
+{
+       struct socket *so = kn->kn_hook;
+
+       return (filt_fifoexcept_common(kn, so));
+}
+
+int
+filt_fifoexceptmodify(struct kevent *kev, struct knote *kn)
+{
+       struct socket *so = kn->kn_hook;
+       int rv, s;
+
+       s = solock(so);
+       knote_modify(kev, kn);
+       rv = filt_fifoexcept_common(kn, so);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_fifoexceptprocess(struct knote *kn, struct kevent *kev)
+{
+       struct socket *so = kn->kn_hook;
+       int rv, s;
+
+       s = solock(so);
+       if (kev != NULL && (kn->kn_flags & EV_ONESHOT))
+               rv = 1;
+       else
+               rv = filt_fifoexcept_common(kn, so);
+       if (rv != 0)
+               knote_submit(kn, kev);
+       sounlock(so, s);
+
+       return (rv);
+}
+

Reply via email to