static struct sock *nls;
 static DEFINE_MUTEX(rx_queue_mutex);
 
+/*
+ * conn_mutex protects the {start,bind,stop,destroy}_conn from racing
+ * against the kernel stop_connection recovery mechanism
+ */
+static DEFINE_MUTEX(conn_mutex);
+
 static LIST_HEAD(sesslist);
 static LIST_HEAD(sessdestroylist);
 static DEFINE_SPINLOCK(sesslock);
 }
 EXPORT_SYMBOL_GPL(iscsi_offload_mesg);
 
+/*
+ * This can be called without the rx_queue_mutex, if invoked by the kernel
+ * stop work. But, in that case, it is guaranteed not to race with
+ * iscsi_destroy by conn_mutex.
+ */
+static void iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag)
+{
+       /*
+        * It is important that this path doesn't rely on
+        * rx_queue_mutex, otherwise, a thread doing allocation on a
+        * start_session/start_connection could sleep waiting on a
+        * writeback to a failed iscsi device, that cannot be recovered
+        * because the lock is held.  If we don't hold it here, the
+        * kernel stop_conn_work_fn has a chance to stop the broken
+        * session and resolve the allocation.
+        *
+        * Still, the user invoked .stop_conn() needs to be serialized
+        * with stop_conn_work_fn by a private mutex.  Not pretty, but
+        * it works.
+        */
+       mutex_lock(&conn_mutex);
+       conn->transport->stop_conn(conn, flag);
+       mutex_unlock(&conn_mutex);
+
+}
+
 static void stop_conn_work_fn(struct work_struct *work)
 {
        struct iscsi_cls_conn *conn, *tmp;
                uint32_t sid = iscsi_conn_get_sid(conn);
                struct iscsi_cls_session *session;
 
-               mutex_lock(&rx_queue_mutex);
-
                session = iscsi_session_lookup(sid);
                if (session) {
                        if (system_state != SYSTEM_RUNNING) {
                                session->recovery_tmo = 0;
-                               conn->transport->stop_conn(conn,
-                                                          STOP_CONN_TERM);
+                               iscsi_if_stop_conn(conn, STOP_CONN_TERM);
                        } else {
-                               conn->transport->stop_conn(conn,
-                                                          STOP_CONN_RECOVER);
+                               iscsi_if_stop_conn(conn, STOP_CONN_RECOVER);
                        }
                }
 
                list_del_init(&conn->conn_list_err);
-
-               mutex_unlock(&rx_queue_mutex);
-
-               /* we don't want to hold rx_queue_mutex for too long,
-                * for instance if many conns failed at the same time,
-                * since this stall other iscsi maintenance operations.
-                * Give other users a chance to proceed.
-                */
-               cond_resched();
        }
 }
 
        spin_unlock_irqrestore(&connlock, flags);
 
        ISCSI_DBG_TRANS_CONN(conn, "Destroying transport conn\n");
+
+       mutex_lock(&conn_mutex);
        if (transport->destroy_conn)
                transport->destroy_conn(conn);
+       mutex_unlock(&conn_mutex);
 
        return 0;
 }
                        break;
                }
 
+               mutex_lock(&conn_mutex);
                ev->r.retcode = transport->bind_conn(session, conn,
                                                ev->u.b_conn.transport_eph,
                                                ev->u.b_conn.is_leading);
+               mutex_unlock(&conn_mutex);
+
                if (ev->r.retcode || !transport->ep_connect)
                        break;
 
        case ISCSI_UEVENT_START_CONN:
                conn = iscsi_conn_lookup(ev->u.start_conn.sid, ev->u.start_conn.cid);
                if (conn) {
+                       mutex_lock(&conn_mutex);
                        ev->r.retcode = transport->start_conn(conn);
                        if (!ev->r.retcode)
                                conn->state = ISCSI_CONN_UP;
+                       mutex_unlock(&conn_mutex);
                }
                else
                        err = -EINVAL;
        case ISCSI_UEVENT_STOP_CONN:
                conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid);
                if (conn)
-                       transport->stop_conn(conn, ev->u.stop_conn.flag);
+                       iscsi_if_stop_conn(conn, ev->u.stop_conn.flag);
                else
                        err = -EINVAL;
                break;
        case ISCSI_UEVENT_SEND_PDU:
                conn = iscsi_conn_lookup(ev->u.send_pdu.sid, ev->u.send_pdu.cid);
-               if (conn)
+               if (conn) {
+                       mutex_lock(&conn_mutex);
                        ev->r.retcode = transport->send_pdu(conn,
                                (struct iscsi_hdr*)((char*)ev + sizeof(*ev)),
                                (char*)ev + sizeof(*ev) + ev->u.send_pdu.hdr_size,
                                ev->u.send_pdu.data_size);
+                       mutex_unlock(&conn_mutex);
+               }
                else
                        err = -EINVAL;
                break;