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); +} +