* Add a flag to track an in-transit socket abort to avoid races when closing
authorMatthew Dillon <dillon@dragonflybsd.org>
Thu, 28 Aug 2008 23:15:45 +0000 (23:15 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Thu, 28 Aug 2008 23:15:45 +0000 (23:15 +0000)
  a socket.

* Abort sockets asynchronously to prevent socket proto threads from
  deadlocking each other.

Reported-by: Peter Avalos
sys/kern/uipc_msg.c
sys/kern/uipc_socket.c
sys/kern/uipc_socket2.c
sys/sys/socketops.h
sys/sys/socketvar.h

index 0c412f9..b9d1a5b 100644 (file)
@@ -30,7 +30,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/kern/uipc_msg.c,v 1.23 2008/07/10 00:19:27 aggelos Exp $
+ * $DragonFly: src/sys/kern/uipc_msg.c,v 1.24 2008/08/28 23:15:42 dillon Exp $
  */
 
 #include <sys/param.h>
 #include <net/netisr.h>
 #include <net/netmsg.h>
 
-int
+/*
+ * Abort a socket and free it
+ */
+void
 so_pru_abort(struct socket *so)
 {
-       int error;
        struct netmsg_pru_abort msg;
        lwkt_port_t port;
 
        port = so->so_proto->pr_mport(so, NULL, NULL, PRU_ABORT);
-       netmsg_init(&msg.nm_netmsg, &curthread->td_msgport, 0,
-                   netmsg_pru_abort);
+       netmsg_init(&msg.nm_netmsg, &curthread->td_msgport,
+                   0, netmsg_pru_abort);
        msg.nm_prufn = so->so_proto->pr_usrreqs->pru_abort;
        msg.nm_so = so;
-       error = lwkt_domsg(port, &msg.nm_netmsg.nm_lmsg, 0);
-       return (error);
+       (void)lwkt_domsg(port, &msg.nm_netmsg.nm_lmsg, 0);
+}
+
+/*
+ * Abort a socket and free it, asynchronously.
+ */
+void
+so_pru_aborta(struct socket *so)
+{
+       struct netmsg_pru_abort *msg;
+       lwkt_port_t port;
+
+       msg = kmalloc(sizeof(*msg), M_LWKTMSG, M_WAITOK | M_ZERO);
+       port = so->so_proto->pr_mport(so, NULL, NULL, PRU_ABORT);
+       netmsg_init(&msg->nm_netmsg, &netisr_afree_rport,
+                   0, netmsg_pru_abort);
+       msg->nm_prufn = so->so_proto->pr_usrreqs->pru_abort;
+       msg->nm_so = so;
+       lwkt_sendmsg(port, &msg->nm_netmsg.nm_lmsg);
 }
 
 int
@@ -406,12 +425,23 @@ so_pru_ctloutput(struct socket *so, struct sockopt *sopt)
  * our dispatcher ignores the return value, but since we are handling
  * the replymsg ourselves we return EASYNC by convention.
  */
+
+/*
+ * Abort and destroy a socket.
+ */
 void
 netmsg_pru_abort(netmsg_t msg)
 {
        struct netmsg_pru_abort *nm = (void *)msg;
+       struct socket *so = nm->nm_so;
+       int error;
 
-       lwkt_replymsg(&msg->nm_lmsg, nm->nm_prufn(nm->nm_so));
+       KKASSERT(so->so_state & SS_ABORTING);
+       so->so_state &= ~SS_ABORTING;
+       error = nm->nm_prufn(so);
+       if (error)
+               sofree(so);
+       lwkt_replymsg(&msg->nm_lmsg, error);
 }
 
 #ifdef notused
index 91b76cf..fc501ad 100644 (file)
@@ -65,7 +65,7 @@
  *
  *     @(#)uipc_socket.c       8.3 (Berkeley) 4/15/94
  * $FreeBSD: src/sys/kern/uipc_socket.c,v 1.68.2.24 2003/11/11 17:18:18 silby Exp $
- * $DragonFly: src/sys/kern/uipc_socket.c,v 1.53 2008/08/15 17:37:29 nth Exp $
+ * $DragonFly: src/sys/kern/uipc_socket.c,v 1.54 2008/08/28 23:15:43 dillon Exp $
  */
 
 #include "opt_inet.h"
@@ -278,6 +278,14 @@ solisten(struct socket *so, int backlog, struct thread *td)
        return (0);
 }
 
+/*
+ * Destroy a disconnected socket.  This routine is a NOP if entities
+ * still have a reference on the socket:
+ *
+ *     so_pcb -        The protocol stack still has a reference
+ *     SS_NOFDREF -    There is no longer a file pointer reference
+ *     SS_ABORTING -   An abort netmsg is in-flight
+ */
 void
 sofree(struct socket *so)
 {
@@ -285,6 +293,8 @@ sofree(struct socket *so)
 
        if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
                return;
+       if (so->so_state & SS_ABORTING)
+               return;
        if (head != NULL) {
                if (so->so_state & SS_INCOMP) {
                        TAILQ_REMOVE(&head->so_incomp, so, so_list);
@@ -350,21 +360,27 @@ drop:
        }
 discard:
        if (so->so_options & SO_ACCEPTCONN) {
-               struct socket *sp, *sonext;
+               struct socket *sp;
 
-               sp = TAILQ_FIRST(&so->so_incomp);
-               for (; sp != NULL; sp = sonext) {
-                       sonext = TAILQ_NEXT(sp, so_list);
-                       (void) soabort(sp);
+               while ((sp = TAILQ_FIRST(&so->so_incomp)) != NULL) {
+                       TAILQ_REMOVE(&so->so_incomp, sp, so_list);
+                       sp->so_state &= ~SS_INCOMP;
+                       sp->so_head = NULL;
+                       so->so_incqlen--;
+                       if ((sp->so_state & SS_ABORTING) == 0) {
+                               sp->so_state |= SS_ABORTING;
+                               soaborta(sp);
+                       }
                }
-               for (sp = TAILQ_FIRST(&so->so_comp); sp != NULL; sp = sonext) {
-                       sonext = TAILQ_NEXT(sp, so_list);
-                       /* Dequeue from so_comp since sofree() won't do it */
+               while ((sp = TAILQ_FIRST(&so->so_comp)) != NULL) {
                        TAILQ_REMOVE(&so->so_comp, sp, so_list);
-                       so->so_qlen--;
                        sp->so_state &= ~SS_COMP;
                        sp->so_head = NULL;
-                       (void) soabort(sp);
+                       so->so_qlen--;
+                       if ((sp->so_state & SS_ABORTING) == 0) {
+                               sp->so_state |= SS_ABORTING;
+                               soaborta(sp);
+                       }
                }
        }
        if (so->so_state & SS_NOFDREF)
@@ -376,19 +392,18 @@ discard:
 }
 
 /*
- * Must be called from a critical section.
+ * Abort and destroy a socket.
  */
-int
+void
 soabort(struct socket *so)
 {
-       int error;
+       so_pru_abort(so);
+}
 
-       error = so_pru_abort(so);
-       if (error) {
-               sofree(so);
-               return error;
-       }
-       return (0);
+void
+soaborta(struct socket *so)
+{
+       so_pru_aborta(so);
 }
 
 int
index 0d7faff..45d599e 100644 (file)
@@ -33,7 +33,7 @@
  *
  *     @(#)uipc_socket2.c      8.1 (Berkeley) 6/10/93
  * $FreeBSD: src/sys/kern/uipc_socket2.c,v 1.55.2.17 2002/08/31 19:04:55 dwmalone Exp $
- * $DragonFly: src/sys/kern/uipc_socket2.c,v 1.31 2008/05/27 05:25:34 dillon Exp $
+ * $DragonFly: src/sys/kern/uipc_socket2.c,v 1.32 2008/08/28 23:15:43 dillon Exp $
  */
 
 #include "opt_param.h"
@@ -229,6 +229,7 @@ struct socket *
 sonewconn(struct socket *head, int connstatus)
 {
        struct socket *so;
+       struct socket *sp;
        struct pru_attach_info ai;
 
        if (head->so_qlen > 3 * head->so_qlimit / 2)
@@ -262,9 +263,15 @@ sonewconn(struct socket *head, int connstatus)
                head->so_qlen++;
        } else {
                if (head->so_incqlen > head->so_qlimit) {
-                       struct socket *sp;
                        sp = TAILQ_FIRST(&head->so_incomp);
-                       (void) soabort(sp);
+                       TAILQ_REMOVE(&head->so_incomp, sp, so_list);
+                       head->so_incqlen--;
+                       sp->so_state &= ~SS_INCOMP;
+                       sp->so_head = NULL;
+                       if ((sp->so_state & SS_ABORTING) == 0) {
+                               sp->so_state |= SS_ABORTING;
+                               soaborta(sp);
+                       }
                }
                TAILQ_INSERT_TAIL(&head->so_incomp, so, so_list);
                so->so_state |= SS_INCOMP;
index 92a1487..a0424e3 100644 (file)
@@ -30,7 +30,7 @@
  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  *
- * $DragonFly: src/sys/sys/socketops.h,v 1.12 2008/06/17 20:50:11 aggelos Exp $
+ * $DragonFly: src/sys/sys/socketops.h,v 1.13 2008/08/28 23:15:45 dillon Exp $
  */
 
 #ifndef _SOCKETOPS_H_
@@ -73,7 +73,8 @@ so_pru_soreceive(struct socket *so, struct sockaddr **paddr, struct uio *uio,
                controlp, flagsp));
 }
 
-int so_pru_abort (struct socket *so);
+void so_pru_abort (struct socket *so);
+void so_pru_aborta (struct socket *so);
 int so_pru_accept (struct socket *so, struct sockaddr **nam);
 int so_pru_attach (struct socket *so, int proto, struct pru_attach_info *ai);
 int so_pru_bind (struct socket *so, struct sockaddr *nam, struct thread *td);
index d64ac74..02e3548 100644 (file)
@@ -32,7 +32,7 @@
  *
  *     @(#)socketvar.h 8.3 (Berkeley) 2/19/95
  * $FreeBSD: src/sys/sys/socketvar.h,v 1.46.2.10 2003/08/24 08:24:39 hsu Exp $
- * $DragonFly: src/sys/sys/socketvar.h,v 1.34 2008/08/15 17:37:29 nth Exp $
+ * $DragonFly: src/sys/sys/socketvar.h,v 1.35 2008/08/28 23:15:45 dillon Exp $
  */
 
 #ifndef _SYS_SOCKETVAR_H_
@@ -146,7 +146,7 @@ struct socket {
 #define        SS_CANTRCVMORE          0x0020  /* can't receive more data from peer */
 #define        SS_RCVATMARK            0x0040  /* at mark on input */
 
-#define        SS_UNUSED0100           0x0100
+#define        SS_ABORTING             0x0100  /* so_abort() in progress */
 #define        SS_ASYNC                0x0200  /* async i/o notify */
 #define        SS_ISCONFIRMING         0x0400  /* deciding to accept connection req */
 
@@ -355,7 +355,8 @@ void        ssbtoxsockbuf (struct signalsockbuf *sb, struct xsockbuf *xsb);
 int    ssb_wait (struct signalsockbuf *sb);
 int    _ssb_lock (struct signalsockbuf *sb);
 
-int    soabort (struct socket *so);
+void   soabort (struct socket *so);
+void   soaborta (struct socket *so);
 int    soaccept (struct socket *so, struct sockaddr **nam);
 struct socket *soalloc (int waitok);
 int    sobind (struct socket *so, struct sockaddr *nam, struct thread *td);