Print this page
1631 kernel panic in tcp_input_data

@@ -19,10 +19,11 @@
  * CDDL HEADER END
  */
 
 /*
  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
  */
 
 /* This file contains all TCP input processing functions. */
 
 #include <sys/types.h>

@@ -2228,10 +2229,118 @@
                 return (mp1);
         }
 }
 
 /*
+ * Dummy socket upcalls for if/when the conn_t gets detached from a
+ * direct-callback sonode via a user-driven close().  Easy to catch with
+ * DTrace FBT, and should be mostly harmless.
+ */
+
+/* ARGSUSED */
+static sock_upper_handle_t
+tcp_dummy_newconn(sock_upper_handle_t x, sock_lower_handle_t y,
+    sock_downcalls_t *z, cred_t *cr, pid_t pid, sock_upcalls_t **ignored)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+        return (NULL);
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_connected(sock_upper_handle_t x, sock_connid_t y, cred_t *cr,
+    pid_t pid)
+{
+        ASSERT(x == NULL);
+        /* Normally we'd crhold(cr) and attach it to socket state. */
+        /* LINTED */
+}
+
+/* ARGSUSED */
+static int
+tcp_dummy_disconnected(sock_upper_handle_t x, sock_connid_t y, int blah)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+        return (-1);
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_opctl(sock_upper_handle_t x, sock_opctl_action_t y, uintptr_t blah)
+{
+        ASSERT(x == NULL);
+        /* We really want this one to be a harmless NOP for now. */
+        /* LINTED */
+}
+
+/* ARGSUSED */
+static ssize_t
+tcp_dummy_recv(sock_upper_handle_t x, mblk_t *mp, size_t len, int flags,
+    int *error, boolean_t *push)
+{
+        ASSERT(x == NULL);
+
+        /*
+         * Consume the message, set ESHUTDOWN, and return an error.
+         * Nobody's home!
+         */
+        freemsg(mp);
+        *error = ESHUTDOWN;
+        return (-1);
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_set_proto_props(sock_upper_handle_t x, struct sock_proto_props *y)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_txq_full(sock_upper_handle_t x, boolean_t y)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_signal_oob(sock_upper_handle_t x, ssize_t len)
+{
+        ASSERT(x == NULL);
+        /* Otherwise, this would signal socket state about OOB data. */
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_set_error(sock_upper_handle_t x, int err)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+}
+
+/* ARGSUSED */
+static void
+tcp_dummy_onearg(sock_upper_handle_t x)
+{
+        ASSERT(0);      /* Panic in debug, otherwise ignore. */
+}
+
+static sock_upcalls_t tcp_dummy_upcalls = {
+        tcp_dummy_newconn,
+        tcp_dummy_connected,
+        tcp_dummy_disconnected,
+        tcp_dummy_opctl,
+        tcp_dummy_recv,
+        tcp_dummy_set_proto_props,
+        tcp_dummy_txq_full,
+        tcp_dummy_signal_oob,
+        tcp_dummy_onearg,
+        tcp_dummy_set_error,
+        tcp_dummy_onearg
+};
+
+/*
  * Handle M_DATA messages from IP. Its called directly from IP via
  * squeue for received IP packets.
  *
  * The first argument is always the connp/tcp to which the mp belongs.
  * There are no exceptions to this rule. The caller has already put

@@ -2269,10 +2378,11 @@
         int             mss;
         conn_t          *connp = (conn_t *)arg;
         squeue_t        *sqp = (squeue_t *)arg2;
         tcp_t           *tcp = connp->conn_tcp;
         tcp_stack_t     *tcps = tcp->tcp_tcps;
+        sock_upcalls_t  *sockupcalls;
 
         /*
          * RST from fused tcp loopback peer should trigger an unfuse.
          */
         if (tcp->tcp_fused) {

@@ -2394,10 +2504,15 @@
                         mp->b_wptr = (uchar_t *)tcpha + TCP_HDR_LENGTH(tcpha);
                         seg_len = 0;
                 }
         }
 
+        sockupcalls = connp->conn_upcalls;
+        /* A conn_t may have belonged to a now-closed socket.  Be careful. */
+        if (sockupcalls == NULL)
+                sockupcalls = &tcp_dummy_upcalls;
+
         switch (tcp->tcp_state) {
         case TCPS_SYN_SENT:
                 if (connp->conn_final_sqp == NULL &&
                     tcp_outbound_squeue_switch && sqp != NULL) {
                         ASSERT(connp->conn_initial_sqp == connp->conn_sqp);

@@ -2605,12 +2720,11 @@
                                                             ira->ira_cred,
                                                             ira->ira_cpid);
                                                 }
                                                 putnext(connp->conn_rq, mp1);
                                         } else {
-                                                (*connp->conn_upcalls->
-                                                    su_connected)
+                                                (*sockupcalls->su_connected)
                                                     (connp->conn_upper_handle,
                                                     tcp->tcp_connid,
                                                     ira->ira_cred,
                                                     ira->ira_cpid);
                                                 freemsg(mp1);

@@ -2633,11 +2747,11 @@
                                                 mblk_setcred(mp1, ira->ira_cred,
                                                     ira->ira_cpid);
                                         }
                                         putnext(connp->conn_rq, mp1);
                                 } else {
-                                        (*connp->conn_upcalls->su_connected)
+                                        (*sockupcalls->su_connected)
                                             (connp->conn_upper_handle,
                                             tcp->tcp_connid, ira->ira_cred,
                                             ira->ira_cpid);
                                         freemsg(mp1);
                                 }

@@ -3007,12 +3121,11 @@
                         if ((flags & TH_URG) &&
                             (!tcp->tcp_urp_last_valid || SEQ_GT(urp + seg_seq,
                             tcp->tcp_urp_last))) {
                                 if (IPCL_IS_NONSTR(connp)) {
                                         if (!TCP_IS_DETACHED(tcp)) {
-                                                (*connp->conn_upcalls->
-                                                    su_signal_oob)
+                                                (*sockupcalls->su_signal_oob)
                                                     (connp->conn_upper_handle,
                                                     urp);
                                         }
                                 } else {
                                         mp1 = allocb(0, BPRI_MED);

@@ -3286,11 +3399,11 @@
                          * Neither TH_SEND_URP_MARK nor TH_MARKNEXT_NEEDED
                          * are used by non-STREAMS sockets.
                          */
                         if (IPCL_IS_NONSTR(connp)) {
                                 if (!TCP_IS_DETACHED(tcp)) {
-                                        (*connp->conn_upcalls->su_signal_oob)
+                                        (*sockupcalls->su_signal_oob)
                                             (connp->conn_upper_handle, urp);
                                 }
                         } else {
                                 /*
                                  * If we haven't generated the signal yet for

@@ -3445,11 +3558,11 @@
                          * have to allocate the T_exdata_ind, if we can.
                          */
                         if (IPCL_IS_NONSTR(connp)) {
                                 int error;
 
-                                (*connp->conn_upcalls->su_recv)
+                                (*sockupcalls->su_recv)
                                     (connp->conn_upper_handle, mp, seg_len,
                                     MSG_OOB, &error, NULL);
                                 /*
                                  * We should never be in middle of a
                                  * fallback, the squeue guarantees that.

@@ -4624,12 +4737,11 @@
                  * Non-STREAMS socket
                  */
                 boolean_t push = flags & (TH_PUSH|TH_FIN);
                 int error;
 
-                if ((*connp->conn_upcalls->su_recv)(
-                    connp->conn_upper_handle,
+                if ((*sockupcalls->su_recv)(connp->conn_upper_handle,
                     mp, seg_len, 0, &error, &push) <= 0) {
                         /*
                          * We should never be in middle of a
                          * fallback, the squeue guarantees that.
                          */

@@ -4867,12 +4979,12 @@
                 ASSERT(!tcp->tcp_detached);
 
                 if (IPCL_IS_NONSTR(connp)) {
                         ASSERT(tcp->tcp_ordrel_mp == NULL);
                         tcp->tcp_ordrel_done = B_TRUE;
-                        (*connp->conn_upcalls->su_opctl)
-                            (connp->conn_upper_handle, SOCK_OPCTL_SHUT_RECV, 0);
+                        (*sockupcalls->su_opctl)(connp->conn_upper_handle,
+                            SOCK_OPCTL_SHUT_RECV, 0);
                         goto done;
                 }
 
                 if (tcp->tcp_rcv_list != NULL) {
                         /*