On Thu, May 20, 2021 at 11:35:32AM +0200, Martin Pieuchot wrote:
> On 18/05/21(Tue) 14:22, Visa Hankala wrote:
> > This diff adds f_modify and f_process callbacks to socket event filters.
> > As a result, socket events are handled using the non-legacy paths in
> > filter_modify() and filter_process() of kern_event.c This a step toward
> > MP-safety. However, everything still runs under the kernel lock.
> > 
> > The change has three intended effects:
> > 
> > * Socket events are handled without raising the system priority level.
> >   This makes the activity observable with btrace(8).
> > 
> > * kqueue itself no longer calls f_event of socket filterops, which
> >   allows replacing the conditional, NOTE_SUBMIT-based locking with
> >   a fixed call pattern.
> 
> I love this.
> 
> > * The state of a socket event is now always rechecked before delivery
> >   to user. Before, the recheck was skipped if the event was registered
> >   with EV_ONESHOT.
> 
> To me this sounds sane.  I can't think of a way to rely on the current
> behavior.  However if there's an easy way to split these changes in two
> commits, I'd prefer to stay on the safe side.

Below is an updated diff that preserves the current EV_ONESHOT
behaviour. I have just adapted a part of the compatibility logic
from function filter_process().

When f_process is given a non-NULL kev argument, it is known that
the callback is invoked from kqueue_scan(). If kev is NULL,
kqueue_register() is checking if the knote should be activated and
there is no intent to deliver the event right now.

Index: kern/uipc_socket.c
===================================================================
RCS file: src/sys/kern/uipc_socket.c,v
retrieving revision 1.261
diff -u -p -r1.261 uipc_socket.c
--- kern/uipc_socket.c  13 May 2021 19:43:11 -0000      1.261
+++ kern/uipc_socket.c  20 May 2021 14:01:18 -0000
@@ -70,15 +70,26 @@ void        sorflush(struct socket *);
 
 void   filt_sordetach(struct knote *kn);
 int    filt_soread(struct knote *kn, long hint);
+int    filt_soreadmodify(struct kevent *kev, struct knote *kn);
+int    filt_soreadprocess(struct knote *kn, struct kevent *kev);
+int    filt_soread_common(struct knote *kn, struct socket *so);
 void   filt_sowdetach(struct knote *kn);
 int    filt_sowrite(struct knote *kn, long hint);
+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_solisten(struct knote *kn, long hint);
+int    filt_solistenmodify(struct kevent *kev, struct knote *kn);
+int    filt_solistenprocess(struct knote *kn, struct kevent *kev);
+int    filt_solisten_common(struct knote *kn, struct socket *so);
 
 const struct filterops solisten_filtops = {
        .f_flags        = FILTEROP_ISFD,
        .f_attach       = NULL,
        .f_detach       = filt_sordetach,
        .f_event        = filt_solisten,
+       .f_modify       = filt_solistenmodify,
+       .f_process      = filt_solistenprocess,
 };
 
 const struct filterops soread_filtops = {
@@ -86,6 +97,8 @@ const struct filterops soread_filtops = 
        .f_attach       = NULL,
        .f_detach       = filt_sordetach,
        .f_event        = filt_soread,
+       .f_modify       = filt_soreadmodify,
+       .f_process      = filt_soreadprocess,
 };
 
 const struct filterops sowrite_filtops = {
@@ -93,6 +106,8 @@ const struct filterops sowrite_filtops =
        .f_attach       = NULL,
        .f_detach       = filt_sowdetach,
        .f_event        = filt_sowrite,
+       .f_modify       = filt_sowritemodify,
+       .f_process      = filt_sowriteprocess,
 };
 
 const struct filterops soexcept_filtops = {
@@ -100,6 +115,8 @@ const struct filterops soexcept_filtops 
        .f_attach       = NULL,
        .f_detach       = filt_sordetach,
        .f_event        = filt_soread,
+       .f_modify       = filt_soreadmodify,
+       .f_process      = filt_soreadprocess,
 };
 
 #ifndef SOMINCONN
@@ -2056,13 +2073,12 @@ filt_sordetach(struct knote *kn)
 }
 
 int
-filt_soread(struct knote *kn, long hint)
+filt_soread_common(struct knote *kn, struct socket *so)
 {
-       struct socket *so = kn->kn_fp->f_data;
-       int s, rv = 0;
+       int rv = 0;
+
+       soassertlocked(so);
 
-       if ((hint & NOTE_SUBMIT) == 0)
-               s = solock(so);
        kn->kn_data = so->so_rcv.sb_cc;
 #ifdef SOCKET_SPLICE
        if (isspliced(so)) {
@@ -2090,12 +2106,50 @@ filt_soread(struct knote *kn, long hint)
        } else {
                rv = (kn->kn_data >= so->so_rcv.sb_lowat);
        }
-       if ((hint & NOTE_SUBMIT) == 0)
-               sounlock(so, s);
 
        return rv;
 }
 
+int
+filt_soread(struct knote *kn, long hint)
+{
+       struct socket *so = kn->kn_fp->f_data;
+
+       return (filt_soread_common(kn, so));
+}
+
+int
+filt_soreadmodify(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_soread_common(kn, so);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_soreadprocess(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_soread_common(kn, so);
+       if (rv != 0)
+               knote_submit(kn, kev);
+       sounlock(so, s);
+
+       return (rv);
+}
+
 void
 filt_sowdetach(struct knote *kn)
 {
@@ -2107,13 +2161,12 @@ filt_sowdetach(struct knote *kn)
 }
 
 int
-filt_sowrite(struct knote *kn, long hint)
+filt_sowrite_common(struct knote *kn, struct socket *so)
 {
-       struct socket *so = kn->kn_fp->f_data;
-       int s, rv;
+       int rv;
+
+       soassertlocked(so);
 
-       if ((hint & NOTE_SUBMIT) == 0)
-               s = solock(so);
        kn->kn_data = sbspace(so, &so->so_snd);
        if (so->so_state & SS_CANTSENDMORE) {
                kn->kn_flags |= EV_EOF;
@@ -2133,27 +2186,100 @@ filt_sowrite(struct knote *kn, long hint
        } else {
                rv = (kn->kn_data >= so->so_snd.sb_lowat);
        }
-       if ((hint & NOTE_SUBMIT) == 0)
-               sounlock(so, s);
 
        return (rv);
 }
 
 int
-filt_solisten(struct knote *kn, long hint)
+filt_sowrite(struct knote *kn, long hint)
 {
        struct socket *so = kn->kn_fp->f_data;
-       int s;
 
-       if ((hint & NOTE_SUBMIT) == 0)
-               s = solock(so);
+       return (filt_sowrite_common(kn, so));
+}
+
+int
+filt_sowritemodify(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_sowrite_common(kn, so);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_sowriteprocess(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_sowrite_common(kn, so);
+       if (rv != 0)
+               knote_submit(kn, kev);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_solisten_common(struct knote *kn, struct socket *so)
+{
+       soassertlocked(so);
+
        kn->kn_data = so->so_qlen;
-       if ((hint & NOTE_SUBMIT) == 0)
-               sounlock(so, s);
 
        return (kn->kn_data != 0);
 }
 
+int
+filt_solisten(struct knote *kn, long hint)
+{
+       struct socket *so = kn->kn_fp->f_data;
+
+       return (filt_solisten_common(kn, so));
+}
+
+int
+filt_solistenmodify(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_solisten_common(kn, so);
+       sounlock(so, s);
+
+       return (rv);
+}
+
+int
+filt_solistenprocess(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_solisten_common(kn, so);
+       if (rv != 0)
+               knote_submit(kn, kev);
+       sounlock(so, s);
+
+       return (rv);
+}
+
 #ifdef DDB
 void
 sobuf_print(struct sockbuf *,
Index: kern/uipc_syscalls.c
===================================================================
RCS file: src/sys/kern/uipc_syscalls.c,v
retrieving revision 1.190
diff -u -p -r1.190 uipc_syscalls.c
--- kern/uipc_syscalls.c        13 May 2021 17:31:59 -0000      1.190
+++ kern/uipc_syscalls.c        20 May 2021 14:01:18 -0000
@@ -308,7 +308,7 @@ doaccept(struct proc *p, int sock, struc
            : (flags & SOCK_NONBLOCK ? FNONBLOCK : 0);
 
        /* connection has been removed from the listen queue */
-       KNOTE(&head->so_rcv.sb_sel.si_note, NOTE_SUBMIT);
+       KNOTE(&head->so_rcv.sb_sel.si_note, 0);
 
        fp->f_type = DTYPE_SOCKET;
        fp->f_flag = FREAD | FWRITE | nflag;

Reply via email to