iscsi-target: Fix potential dead-lock during node acl delete
[firefly-linux-kernel-4.4.55.git] / drivers / target / iscsi / iscsi_target_configfs.c
index 255204cc43e6218ff7caa6725d7430f300a4d571..b4bfd706ac9422705a35fc879705f46d6787fc13 100644 (file)
@@ -1593,7 +1593,8 @@ static int lio_tpg_check_prot_fabric_only(
 }
 
 /*
- * Called with spin_lock_bh(struct se_portal_group->session_lock) held..
+ * Called with spin_lock_irq(struct se_portal_group->session_lock) held
+ * or not held.
  *
  * Also, this function calls iscsit_inc_session_usage_count() on the
  * struct iscsi_session in question.
@@ -1601,19 +1602,32 @@ static int lio_tpg_check_prot_fabric_only(
 static int lio_tpg_shutdown_session(struct se_session *se_sess)
 {
        struct iscsi_session *sess = se_sess->fabric_sess_ptr;
+       struct se_portal_group *se_tpg = se_sess->se_tpg;
+       bool local_lock = false;
+
+       if (!spin_is_locked(&se_tpg->session_lock)) {
+               spin_lock_irq(&se_tpg->session_lock);
+               local_lock = true;
+       }
 
        spin_lock(&sess->conn_lock);
        if (atomic_read(&sess->session_fall_back_to_erl0) ||
            atomic_read(&sess->session_logout) ||
            (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
                spin_unlock(&sess->conn_lock);
+               if (local_lock)
+                       spin_unlock_irq(&sess->conn_lock);
                return 0;
        }
        atomic_set(&sess->session_reinstatement, 1);
        spin_unlock(&sess->conn_lock);
 
        iscsit_stop_time2retain_timer(sess);
+       spin_unlock_irq(&se_tpg->session_lock);
+
        iscsit_stop_session(sess, 1, 1);
+       if (!local_lock)
+               spin_lock_irq(&se_tpg->session_lock);
 
        return 1;
 }