162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/******************************************************************************* 362306a36Sopenharmony_ci * This file contains error recovery level two functions used by 462306a36Sopenharmony_ci * the iSCSI Target driver. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci ******************************************************************************/ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/slab.h> 1362306a36Sopenharmony_ci#include <scsi/iscsi_proto.h> 1462306a36Sopenharmony_ci#include <target/target_core_base.h> 1562306a36Sopenharmony_ci#include <target/target_core_fabric.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h> 1862306a36Sopenharmony_ci#include "iscsi_target_datain_values.h" 1962306a36Sopenharmony_ci#include "iscsi_target_util.h" 2062306a36Sopenharmony_ci#include "iscsi_target_erl0.h" 2162306a36Sopenharmony_ci#include "iscsi_target_erl1.h" 2262306a36Sopenharmony_ci#include "iscsi_target_erl2.h" 2362306a36Sopenharmony_ci#include "iscsi_target.h" 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* 2662306a36Sopenharmony_ci * FIXME: Does RData SNACK apply here as well? 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_civoid iscsit_create_conn_recovery_datain_values( 2962306a36Sopenharmony_ci struct iscsit_cmd *cmd, 3062306a36Sopenharmony_ci __be32 exp_data_sn) 3162306a36Sopenharmony_ci{ 3262306a36Sopenharmony_ci u32 data_sn = 0; 3362306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci cmd->next_burst_len = 0; 3662306a36Sopenharmony_ci cmd->read_data_done = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci while (be32_to_cpu(exp_data_sn) > data_sn) { 3962306a36Sopenharmony_ci if ((cmd->next_burst_len + 4062306a36Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength) < 4162306a36Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) { 4262306a36Sopenharmony_ci cmd->read_data_done += 4362306a36Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength; 4462306a36Sopenharmony_ci cmd->next_burst_len += 4562306a36Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength; 4662306a36Sopenharmony_ci } else { 4762306a36Sopenharmony_ci cmd->read_data_done += 4862306a36Sopenharmony_ci (conn->sess->sess_ops->MaxBurstLength - 4962306a36Sopenharmony_ci cmd->next_burst_len); 5062306a36Sopenharmony_ci cmd->next_burst_len = 0; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci data_sn++; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_civoid iscsit_create_conn_recovery_dataout_values( 5762306a36Sopenharmony_ci struct iscsit_cmd *cmd) 5862306a36Sopenharmony_ci{ 5962306a36Sopenharmony_ci u32 write_data_done = 0; 6062306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci cmd->data_sn = 0; 6362306a36Sopenharmony_ci cmd->next_burst_len = 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci while (cmd->write_data_done > write_data_done) { 6662306a36Sopenharmony_ci if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <= 6762306a36Sopenharmony_ci cmd->write_data_done) 6862306a36Sopenharmony_ci write_data_done += conn->sess->sess_ops->MaxBurstLength; 6962306a36Sopenharmony_ci else 7062306a36Sopenharmony_ci break; 7162306a36Sopenharmony_ci } 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci cmd->write_data_done = write_data_done; 7462306a36Sopenharmony_ci} 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int iscsit_attach_active_connection_recovery_entry( 7762306a36Sopenharmony_ci struct iscsit_session *sess, 7862306a36Sopenharmony_ci struct iscsi_conn_recovery *cr) 7962306a36Sopenharmony_ci{ 8062306a36Sopenharmony_ci spin_lock(&sess->cr_a_lock); 8162306a36Sopenharmony_ci list_add_tail(&cr->cr_list, &sess->cr_active_list); 8262306a36Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci return 0; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic int iscsit_attach_inactive_connection_recovery_entry( 8862306a36Sopenharmony_ci struct iscsit_session *sess, 8962306a36Sopenharmony_ci struct iscsi_conn_recovery *cr) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci spin_lock(&sess->cr_i_lock); 9262306a36Sopenharmony_ci list_add_tail(&cr->cr_list, &sess->cr_inactive_list); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sess->conn_recovery_count++; 9562306a36Sopenharmony_ci pr_debug("Incremented connection recovery count to %u for" 9662306a36Sopenharmony_ci " SID: %u\n", sess->conn_recovery_count, sess->sid); 9762306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci return 0; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry( 10362306a36Sopenharmony_ci struct iscsit_session *sess, 10462306a36Sopenharmony_ci u16 cid) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct iscsi_conn_recovery *cr; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci spin_lock(&sess->cr_i_lock); 10962306a36Sopenharmony_ci list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) { 11062306a36Sopenharmony_ci if (cr->cid == cid) { 11162306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 11262306a36Sopenharmony_ci return cr; 11362306a36Sopenharmony_ci } 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci return NULL; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_civoid iscsit_free_connection_recovery_entries(struct iscsit_session *sess) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct iscsit_cmd *cmd, *cmd_tmp; 12362306a36Sopenharmony_ci struct iscsi_conn_recovery *cr, *cr_tmp; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spin_lock(&sess->cr_a_lock); 12662306a36Sopenharmony_ci list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) { 12762306a36Sopenharmony_ci list_del(&cr->cr_list); 12862306a36Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 13162306a36Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 13262306a36Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 13562306a36Sopenharmony_ci cmd->conn = NULL; 13662306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 13762306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 13862306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 13962306a36Sopenharmony_ci } 14062306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 14162306a36Sopenharmony_ci spin_lock(&sess->cr_a_lock); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci kfree(cr); 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock(&sess->cr_i_lock); 14862306a36Sopenharmony_ci list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) { 14962306a36Sopenharmony_ci list_del(&cr->cr_list); 15062306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 15362306a36Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 15462306a36Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 15762306a36Sopenharmony_ci cmd->conn = NULL; 15862306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 15962306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 16062306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 16362306a36Sopenharmony_ci spin_lock(&sess->cr_i_lock); 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci kfree(cr); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ciint iscsit_remove_active_connection_recovery_entry( 17162306a36Sopenharmony_ci struct iscsi_conn_recovery *cr, 17262306a36Sopenharmony_ci struct iscsit_session *sess) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci spin_lock(&sess->cr_a_lock); 17562306a36Sopenharmony_ci list_del(&cr->cr_list); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci sess->conn_recovery_count--; 17862306a36Sopenharmony_ci pr_debug("Decremented connection recovery count to %u for" 17962306a36Sopenharmony_ci " SID: %u\n", sess->conn_recovery_count, sess->sid); 18062306a36Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci kfree(cr); 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci return 0; 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void iscsit_remove_inactive_connection_recovery_entry( 18862306a36Sopenharmony_ci struct iscsi_conn_recovery *cr, 18962306a36Sopenharmony_ci struct iscsit_session *sess) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci spin_lock(&sess->cr_i_lock); 19262306a36Sopenharmony_ci list_del(&cr->cr_list); 19362306a36Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci/* 19762306a36Sopenharmony_ci * Called with cr->conn_recovery_cmd_lock help. 19862306a36Sopenharmony_ci */ 19962306a36Sopenharmony_ciint iscsit_remove_cmd_from_connection_recovery( 20062306a36Sopenharmony_ci struct iscsit_cmd *cmd, 20162306a36Sopenharmony_ci struct iscsit_session *sess) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci struct iscsi_conn_recovery *cr; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (!cmd->cr) { 20662306a36Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 20762306a36Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 20862306a36Sopenharmony_ci BUG(); 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci cr = cmd->cr; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 21362306a36Sopenharmony_ci return --cr->cmd_count; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_civoid iscsit_discard_cr_cmds_by_expstatsn( 21762306a36Sopenharmony_ci struct iscsi_conn_recovery *cr, 21862306a36Sopenharmony_ci u32 exp_statsn) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci u32 dropped_count = 0; 22162306a36Sopenharmony_ci struct iscsit_cmd *cmd, *cmd_tmp; 22262306a36Sopenharmony_ci struct iscsit_session *sess = cr->sess; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 22562306a36Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 22662306a36Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) && 22962306a36Sopenharmony_ci (cmd->deferred_i_state != ISTATE_REMOVE)) || 23062306a36Sopenharmony_ci (cmd->stat_sn >= exp_statsn)) { 23162306a36Sopenharmony_ci continue; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci dropped_count++; 23562306a36Sopenharmony_ci pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:" 23662306a36Sopenharmony_ci " 0x%08x, CID: %hu.\n", cmd->init_task_tag, 23762306a36Sopenharmony_ci cmd->stat_sn, cr->cid); 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci iscsit_remove_cmd_from_connection_recovery(cmd, sess); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 24262306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 24362306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci pr_debug("Dropped %u total acknowledged commands on" 24862306a36Sopenharmony_ci " CID: %hu less than old ExpStatSN: 0x%08x\n", 24962306a36Sopenharmony_ci dropped_count, cr->cid, exp_statsn); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (!cr->cmd_count) { 25262306a36Sopenharmony_ci pr_debug("No commands to be reassigned for failed" 25362306a36Sopenharmony_ci " connection CID: %hu on SID: %u\n", 25462306a36Sopenharmony_ci cr->cid, sess->sid); 25562306a36Sopenharmony_ci iscsit_remove_inactive_connection_recovery_entry(cr, sess); 25662306a36Sopenharmony_ci iscsit_attach_active_connection_recovery_entry(sess, cr); 25762306a36Sopenharmony_ci pr_debug("iSCSI connection recovery successful for CID:" 25862306a36Sopenharmony_ci " %hu on SID: %u\n", cr->cid, sess->sid); 25962306a36Sopenharmony_ci iscsit_remove_active_connection_recovery_entry(cr, sess); 26062306a36Sopenharmony_ci } else { 26162306a36Sopenharmony_ci iscsit_remove_inactive_connection_recovery_entry(cr, sess); 26262306a36Sopenharmony_ci iscsit_attach_active_connection_recovery_entry(sess, cr); 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ciint iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsit_conn *conn) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci u32 dropped_count = 0; 26962306a36Sopenharmony_ci struct iscsit_cmd *cmd, *cmd_tmp; 27062306a36Sopenharmony_ci struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; 27162306a36Sopenharmony_ci struct iscsit_session *sess = conn->sess; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci mutex_lock(&sess->cmdsn_mutex); 27462306a36Sopenharmony_ci list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, 27562306a36Sopenharmony_ci &sess->sess_ooo_cmdsn_list, ooo_list) { 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci if (ooo_cmdsn->cid != conn->cid) 27862306a36Sopenharmony_ci continue; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci dropped_count++; 28162306a36Sopenharmony_ci pr_debug("Dropping unacknowledged CmdSN:" 28262306a36Sopenharmony_ci " 0x%08x during connection recovery on CID: %hu\n", 28362306a36Sopenharmony_ci ooo_cmdsn->cmdsn, conn->cid); 28462306a36Sopenharmony_ci iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci mutex_unlock(&sess->cmdsn_mutex); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 28962306a36Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) { 29062306a36Sopenharmony_ci if (!(cmd->cmd_flags & ICF_OOO_CMDSN)) 29162306a36Sopenharmony_ci continue; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 29662306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 29762306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci pr_debug("Dropped %u total unacknowledged commands on CID:" 30262306a36Sopenharmony_ci " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid, 30362306a36Sopenharmony_ci sess->exp_cmd_sn); 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ciint iscsit_prepare_cmds_for_reallegiance(struct iscsit_conn *conn) 30862306a36Sopenharmony_ci{ 30962306a36Sopenharmony_ci u32 cmd_count = 0; 31062306a36Sopenharmony_ci struct iscsit_cmd *cmd, *cmd_tmp; 31162306a36Sopenharmony_ci struct iscsi_conn_recovery *cr; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* 31462306a36Sopenharmony_ci * Allocate an struct iscsi_conn_recovery for this connection. 31562306a36Sopenharmony_ci * Each struct iscsit_cmd contains an struct iscsi_conn_recovery pointer 31662306a36Sopenharmony_ci * (struct iscsit_cmd->cr) so we need to allocate this before preparing the 31762306a36Sopenharmony_ci * connection's command list for connection recovery. 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL); 32062306a36Sopenharmony_ci if (!cr) { 32162306a36Sopenharmony_ci pr_err("Unable to allocate memory for" 32262306a36Sopenharmony_ci " struct iscsi_conn_recovery.\n"); 32362306a36Sopenharmony_ci return -1; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci INIT_LIST_HEAD(&cr->cr_list); 32662306a36Sopenharmony_ci INIT_LIST_HEAD(&cr->conn_recovery_cmd_list); 32762306a36Sopenharmony_ci spin_lock_init(&cr->conn_recovery_cmd_lock); 32862306a36Sopenharmony_ci /* 32962306a36Sopenharmony_ci * Only perform connection recovery on ISCSI_OP_SCSI_CMD or 33062306a36Sopenharmony_ci * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call 33162306a36Sopenharmony_ci * list_del_init(&cmd->i_conn_node); to release the command to the 33262306a36Sopenharmony_ci * session pool and remove it from the connection's list. 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Also stop the DataOUT timer, which will be restarted after 33562306a36Sopenharmony_ci * sending the TMR response. 33662306a36Sopenharmony_ci */ 33762306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 33862306a36Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) { 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) && 34162306a36Sopenharmony_ci (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) { 34262306a36Sopenharmony_ci pr_debug("Not performing reallegiance on" 34362306a36Sopenharmony_ci " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x," 34462306a36Sopenharmony_ci " CID: %hu\n", cmd->iscsi_opcode, 34562306a36Sopenharmony_ci cmd->init_task_tag, cmd->cmd_sn, conn->cid); 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 34862306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 34962306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 35062306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Special case where commands greater than or equal to 35662306a36Sopenharmony_ci * the session's ExpCmdSN are attached to the connection 35762306a36Sopenharmony_ci * list but not to the out of order CmdSN list. The one 35862306a36Sopenharmony_ci * obvious case is when a command with immediate data 35962306a36Sopenharmony_ci * attached must only check the CmdSN against ExpCmdSN 36062306a36Sopenharmony_ci * after the data is received. The special case below 36162306a36Sopenharmony_ci * is when the connection fails before data is received, 36262306a36Sopenharmony_ci * but also may apply to other PDUs, so it has been 36362306a36Sopenharmony_ci * made generic here. 36462306a36Sopenharmony_ci */ 36562306a36Sopenharmony_ci if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && 36662306a36Sopenharmony_ci iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) { 36762306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 36862306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 36962306a36Sopenharmony_ci iscsit_free_cmd(cmd, true); 37062306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 37162306a36Sopenharmony_ci continue; 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci cmd_count++; 37562306a36Sopenharmony_ci pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x," 37662306a36Sopenharmony_ci " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for" 37762306a36Sopenharmony_ci " reallegiance.\n", cmd->iscsi_opcode, 37862306a36Sopenharmony_ci cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn, 37962306a36Sopenharmony_ci conn->cid); 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci cmd->deferred_i_state = cmd->i_state; 38262306a36Sopenharmony_ci cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci if (cmd->data_direction == DMA_TO_DEVICE) 38562306a36Sopenharmony_ci iscsit_stop_dataout_timer(cmd); 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci cmd->sess = conn->sess; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci list_del_init(&cmd->i_conn_node); 39062306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci iscsit_free_all_datain_reqs(cmd); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci transport_wait_for_tasks(&cmd->se_cmd); 39562306a36Sopenharmony_ci /* 39662306a36Sopenharmony_ci * Add the struct iscsit_cmd to the connection recovery cmd list 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 39962306a36Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &cr->conn_recovery_cmd_list); 40062306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 40362306a36Sopenharmony_ci cmd->cr = cr; 40462306a36Sopenharmony_ci cmd->conn = NULL; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 40762306a36Sopenharmony_ci /* 40862306a36Sopenharmony_ci * Fill in the various values in the preallocated struct iscsi_conn_recovery. 40962306a36Sopenharmony_ci */ 41062306a36Sopenharmony_ci cr->cid = conn->cid; 41162306a36Sopenharmony_ci cr->cmd_count = cmd_count; 41262306a36Sopenharmony_ci cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength; 41362306a36Sopenharmony_ci cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength; 41462306a36Sopenharmony_ci cr->sess = conn->sess; 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr); 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci return 0; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ciint iscsit_connection_recovery_transport_reset(struct iscsit_conn *conn) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci atomic_set(&conn->connection_recovery, 1); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci if (iscsit_close_connection(conn) < 0) 42662306a36Sopenharmony_ci return -1; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return 0; 42962306a36Sopenharmony_ci} 430