18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/******************************************************************************* 38c2ecf20Sopenharmony_ci * This file contains error recovery level two functions used by 48c2ecf20Sopenharmony_ci * the iSCSI Target driver. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci ******************************************************************************/ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h> 148c2ecf20Sopenharmony_ci#include <target/target_core_base.h> 158c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h> 188c2ecf20Sopenharmony_ci#include "iscsi_target_datain_values.h" 198c2ecf20Sopenharmony_ci#include "iscsi_target_util.h" 208c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h" 218c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h" 228c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h" 238c2ecf20Sopenharmony_ci#include "iscsi_target.h" 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * FIXME: Does RData SNACK apply here as well? 278c2ecf20Sopenharmony_ci */ 288c2ecf20Sopenharmony_civoid iscsit_create_conn_recovery_datain_values( 298c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 308c2ecf20Sopenharmony_ci __be32 exp_data_sn) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci u32 data_sn = 0; 338c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci cmd->next_burst_len = 0; 368c2ecf20Sopenharmony_ci cmd->read_data_done = 0; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci while (be32_to_cpu(exp_data_sn) > data_sn) { 398c2ecf20Sopenharmony_ci if ((cmd->next_burst_len + 408c2ecf20Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength) < 418c2ecf20Sopenharmony_ci conn->sess->sess_ops->MaxBurstLength) { 428c2ecf20Sopenharmony_ci cmd->read_data_done += 438c2ecf20Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength; 448c2ecf20Sopenharmony_ci cmd->next_burst_len += 458c2ecf20Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength; 468c2ecf20Sopenharmony_ci } else { 478c2ecf20Sopenharmony_ci cmd->read_data_done += 488c2ecf20Sopenharmony_ci (conn->sess->sess_ops->MaxBurstLength - 498c2ecf20Sopenharmony_ci cmd->next_burst_len); 508c2ecf20Sopenharmony_ci cmd->next_burst_len = 0; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci data_sn++; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_civoid iscsit_create_conn_recovery_dataout_values( 578c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd) 588c2ecf20Sopenharmony_ci{ 598c2ecf20Sopenharmony_ci u32 write_data_done = 0; 608c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci cmd->data_sn = 0; 638c2ecf20Sopenharmony_ci cmd->next_burst_len = 0; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci while (cmd->write_data_done > write_data_done) { 668c2ecf20Sopenharmony_ci if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <= 678c2ecf20Sopenharmony_ci cmd->write_data_done) 688c2ecf20Sopenharmony_ci write_data_done += conn->sess->sess_ops->MaxBurstLength; 698c2ecf20Sopenharmony_ci else 708c2ecf20Sopenharmony_ci break; 718c2ecf20Sopenharmony_ci } 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci cmd->write_data_done = write_data_done; 748c2ecf20Sopenharmony_ci} 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int iscsit_attach_active_connection_recovery_entry( 778c2ecf20Sopenharmony_ci struct iscsi_session *sess, 788c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr) 798c2ecf20Sopenharmony_ci{ 808c2ecf20Sopenharmony_ci spin_lock(&sess->cr_a_lock); 818c2ecf20Sopenharmony_ci list_add_tail(&cr->cr_list, &sess->cr_active_list); 828c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci return 0; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int iscsit_attach_inactive_connection_recovery_entry( 888c2ecf20Sopenharmony_ci struct iscsi_session *sess, 898c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci spin_lock(&sess->cr_i_lock); 928c2ecf20Sopenharmony_ci list_add_tail(&cr->cr_list, &sess->cr_inactive_list); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci sess->conn_recovery_count++; 958c2ecf20Sopenharmony_ci pr_debug("Incremented connection recovery count to %u for" 968c2ecf20Sopenharmony_ci " SID: %u\n", sess->conn_recovery_count, sess->sid); 978c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci return 0; 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry( 1038c2ecf20Sopenharmony_ci struct iscsi_session *sess, 1048c2ecf20Sopenharmony_ci u16 cid) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci spin_lock(&sess->cr_i_lock); 1098c2ecf20Sopenharmony_ci list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) { 1108c2ecf20Sopenharmony_ci if (cr->cid == cid) { 1118c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 1128c2ecf20Sopenharmony_ci return cr; 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci return NULL; 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_civoid iscsit_free_connection_recovery_entries(struct iscsi_session *sess) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, *cmd_tmp; 1238c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr, *cr_tmp; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci spin_lock(&sess->cr_a_lock); 1268c2ecf20Sopenharmony_ci list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) { 1278c2ecf20Sopenharmony_ci list_del(&cr->cr_list); 1288c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 1318c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 1328c2ecf20Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 1358c2ecf20Sopenharmony_ci cmd->conn = NULL; 1368c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 1378c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 1388c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 1418c2ecf20Sopenharmony_ci spin_lock(&sess->cr_a_lock); 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci kfree(cr); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci spin_lock(&sess->cr_i_lock); 1488c2ecf20Sopenharmony_ci list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) { 1498c2ecf20Sopenharmony_ci list_del(&cr->cr_list); 1508c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 1538c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 1548c2ecf20Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 1578c2ecf20Sopenharmony_ci cmd->conn = NULL; 1588c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 1598c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 1608c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 1638c2ecf20Sopenharmony_ci spin_lock(&sess->cr_i_lock); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci kfree(cr); 1668c2ecf20Sopenharmony_ci } 1678c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 1688c2ecf20Sopenharmony_ci} 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ciint iscsit_remove_active_connection_recovery_entry( 1718c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr, 1728c2ecf20Sopenharmony_ci struct iscsi_session *sess) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci spin_lock(&sess->cr_a_lock); 1758c2ecf20Sopenharmony_ci list_del(&cr->cr_list); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci sess->conn_recovery_count--; 1788c2ecf20Sopenharmony_ci pr_debug("Decremented connection recovery count to %u for" 1798c2ecf20Sopenharmony_ci " SID: %u\n", sess->conn_recovery_count, sess->sid); 1808c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_a_lock); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci kfree(cr); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci return 0; 1858c2ecf20Sopenharmony_ci} 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic void iscsit_remove_inactive_connection_recovery_entry( 1888c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr, 1898c2ecf20Sopenharmony_ci struct iscsi_session *sess) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci spin_lock(&sess->cr_i_lock); 1928c2ecf20Sopenharmony_ci list_del(&cr->cr_list); 1938c2ecf20Sopenharmony_ci spin_unlock(&sess->cr_i_lock); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci/* 1978c2ecf20Sopenharmony_ci * Called with cr->conn_recovery_cmd_lock help. 1988c2ecf20Sopenharmony_ci */ 1998c2ecf20Sopenharmony_ciint iscsit_remove_cmd_from_connection_recovery( 2008c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 2018c2ecf20Sopenharmony_ci struct iscsi_session *sess) 2028c2ecf20Sopenharmony_ci{ 2038c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!cmd->cr) { 2068c2ecf20Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 2078c2ecf20Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 2088c2ecf20Sopenharmony_ci BUG(); 2098c2ecf20Sopenharmony_ci } 2108c2ecf20Sopenharmony_ci cr = cmd->cr; 2118c2ecf20Sopenharmony_ci 2128c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 2138c2ecf20Sopenharmony_ci return --cr->cmd_count; 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_civoid iscsit_discard_cr_cmds_by_expstatsn( 2178c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr, 2188c2ecf20Sopenharmony_ci u32 exp_statsn) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci u32 dropped_count = 0; 2218c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, *cmd_tmp; 2228c2ecf20Sopenharmony_ci struct iscsi_session *sess = cr->sess; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 2258c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, 2268c2ecf20Sopenharmony_ci &cr->conn_recovery_cmd_list, i_conn_node) { 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) && 2298c2ecf20Sopenharmony_ci (cmd->deferred_i_state != ISTATE_REMOVE)) || 2308c2ecf20Sopenharmony_ci (cmd->stat_sn >= exp_statsn)) { 2318c2ecf20Sopenharmony_ci continue; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci dropped_count++; 2358c2ecf20Sopenharmony_ci pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:" 2368c2ecf20Sopenharmony_ci " 0x%08x, CID: %hu.\n", cmd->init_task_tag, 2378c2ecf20Sopenharmony_ci cmd->stat_sn, cr->cid); 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci iscsit_remove_cmd_from_connection_recovery(cmd, sess); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 2428c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 2438c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci pr_debug("Dropped %u total acknowledged commands on" 2488c2ecf20Sopenharmony_ci " CID: %hu less than old ExpStatSN: 0x%08x\n", 2498c2ecf20Sopenharmony_ci dropped_count, cr->cid, exp_statsn); 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!cr->cmd_count) { 2528c2ecf20Sopenharmony_ci pr_debug("No commands to be reassigned for failed" 2538c2ecf20Sopenharmony_ci " connection CID: %hu on SID: %u\n", 2548c2ecf20Sopenharmony_ci cr->cid, sess->sid); 2558c2ecf20Sopenharmony_ci iscsit_remove_inactive_connection_recovery_entry(cr, sess); 2568c2ecf20Sopenharmony_ci iscsit_attach_active_connection_recovery_entry(sess, cr); 2578c2ecf20Sopenharmony_ci pr_debug("iSCSI connection recovery successful for CID:" 2588c2ecf20Sopenharmony_ci " %hu on SID: %u\n", cr->cid, sess->sid); 2598c2ecf20Sopenharmony_ci iscsit_remove_active_connection_recovery_entry(cr, sess); 2608c2ecf20Sopenharmony_ci } else { 2618c2ecf20Sopenharmony_ci iscsit_remove_inactive_connection_recovery_entry(cr, sess); 2628c2ecf20Sopenharmony_ci iscsit_attach_active_connection_recovery_entry(sess, cr); 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ciint iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci u32 dropped_count = 0; 2698c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, *cmd_tmp; 2708c2ecf20Sopenharmony_ci struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp; 2718c2ecf20Sopenharmony_ci struct iscsi_session *sess = conn->sess; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci mutex_lock(&sess->cmdsn_mutex); 2748c2ecf20Sopenharmony_ci list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp, 2758c2ecf20Sopenharmony_ci &sess->sess_ooo_cmdsn_list, ooo_list) { 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci if (ooo_cmdsn->cid != conn->cid) 2788c2ecf20Sopenharmony_ci continue; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dropped_count++; 2818c2ecf20Sopenharmony_ci pr_debug("Dropping unacknowledged CmdSN:" 2828c2ecf20Sopenharmony_ci " 0x%08x during connection recovery on CID: %hu\n", 2838c2ecf20Sopenharmony_ci ooo_cmdsn->cmdsn, conn->cid); 2848c2ecf20Sopenharmony_ci iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn); 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci mutex_unlock(&sess->cmdsn_mutex); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 2898c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) { 2908c2ecf20Sopenharmony_ci if (!(cmd->cmd_flags & ICF_OOO_CMDSN)) 2918c2ecf20Sopenharmony_ci continue; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 2968c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 2978c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci pr_debug("Dropped %u total unacknowledged commands on CID:" 3028c2ecf20Sopenharmony_ci " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid, 3038c2ecf20Sopenharmony_ci sess->exp_cmd_sn); 3048c2ecf20Sopenharmony_ci return 0; 3058c2ecf20Sopenharmony_ci} 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ciint iscsit_prepare_cmds_for_reallegiance(struct iscsi_conn *conn) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci u32 cmd_count = 0; 3108c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, *cmd_tmp; 3118c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr; 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci /* 3148c2ecf20Sopenharmony_ci * Allocate an struct iscsi_conn_recovery for this connection. 3158c2ecf20Sopenharmony_ci * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer 3168c2ecf20Sopenharmony_ci * (struct iscsi_cmd->cr) so we need to allocate this before preparing the 3178c2ecf20Sopenharmony_ci * connection's command list for connection recovery. 3188c2ecf20Sopenharmony_ci */ 3198c2ecf20Sopenharmony_ci cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL); 3208c2ecf20Sopenharmony_ci if (!cr) { 3218c2ecf20Sopenharmony_ci pr_err("Unable to allocate memory for" 3228c2ecf20Sopenharmony_ci " struct iscsi_conn_recovery.\n"); 3238c2ecf20Sopenharmony_ci return -1; 3248c2ecf20Sopenharmony_ci } 3258c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cr->cr_list); 3268c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&cr->conn_recovery_cmd_list); 3278c2ecf20Sopenharmony_ci spin_lock_init(&cr->conn_recovery_cmd_lock); 3288c2ecf20Sopenharmony_ci /* 3298c2ecf20Sopenharmony_ci * Only perform connection recovery on ISCSI_OP_SCSI_CMD or 3308c2ecf20Sopenharmony_ci * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call 3318c2ecf20Sopenharmony_ci * list_del_init(&cmd->i_conn_node); to release the command to the 3328c2ecf20Sopenharmony_ci * session pool and remove it from the connection's list. 3338c2ecf20Sopenharmony_ci * 3348c2ecf20Sopenharmony_ci * Also stop the DataOUT timer, which will be restarted after 3358c2ecf20Sopenharmony_ci * sending the TMR response. 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 3388c2ecf20Sopenharmony_ci list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) { 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) && 3418c2ecf20Sopenharmony_ci (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) { 3428c2ecf20Sopenharmony_ci pr_debug("Not performing reallegiance on" 3438c2ecf20Sopenharmony_ci " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x," 3448c2ecf20Sopenharmony_ci " CID: %hu\n", cmd->iscsi_opcode, 3458c2ecf20Sopenharmony_ci cmd->init_task_tag, cmd->cmd_sn, conn->cid); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 3488c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 3498c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 3508c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 3518c2ecf20Sopenharmony_ci continue; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci /* 3558c2ecf20Sopenharmony_ci * Special case where commands greater than or equal to 3568c2ecf20Sopenharmony_ci * the session's ExpCmdSN are attached to the connection 3578c2ecf20Sopenharmony_ci * list but not to the out of order CmdSN list. The one 3588c2ecf20Sopenharmony_ci * obvious case is when a command with immediate data 3598c2ecf20Sopenharmony_ci * attached must only check the CmdSN against ExpCmdSN 3608c2ecf20Sopenharmony_ci * after the data is received. The special case below 3618c2ecf20Sopenharmony_ci * is when the connection fails before data is received, 3628c2ecf20Sopenharmony_ci * but also may apply to other PDUs, so it has been 3638c2ecf20Sopenharmony_ci * made generic here. 3648c2ecf20Sopenharmony_ci */ 3658c2ecf20Sopenharmony_ci if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd && 3668c2ecf20Sopenharmony_ci iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) { 3678c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 3688c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 3698c2ecf20Sopenharmony_ci iscsit_free_cmd(cmd, true); 3708c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 3718c2ecf20Sopenharmony_ci continue; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci cmd_count++; 3758c2ecf20Sopenharmony_ci pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x," 3768c2ecf20Sopenharmony_ci " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for" 3778c2ecf20Sopenharmony_ci " reallegiance.\n", cmd->iscsi_opcode, 3788c2ecf20Sopenharmony_ci cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn, 3798c2ecf20Sopenharmony_ci conn->cid); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci cmd->deferred_i_state = cmd->i_state; 3828c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci if (cmd->data_direction == DMA_TO_DEVICE) 3858c2ecf20Sopenharmony_ci iscsit_stop_dataout_timer(cmd); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci cmd->sess = conn->sess; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci list_del_init(&cmd->i_conn_node); 3908c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci iscsit_free_all_datain_reqs(cmd); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci transport_wait_for_tasks(&cmd->se_cmd); 3958c2ecf20Sopenharmony_ci /* 3968c2ecf20Sopenharmony_ci * Add the struct iscsi_cmd to the connection recovery cmd list 3978c2ecf20Sopenharmony_ci */ 3988c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 3998c2ecf20Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &cr->conn_recovery_cmd_list); 4008c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 4038c2ecf20Sopenharmony_ci cmd->cr = cr; 4048c2ecf20Sopenharmony_ci cmd->conn = NULL; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 4078c2ecf20Sopenharmony_ci /* 4088c2ecf20Sopenharmony_ci * Fill in the various values in the preallocated struct iscsi_conn_recovery. 4098c2ecf20Sopenharmony_ci */ 4108c2ecf20Sopenharmony_ci cr->cid = conn->cid; 4118c2ecf20Sopenharmony_ci cr->cmd_count = cmd_count; 4128c2ecf20Sopenharmony_ci cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength; 4138c2ecf20Sopenharmony_ci cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength; 4148c2ecf20Sopenharmony_ci cr->sess = conn->sess; 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_ci iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci return 0; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ciint iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci atomic_set(&conn->connection_recovery, 1); 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci if (iscsit_close_connection(conn) < 0) 4268c2ecf20Sopenharmony_ci return -1; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return 0; 4298c2ecf20Sopenharmony_ci} 430