Re: sblock() & solock() ordering

2017-07-03 Thread Alexander Bluhm
On Mon, Jul 03, 2017 at 11:42:19AM +0200, Martin Pieuchot wrote:
> Updated diff that fixes some issues reported by visa@:
> 
>   - prevents relocking the netlock in the 'restart' case.
>   - always call solock() after sbunlock() in sosplice().
> 
> Alexander is there an easy way to trigger the 'restart' case in the
> regression tests?  I ran the relayd regress but I'm not sure which
> kernel code it exercises.

See /usr/src/regress/sys/kern/sosplice, it runs all tests with and
without socket splicing.  Goal was to see that behavior is identical.
It tries a bunch of timings and dataflow variants with user land
sleep, send and receive syscalls.  I triggered many goto restart
in soreceive() and a few in sosend().

> Anyway, ok?

OK bluhm@

> 
> Index: sys/socketvar.h
> ===
> RCS file: /cvs/src/sys/sys/socketvar.h,v
> retrieving revision 1.70
> diff -u -p -r1.70 socketvar.h
> --- sys/socketvar.h   26 Jun 2017 09:32:32 -  1.70
> +++ sys/socketvar.h   3 Jul 2017 08:38:14 -
> @@ -239,7 +239,7 @@ struct rwlock;
>   * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
>   * Returns error without lock if sleep is interrupted.
>   */
> -int sblock(struct sockbuf *, int, struct rwlock *);
> +int sblock(struct socket *, struct sockbuf *, int);
>  
>  /* release lock on sockbuf sb */
>  void sbunlock(struct sockbuf *);
> Index: kern/uipc_socket.c
> ===
> RCS file: /cvs/src/sys/kern/uipc_socket.c,v
> retrieving revision 1.191
> diff -u -p -r1.191 uipc_socket.c
> --- kern/uipc_socket.c3 Jul 2017 08:29:24 -   1.191
> +++ kern/uipc_socket.c3 Jul 2017 08:41:24 -
> @@ -418,14 +418,14 @@ sosend(struct socket *so, struct mbuf *a
>   (sizeof(struct fdpass) / sizeof(int)));
>   }
>  
> -#define  snderr(errno)   { error = errno; sounlock(s); goto release; }
> +#define  snderr(errno)   { error = errno; goto release; }
>  
> + s = solock(so);
>  restart:
> - if ((error = sblock(>so_snd, SBLOCKWAIT(flags), NULL)) != 0)
> + if ((error = sblock(so, >so_snd, SBLOCKWAIT(flags))) != 0)
>   goto out;
>   so->so_state |= SS_ISSENDING;
>   do {
> - s = solock(so);
>   if (so->so_state & SS_CANTSENDMORE)
>   snderr(EPIPE);
>   if (so->so_error) {
> @@ -455,12 +455,10 @@ restart:
>   sbunlock(>so_snd);
>   error = sbwait(so, >so_snd);
>   so->so_state &= ~SS_ISSENDING;
> - sounlock(s);
>   if (error)
>   goto out;
>   goto restart;
>   }
> - sounlock(s);
>   space -= clen;
>   do {
>   if (uio == NULL) {
> @@ -471,8 +469,9 @@ restart:
>   if (flags & MSG_EOR)
>   top->m_flags |= M_EOR;
>   } else {
> - error = m_getuio(, atomic,
> - space, uio);
> + sounlock(s);
> + error = m_getuio(, atomic, space, uio);
> + s = solock(so);
>   if (error)
>   goto release;
>   space -= top->m_pkthdr.len;
> @@ -480,7 +479,6 @@ restart:
>   if (flags & MSG_EOR)
>   top->m_flags |= M_EOR;
>   }
> - s = solock(so);
>   if (resid == 0)
>   so->so_state &= ~SS_ISSENDING;
>   if (top && so->so_options & SO_ZEROIZE)
> @@ -488,7 +486,6 @@ restart:
>   error = (*so->so_proto->pr_usrreq)(so,
>   (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
>   top, addr, control, curproc);
> - sounlock(s);
>   clen = 0;
>   control = NULL;
>   top = NULL;
> @@ -501,6 +498,7 @@ release:
>   so->so_state &= ~SS_ISSENDING;
>   sbunlock(>so_snd);
>  out:
> + sounlock(s);
>   m_freem(top);
>   m_freem(control);
>   return (error);
> @@ -670,9 +668,11 @@ bad:
>   *mp = NULL;
>  
>  restart:
> - if ((error = sblock(>so_rcv, SBLOCKWAIT(flags), NULL)) != 0)
> - return (error);
>   s = solock(so);
> + if ((error = sblock(so, >so_rcv, SBLOCKWAIT(flags))) != 0) {
> + sounlock(s);
> + return (error);
> + }
>  
>   m = so->so_rcv.sb_mb;
>  #ifdef SOCKET_SPLICE
> @@ -1040,13 +1040,10 @@ sorflush(struct socket *so)
>  {
>   struct sockbuf *sb = >so_rcv;
>   struct 

Re: sblock() & solock() ordering

2017-07-03 Thread Martin Pieuchot
On 26/06/17(Mon) 16:15, Martin Pieuchot wrote:
> I'd like to enforce the following "lock" ordering: always hold the
> socket lock when calling sblock().
> 
> This would allow me to protect `so_state' in sosend() when setting the
> SS_ISSENDING bit.
> 
> Diff below implements that.  It also gets rid of sbsleep() and uses
> sosleep() instead.

Updated diff that fixes some issues reported by visa@:

  - prevents relocking the netlock in the 'restart' case.
  - always call solock() after sbunlock() in sosplice().

Alexander is there an easy way to trigger the 'restart' case in the
regression tests?  I ran the relayd regress but I'm not sure which
kernel code it exercises.

Anyway, ok?

Index: sys/socketvar.h
===
RCS file: /cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.70
diff -u -p -r1.70 socketvar.h
--- sys/socketvar.h 26 Jun 2017 09:32:32 -  1.70
+++ sys/socketvar.h 3 Jul 2017 08:38:14 -
@@ -239,7 +239,7 @@ struct rwlock;
  * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
  * Returns error without lock if sleep is interrupted.
  */
-int sblock(struct sockbuf *, int, struct rwlock *);
+int sblock(struct socket *, struct sockbuf *, int);
 
 /* release lock on sockbuf sb */
 void sbunlock(struct sockbuf *);
Index: kern/uipc_socket.c
===
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.191
diff -u -p -r1.191 uipc_socket.c
--- kern/uipc_socket.c  3 Jul 2017 08:29:24 -   1.191
+++ kern/uipc_socket.c  3 Jul 2017 08:41:24 -
@@ -418,14 +418,14 @@ sosend(struct socket *so, struct mbuf *a
(sizeof(struct fdpass) / sizeof(int)));
}
 
-#definesnderr(errno)   { error = errno; sounlock(s); goto release; }
+#definesnderr(errno)   { error = errno; goto release; }
 
+   s = solock(so);
 restart:
-   if ((error = sblock(>so_snd, SBLOCKWAIT(flags), NULL)) != 0)
+   if ((error = sblock(so, >so_snd, SBLOCKWAIT(flags))) != 0)
goto out;
so->so_state |= SS_ISSENDING;
do {
-   s = solock(so);
if (so->so_state & SS_CANTSENDMORE)
snderr(EPIPE);
if (so->so_error) {
@@ -455,12 +455,10 @@ restart:
sbunlock(>so_snd);
error = sbwait(so, >so_snd);
so->so_state &= ~SS_ISSENDING;
-   sounlock(s);
if (error)
goto out;
goto restart;
}
-   sounlock(s);
space -= clen;
do {
if (uio == NULL) {
@@ -471,8 +469,9 @@ restart:
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
} else {
-   error = m_getuio(, atomic,
-   space, uio);
+   sounlock(s);
+   error = m_getuio(, atomic, space, uio);
+   s = solock(so);
if (error)
goto release;
space -= top->m_pkthdr.len;
@@ -480,7 +479,6 @@ restart:
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
}
-   s = solock(so);
if (resid == 0)
so->so_state &= ~SS_ISSENDING;
if (top && so->so_options & SO_ZEROIZE)
@@ -488,7 +486,6 @@ restart:
error = (*so->so_proto->pr_usrreq)(so,
(flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
top, addr, control, curproc);
-   sounlock(s);
clen = 0;
control = NULL;
top = NULL;
@@ -501,6 +498,7 @@ release:
so->so_state &= ~SS_ISSENDING;
sbunlock(>so_snd);
 out:
+   sounlock(s);
m_freem(top);
m_freem(control);
return (error);
@@ -670,9 +668,11 @@ bad:
*mp = NULL;
 
 restart:
-   if ((error = sblock(>so_rcv, SBLOCKWAIT(flags), NULL)) != 0)
-   return (error);
s = solock(so);
+   if ((error = sblock(so, >so_rcv, SBLOCKWAIT(flags))) != 0) {
+   sounlock(s);
+   return (error);
+   }
 
m = so->so_rcv.sb_mb;
 #ifdef SOCKET_SPLICE
@@ -1040,13 +1040,10 @@ sorflush(struct socket *so)
 {
struct sockbuf *sb = >so_rcv;
struct protosw *pr = so->so_proto;
-   sa_family_t af = pr->pr_domain->dom_family;
struct socket aso;
 
sb->sb_flags |= SB_NOINTR;
-   

Re: sblock() & solock() ordering

2017-06-26 Thread Alexander Bluhm
On Mon, Jun 26, 2017 at 04:15:50PM +0200, Martin Pieuchot wrote:
> I'd like to enforce the following "lock" ordering: always hold the
> socket lock when calling sblock().

I was already wondering wether the "panic: receive 1" seen by stsp@
may be caused by an additional sleeping point in soreceive().  Now
we sleep less for the socket lock in the critical section.

> It also gets rid of sbsleep() and uses sosleep() instead.

Yes, much nicer.

> ok?

OK bluhm@

> 
> Index: sys/socketvar.h
> ===
> RCS file: /cvs/src/sys/sys/socketvar.h,v
> retrieving revision 1.70
> diff -u -p -r1.70 socketvar.h
> --- sys/socketvar.h   26 Jun 2017 09:32:32 -  1.70
> +++ sys/socketvar.h   26 Jun 2017 14:01:31 -
> @@ -239,7 +239,7 @@ struct rwlock;
>   * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
>   * Returns error without lock if sleep is interrupted.
>   */
> -int sblock(struct sockbuf *, int, struct rwlock *);
> +int sblock(struct socket *, struct sockbuf *, int);
>  
>  /* release lock on sockbuf sb */
>  void sbunlock(struct sockbuf *);
> Index: kern/uipc_socket.c
> ===
> RCS file: /cvs/src/sys/kern/uipc_socket.c,v
> retrieving revision 1.189
> diff -u -p -r1.189 uipc_socket.c
> --- kern/uipc_socket.c26 Jun 2017 09:32:31 -  1.189
> +++ kern/uipc_socket.c26 Jun 2017 14:10:03 -
> @@ -418,14 +418,14 @@ sosend(struct socket *so, struct mbuf *a
>   (sizeof(struct fdpass) / sizeof(int)));
>   }
>  
> -#define  snderr(errno)   { error = errno; sounlock(s); goto release; }
> +#define  snderr(errno)   { error = errno; goto release; }
>  
>  restart:
> - if ((error = sblock(>so_snd, SBLOCKWAIT(flags), NULL)) != 0)
> + s = solock(so);
> + if ((error = sblock(so, >so_snd, SBLOCKWAIT(flags))) != 0)
>   goto out;
>   so->so_state |= SS_ISSENDING;
>   do {
> - s = solock(so);
>   if (so->so_state & SS_CANTSENDMORE)
>   snderr(EPIPE);
>   if (so->so_error) {
> @@ -455,12 +455,10 @@ restart:
>   sbunlock(>so_snd);
>   error = sbwait(so, >so_snd);
>   so->so_state &= ~SS_ISSENDING;
> - sounlock(s);
>   if (error)
>   goto out;
>   goto restart;
>   }
> - sounlock(s);
>   space -= clen;
>   do {
>   if (uio == NULL) {
> @@ -471,8 +469,9 @@ restart:
>   if (flags & MSG_EOR)
>   top->m_flags |= M_EOR;
>   } else {
> - error = m_getuio(, atomic,
> - space, uio);
> + sounlock(s);
> + error = m_getuio(, atomic, space, uio);
> + s = solock(so);
>   if (error)
>   goto release;
>   space -= top->m_pkthdr.len;
> @@ -480,7 +479,6 @@ restart:
>   if (flags & MSG_EOR)
>   top->m_flags |= M_EOR;
>   }
> - s = solock(so);
>   if (resid == 0)
>   so->so_state &= ~SS_ISSENDING;
>   if (top && so->so_options & SO_ZEROIZE)
> @@ -488,7 +486,6 @@ restart:
>   error = (*so->so_proto->pr_usrreq)(so,
>   (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
>   top, addr, control, curproc);
> - sounlock(s);
>   clen = 0;
>   control = NULL;
>   top = NULL;
> @@ -501,6 +498,7 @@ release:
>   so->so_state &= ~SS_ISSENDING;
>   sbunlock(>so_snd);
>  out:
> + sounlock(s);
>   m_freem(top);
>   m_freem(control);
>   return (error);
> @@ -670,9 +668,11 @@ bad:
>   *mp = NULL;
>  
>  restart:
> - if ((error = sblock(>so_rcv, SBLOCKWAIT(flags), NULL)) != 0)
> - return (error);
>   s = solock(so);
> + if ((error = sblock(so, >so_rcv, SBLOCKWAIT(flags))) != 0) {
> + sounlock(s);
> + return (error);
> + }
>  
>   m = so->so_rcv.sb_mb;
>  #ifdef SOCKET_SPLICE
> @@ -1040,13 +1040,10 @@ sorflush(struct socket *so)
>  {
>   struct sockbuf *sb = >so_rcv;
>   struct protosw *pr = so->so_proto;
> - sa_family_t af = pr->pr_domain->dom_family;
>   struct socket aso;
>  
>   sb->sb_flags |= SB_NOINTR;
> - sblock(sb, M_WAITOK,
> - (af != PF_LOCAL && af != PF_ROUTE && af != PF_KEY) ?
> -  : NULL);
> + sblock(so, sb, 

sblock() & solock() ordering

2017-06-26 Thread Martin Pieuchot
I'd like to enforce the following "lock" ordering: always hold the
socket lock when calling sblock().

This would allow me to protect `so_state' in sosend() when setting the
SS_ISSENDING bit.

Diff below implements that.  It also gets rid of sbsleep() and uses
sosleep() instead.

ok?

Index: sys/socketvar.h
===
RCS file: /cvs/src/sys/sys/socketvar.h,v
retrieving revision 1.70
diff -u -p -r1.70 socketvar.h
--- sys/socketvar.h 26 Jun 2017 09:32:32 -  1.70
+++ sys/socketvar.h 26 Jun 2017 14:01:31 -
@@ -239,7 +239,7 @@ struct rwlock;
  * Unless SB_NOINTR is set on sockbuf, sleep is interruptible.
  * Returns error without lock if sleep is interrupted.
  */
-int sblock(struct sockbuf *, int, struct rwlock *);
+int sblock(struct socket *, struct sockbuf *, int);
 
 /* release lock on sockbuf sb */
 void sbunlock(struct sockbuf *);
Index: kern/uipc_socket.c
===
RCS file: /cvs/src/sys/kern/uipc_socket.c,v
retrieving revision 1.189
diff -u -p -r1.189 uipc_socket.c
--- kern/uipc_socket.c  26 Jun 2017 09:32:31 -  1.189
+++ kern/uipc_socket.c  26 Jun 2017 14:10:03 -
@@ -418,14 +418,14 @@ sosend(struct socket *so, struct mbuf *a
(sizeof(struct fdpass) / sizeof(int)));
}
 
-#definesnderr(errno)   { error = errno; sounlock(s); goto release; }
+#definesnderr(errno)   { error = errno; goto release; }
 
 restart:
-   if ((error = sblock(>so_snd, SBLOCKWAIT(flags), NULL)) != 0)
+   s = solock(so);
+   if ((error = sblock(so, >so_snd, SBLOCKWAIT(flags))) != 0)
goto out;
so->so_state |= SS_ISSENDING;
do {
-   s = solock(so);
if (so->so_state & SS_CANTSENDMORE)
snderr(EPIPE);
if (so->so_error) {
@@ -455,12 +455,10 @@ restart:
sbunlock(>so_snd);
error = sbwait(so, >so_snd);
so->so_state &= ~SS_ISSENDING;
-   sounlock(s);
if (error)
goto out;
goto restart;
}
-   sounlock(s);
space -= clen;
do {
if (uio == NULL) {
@@ -471,8 +469,9 @@ restart:
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
} else {
-   error = m_getuio(, atomic,
-   space, uio);
+   sounlock(s);
+   error = m_getuio(, atomic, space, uio);
+   s = solock(so);
if (error)
goto release;
space -= top->m_pkthdr.len;
@@ -480,7 +479,6 @@ restart:
if (flags & MSG_EOR)
top->m_flags |= M_EOR;
}
-   s = solock(so);
if (resid == 0)
so->so_state &= ~SS_ISSENDING;
if (top && so->so_options & SO_ZEROIZE)
@@ -488,7 +486,6 @@ restart:
error = (*so->so_proto->pr_usrreq)(so,
(flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
top, addr, control, curproc);
-   sounlock(s);
clen = 0;
control = NULL;
top = NULL;
@@ -501,6 +498,7 @@ release:
so->so_state &= ~SS_ISSENDING;
sbunlock(>so_snd);
 out:
+   sounlock(s);
m_freem(top);
m_freem(control);
return (error);
@@ -670,9 +668,11 @@ bad:
*mp = NULL;
 
 restart:
-   if ((error = sblock(>so_rcv, SBLOCKWAIT(flags), NULL)) != 0)
-   return (error);
s = solock(so);
+   if ((error = sblock(so, >so_rcv, SBLOCKWAIT(flags))) != 0) {
+   sounlock(s);
+   return (error);
+   }
 
m = so->so_rcv.sb_mb;
 #ifdef SOCKET_SPLICE
@@ -1040,13 +1040,10 @@ sorflush(struct socket *so)
 {
struct sockbuf *sb = >so_rcv;
struct protosw *pr = so->so_proto;
-   sa_family_t af = pr->pr_domain->dom_family;
struct socket aso;
 
sb->sb_flags |= SB_NOINTR;
-   sblock(sb, M_WAITOK,
-   (af != PF_LOCAL && af != PF_ROUTE && af != PF_KEY) ?
-: NULL);
+   sblock(so, sb, M_WAITOK);
socantrcvmore(so);
sbunlock(sb);
aso.so_proto = pr;
@@ -1094,11 +1091,13 @@ sosplice(struct socket *so, int fd, off_
 
/* If no fd is given, unsplice by removing existing link. */
if (fd < 0) {
+   s =