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.