Merge branch 'for-next' of git://git.kernel.org/pub/scm/linux/kernel/git/nab/target...
[cascardo/linux.git] / drivers / target / iscsi / iscsi_target.c
index a9257a0..342a07c 100644 (file)
@@ -957,9 +957,9 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
                cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA;
 
        conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
-       if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+       if (hdr->flags & ISCSI_FLAG_CMD_READ)
                cmd->targ_xfer_tag = session_get_next_ttt(conn->sess);
-       } else if (hdr->flags & ISCSI_FLAG_CMD_WRITE)
+       else
                cmd->targ_xfer_tag = 0xFFFFFFFF;
        cmd->cmd_sn             = be32_to_cpu(hdr->cmdsn);
        cmd->exp_stat_sn        = be32_to_cpu(hdr->exp_statsn);
@@ -3986,7 +3986,13 @@ get_immediate:
        }
 
 transport_err:
-       iscsit_take_action_for_connection_exit(conn);
+       /*
+        * Avoid the normal connection failure code-path if this connection
+        * is still within LOGIN mode, and iscsi_np process context is
+        * responsible for cleaning up the early connection failure.
+        */
+       if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
+               iscsit_take_action_for_connection_exit(conn);
 out:
        return 0;
 }
@@ -4070,7 +4076,7 @@ reject:
 
 int iscsi_target_rx_thread(void *arg)
 {
-       int ret;
+       int ret, rc;
        u8 buffer[ISCSI_HDR_LEN], opcode;
        u32 checksum = 0, digest = 0;
        struct iscsi_conn *conn = arg;
@@ -4080,10 +4086,16 @@ int iscsi_target_rx_thread(void *arg)
         * connection recovery / failure event can be triggered externally.
         */
        allow_signal(SIGINT);
+       /*
+        * Wait for iscsi_post_login_handler() to complete before allowing
+        * incoming iscsi/tcp socket I/O, and/or failing the connection.
+        */
+       rc = wait_for_completion_interruptible(&conn->rx_login_comp);
+       if (rc < 0)
+               return 0;
 
        if (conn->conn_transport->transport_type == ISCSI_INFINIBAND) {
                struct completion comp;
-               int rc;
 
                init_completion(&comp);
                rc = wait_for_completion_interruptible(&comp);
@@ -4520,7 +4532,18 @@ static void iscsit_logout_post_handler_closesession(
        struct iscsi_conn *conn)
 {
        struct iscsi_session *sess = conn->sess;
-       int sleep = cmpxchg(&conn->tx_thread_active, true, false);
+       int sleep = 1;
+       /*
+        * Traditional iscsi/tcp will invoke this logic from TX thread
+        * context during session logout, so clear tx_thread_active and
+        * sleep if iscsit_close_connection() has not already occured.
+        *
+        * Since iser-target invokes this logic from it's own workqueue,
+        * always sleep waiting for RX/TX thread shutdown to complete
+        * within iscsit_close_connection().
+        */
+       if (conn->conn_transport->transport_type == ISCSI_TCP)
+               sleep = cmpxchg(&conn->tx_thread_active, true, false);
 
        atomic_set(&conn->conn_logout_remove, 0);
        complete(&conn->conn_logout_comp);
@@ -4534,7 +4557,10 @@ static void iscsit_logout_post_handler_closesession(
 static void iscsit_logout_post_handler_samecid(
        struct iscsi_conn *conn)
 {
-       int sleep = cmpxchg(&conn->tx_thread_active, true, false);
+       int sleep = 1;
+
+       if (conn->conn_transport->transport_type == ISCSI_TCP)
+               sleep = cmpxchg(&conn->tx_thread_active, true, false);
 
        atomic_set(&conn->conn_logout_remove, 0);
        complete(&conn->conn_logout_comp);
@@ -4753,6 +4779,7 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
        struct iscsi_session *sess;
        struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
        struct se_session *se_sess, *se_sess_tmp;
+       LIST_HEAD(free_list);
        int session_count = 0;
 
        spin_lock_bh(&se_tpg->session_lock);
@@ -4774,14 +4801,17 @@ int iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
                }
                atomic_set(&sess->session_reinstatement, 1);
                spin_unlock(&sess->conn_lock);
-               spin_unlock_bh(&se_tpg->session_lock);
 
-               iscsit_free_session(sess);
-               spin_lock_bh(&se_tpg->session_lock);
+               list_move_tail(&se_sess->sess_list, &free_list);
+       }
+       spin_unlock_bh(&se_tpg->session_lock);
+
+       list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
+               sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
 
+               iscsit_free_session(sess);
                session_count++;
        }
-       spin_unlock_bh(&se_tpg->session_lock);
 
        pr_debug("Released %d iSCSI Session(s) from Target Portal"
                        " Group: %hu\n", session_count, tpg->tpgt);