18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/******************************************************************************* 38c2ecf20Sopenharmony_ci * This file contains the iSCSI Target specific Task Management functions. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci ******************************************************************************/ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 128c2ecf20Sopenharmony_ci#include <scsi/scsi_proto.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#include <target/iscsi/iscsi_transport.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h> 198c2ecf20Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h" 208c2ecf20Sopenharmony_ci#include "iscsi_target_datain_values.h" 218c2ecf20Sopenharmony_ci#include "iscsi_target_device.h" 228c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h" 238c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h" 248c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h" 258c2ecf20Sopenharmony_ci#include "iscsi_target_tmr.h" 268c2ecf20Sopenharmony_ci#include "iscsi_target_tpg.h" 278c2ecf20Sopenharmony_ci#include "iscsi_target_util.h" 288c2ecf20Sopenharmony_ci#include "iscsi_target.h" 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ciu8 iscsit_tmr_abort_task( 318c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 328c2ecf20Sopenharmony_ci unsigned char *buf) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci struct iscsi_cmd *ref_cmd; 358c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 368c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 378c2ecf20Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 388c2ecf20Sopenharmony_ci struct iscsi_tm *hdr = (struct iscsi_tm *) buf; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt); 418c2ecf20Sopenharmony_ci if (!ref_cmd) { 428c2ecf20Sopenharmony_ci pr_err("Unable to locate RefTaskTag: 0x%08x on CID:" 438c2ecf20Sopenharmony_ci " %hu.\n", hdr->rtt, conn->cid); 448c2ecf20Sopenharmony_ci return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), conn->sess->exp_cmd_sn) && 458c2ecf20Sopenharmony_ci iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), (u32) atomic_read(&conn->sess->max_cmd_sn))) ? 468c2ecf20Sopenharmony_ci ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) { 498c2ecf20Sopenharmony_ci pr_err("RefCmdSN 0x%08x does not equal" 508c2ecf20Sopenharmony_ci " task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n", 518c2ecf20Sopenharmony_ci hdr->refcmdsn, ref_cmd->cmd_sn); 528c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci se_tmr->ref_task_tag = (__force u32)hdr->rtt; 568c2ecf20Sopenharmony_ci tmr_req->ref_cmd = ref_cmd; 578c2ecf20Sopenharmony_ci tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_COMPLETE; 608c2ecf20Sopenharmony_ci} 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci/* 638c2ecf20Sopenharmony_ci * Called from iscsit_handle_task_mgt_cmd(). 648c2ecf20Sopenharmony_ci */ 658c2ecf20Sopenharmony_ciint iscsit_tmr_task_warm_reset( 668c2ecf20Sopenharmony_ci struct iscsi_conn *conn, 678c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 688c2ecf20Sopenharmony_ci unsigned char *buf) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci struct iscsi_session *sess = conn->sess; 718c2ecf20Sopenharmony_ci struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci if (!na->tmr_warm_reset) { 748c2ecf20Sopenharmony_ci pr_err("TMR Opcode TARGET_WARM_RESET authorization" 758c2ecf20Sopenharmony_ci " failed for Initiator Node: %s\n", 768c2ecf20Sopenharmony_ci sess->se_sess->se_node_acl->initiatorname); 778c2ecf20Sopenharmony_ci return -1; 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * Do the real work in transport_generic_do_tmr(). 818c2ecf20Sopenharmony_ci */ 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ciint iscsit_tmr_task_cold_reset( 868c2ecf20Sopenharmony_ci struct iscsi_conn *conn, 878c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 888c2ecf20Sopenharmony_ci unsigned char *buf) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct iscsi_session *sess = conn->sess; 918c2ecf20Sopenharmony_ci struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci if (!na->tmr_cold_reset) { 948c2ecf20Sopenharmony_ci pr_err("TMR Opcode TARGET_COLD_RESET authorization" 958c2ecf20Sopenharmony_ci " failed for Initiator Node: %s\n", 968c2ecf20Sopenharmony_ci sess->se_sess->se_node_acl->initiatorname); 978c2ecf20Sopenharmony_ci return -1; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * Do the real work in transport_generic_do_tmr(). 1018c2ecf20Sopenharmony_ci */ 1028c2ecf20Sopenharmony_ci return 0; 1038c2ecf20Sopenharmony_ci} 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciu8 iscsit_tmr_task_reassign( 1068c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 1078c2ecf20Sopenharmony_ci unsigned char *buf) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci struct iscsi_cmd *ref_cmd = NULL; 1108c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 1118c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr = NULL; 1128c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 1138c2ecf20Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 1148c2ecf20Sopenharmony_ci struct iscsi_tm *hdr = (struct iscsi_tm *) buf; 1158c2ecf20Sopenharmony_ci u64 ret, ref_lun; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x," 1188c2ecf20Sopenharmony_ci " RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n", 1198c2ecf20Sopenharmony_ci hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) { 1228c2ecf20Sopenharmony_ci pr_err("TMR TASK_REASSIGN not supported in ERL<2," 1238c2ecf20Sopenharmony_ci " ignoring request.\n"); 1248c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_NOT_SUPPORTED; 1258c2ecf20Sopenharmony_ci } 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt); 1288c2ecf20Sopenharmony_ci if (ret == -2) { 1298c2ecf20Sopenharmony_ci pr_err("Command ITT: 0x%08x is still alligent to CID:" 1308c2ecf20Sopenharmony_ci " %hu\n", ref_cmd->init_task_tag, cr->cid); 1318c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_TASK_ALLEGIANT; 1328c2ecf20Sopenharmony_ci } else if (ret == -1) { 1338c2ecf20Sopenharmony_ci pr_err("Unable to locate RefTaskTag: 0x%08x in" 1348c2ecf20Sopenharmony_ci " connection recovery command list.\n", hdr->rtt); 1358c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_NO_TASK; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci /* 1388c2ecf20Sopenharmony_ci * Temporary check to prevent connection recovery for 1398c2ecf20Sopenharmony_ci * connections with a differing Max*DataSegmentLength. 1408c2ecf20Sopenharmony_ci */ 1418c2ecf20Sopenharmony_ci if (cr->maxrecvdatasegmentlength != 1428c2ecf20Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength) { 1438c2ecf20Sopenharmony_ci pr_err("Unable to perform connection recovery for" 1448c2ecf20Sopenharmony_ci " differing MaxRecvDataSegmentLength, rejecting" 1458c2ecf20Sopenharmony_ci " TMR TASK_REASSIGN.\n"); 1468c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci if (cr->maxxmitdatasegmentlength != 1498c2ecf20Sopenharmony_ci conn->conn_ops->MaxXmitDataSegmentLength) { 1508c2ecf20Sopenharmony_ci pr_err("Unable to perform connection recovery for" 1518c2ecf20Sopenharmony_ci " differing MaxXmitDataSegmentLength, rejecting" 1528c2ecf20Sopenharmony_ci " TMR TASK_REASSIGN.\n"); 1538c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci ref_lun = scsilun_to_int(&hdr->lun); 1578c2ecf20Sopenharmony_ci if (ref_lun != ref_cmd->se_cmd.orig_fe_lun) { 1588c2ecf20Sopenharmony_ci pr_err("Unable to perform connection recovery for" 1598c2ecf20Sopenharmony_ci " differing ref_lun: %llu ref_cmd orig_fe_lun: %llu\n", 1608c2ecf20Sopenharmony_ci ref_lun, ref_cmd->se_cmd.orig_fe_lun); 1618c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 1628c2ecf20Sopenharmony_ci } 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci se_tmr->ref_task_tag = (__force u32)hdr->rtt; 1658c2ecf20Sopenharmony_ci tmr_req->ref_cmd = ref_cmd; 1668c2ecf20Sopenharmony_ci tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); 1678c2ecf20Sopenharmony_ci tmr_req->conn_recovery = cr; 1688c2ecf20Sopenharmony_ci tmr_req->task_reassign = 1; 1698c2ecf20Sopenharmony_ci /* 1708c2ecf20Sopenharmony_ci * Command can now be reassigned to a new connection. 1718c2ecf20Sopenharmony_ci * The task management response must be sent before the 1728c2ecf20Sopenharmony_ci * reassignment actually happens. See iscsi_tmr_post_handler(). 1738c2ecf20Sopenharmony_ci */ 1748c2ecf20Sopenharmony_ci return ISCSI_TMF_RSP_COMPLETE; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void iscsit_task_reassign_remove_cmd( 1788c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 1798c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr, 1808c2ecf20Sopenharmony_ci struct iscsi_session *sess) 1818c2ecf20Sopenharmony_ci{ 1828c2ecf20Sopenharmony_ci int ret; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 1858c2ecf20Sopenharmony_ci ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess); 1868c2ecf20Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 1878c2ecf20Sopenharmony_ci if (!ret) { 1888c2ecf20Sopenharmony_ci pr_debug("iSCSI connection recovery successful for CID:" 1898c2ecf20Sopenharmony_ci " %hu on SID: %u\n", cr->cid, sess->sid); 1908c2ecf20Sopenharmony_ci iscsit_remove_active_connection_recovery_entry(cr, sess); 1918c2ecf20Sopenharmony_ci } 1928c2ecf20Sopenharmony_ci} 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete_nop_out( 1958c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 1968c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd = tmr_req->ref_cmd; 1998c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci if (!cmd->cr) { 2028c2ecf20Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 2038c2ecf20Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 2048c2ecf20Sopenharmony_ci return -1; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci cr = cmd->cr; 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci /* 2098c2ecf20Sopenharmony_ci * Reset the StatSN so a new one for this commands new connection 2108c2ecf20Sopenharmony_ci * will be assigned. 2118c2ecf20Sopenharmony_ci * Reset the ExpStatSN as well so we may receive Status SNACKs. 2128c2ecf20Sopenharmony_ci */ 2138c2ecf20Sopenharmony_ci cmd->stat_sn = cmd->exp_stat_sn = 0; 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 2188c2ecf20Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); 2198c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_SEND_NOPIN; 2228c2ecf20Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 2238c2ecf20Sopenharmony_ci return 0; 2248c2ecf20Sopenharmony_ci} 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete_write( 2278c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 2288c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci int no_build_r2ts = 0; 2318c2ecf20Sopenharmony_ci u32 length = 0, offset = 0; 2328c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 2338c2ecf20Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 2348c2ecf20Sopenharmony_ci /* 2358c2ecf20Sopenharmony_ci * The Initiator must not send a R2T SNACK with a Begrun less than 2368c2ecf20Sopenharmony_ci * the TMR TASK_REASSIGN's ExpDataSN. 2378c2ecf20Sopenharmony_ci */ 2388c2ecf20Sopenharmony_ci if (!tmr_req->exp_data_sn) { 2398c2ecf20Sopenharmony_ci cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; 2408c2ecf20Sopenharmony_ci cmd->acked_data_sn = 0; 2418c2ecf20Sopenharmony_ci } else { 2428c2ecf20Sopenharmony_ci cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; 2438c2ecf20Sopenharmony_ci cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* 2478c2ecf20Sopenharmony_ci * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the 2488c2ecf20Sopenharmony_ci * Initiator is expecting. The Target controls all WRITE operations 2498c2ecf20Sopenharmony_ci * so if we have received all DataOUT we can safety ignore Initiator. 2508c2ecf20Sopenharmony_ci */ 2518c2ecf20Sopenharmony_ci if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { 2528c2ecf20Sopenharmony_ci if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { 2538c2ecf20Sopenharmony_ci pr_debug("WRITE ITT: 0x%08x: t_state: %d" 2548c2ecf20Sopenharmony_ci " never sent to transport\n", 2558c2ecf20Sopenharmony_ci cmd->init_task_tag, cmd->se_cmd.t_state); 2568c2ecf20Sopenharmony_ci target_execute_cmd(se_cmd); 2578c2ecf20Sopenharmony_ci return 0; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 2618c2ecf20Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci } 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci /* 2668c2ecf20Sopenharmony_ci * Special case to deal with DataSequenceInOrder=No and Non-Immeidate 2678c2ecf20Sopenharmony_ci * Unsolicited DataOut. 2688c2ecf20Sopenharmony_ci */ 2698c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) { 2708c2ecf20Sopenharmony_ci cmd->unsolicited_data = 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci offset = cmd->next_burst_len = cmd->write_data_done; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci if ((conn->sess->sess_ops->FirstBurstLength - offset) >= 2758c2ecf20Sopenharmony_ci cmd->se_cmd.data_length) { 2768c2ecf20Sopenharmony_ci no_build_r2ts = 1; 2778c2ecf20Sopenharmony_ci length = (cmd->se_cmd.data_length - offset); 2788c2ecf20Sopenharmony_ci } else 2798c2ecf20Sopenharmony_ci length = (conn->sess->sess_ops->FirstBurstLength - offset); 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 2828c2ecf20Sopenharmony_ci if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) { 2838c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 2848c2ecf20Sopenharmony_ci return -1; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci cmd->outstanding_r2ts++; 2878c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci if (no_build_r2ts) 2908c2ecf20Sopenharmony_ci return 0; 2918c2ecf20Sopenharmony_ci } 2928c2ecf20Sopenharmony_ci /* 2938c2ecf20Sopenharmony_ci * iscsit_build_r2ts_for_cmd() can handle the rest from here. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci return conn->conn_transport->iscsit_get_dataout(conn, cmd, true); 2968c2ecf20Sopenharmony_ci} 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete_read( 2998c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 3008c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 3018c2ecf20Sopenharmony_ci{ 3028c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 3038c2ecf20Sopenharmony_ci struct iscsi_datain_req *dr; 3048c2ecf20Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 3058c2ecf20Sopenharmony_ci /* 3068c2ecf20Sopenharmony_ci * The Initiator must not send a Data SNACK with a BegRun less than 3078c2ecf20Sopenharmony_ci * the TMR TASK_REASSIGN's ExpDataSN. 3088c2ecf20Sopenharmony_ci */ 3098c2ecf20Sopenharmony_ci if (!tmr_req->exp_data_sn) { 3108c2ecf20Sopenharmony_ci cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; 3118c2ecf20Sopenharmony_ci cmd->acked_data_sn = 0; 3128c2ecf20Sopenharmony_ci } else { 3138c2ecf20Sopenharmony_ci cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; 3148c2ecf20Sopenharmony_ci cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { 3188c2ecf20Sopenharmony_ci pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" 3198c2ecf20Sopenharmony_ci " transport\n", cmd->init_task_tag, 3208c2ecf20Sopenharmony_ci cmd->se_cmd.t_state); 3218c2ecf20Sopenharmony_ci transport_handle_cdb_direct(se_cmd); 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci } 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (!(se_cmd->transport_state & CMD_T_COMPLETE)) { 3268c2ecf20Sopenharmony_ci pr_err("READ ITT: 0x%08x: t_state: %d, never returned" 3278c2ecf20Sopenharmony_ci " from transport\n", cmd->init_task_tag, 3288c2ecf20Sopenharmony_ci cmd->se_cmd.t_state); 3298c2ecf20Sopenharmony_ci return -1; 3308c2ecf20Sopenharmony_ci } 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci dr = iscsit_allocate_datain_req(); 3338c2ecf20Sopenharmony_ci if (!dr) 3348c2ecf20Sopenharmony_ci return -1; 3358c2ecf20Sopenharmony_ci /* 3368c2ecf20Sopenharmony_ci * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the 3378c2ecf20Sopenharmony_ci * Initiator is expecting. 3388c2ecf20Sopenharmony_ci */ 3398c2ecf20Sopenharmony_ci dr->data_sn = dr->begrun = tmr_req->exp_data_sn; 3408c2ecf20Sopenharmony_ci dr->runlength = 0; 3418c2ecf20Sopenharmony_ci dr->generate_recovery_values = 1; 3428c2ecf20Sopenharmony_ci dr->recovery = DATAIN_CONNECTION_RECOVERY; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci iscsit_attach_datain_req(cmd, dr); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_SEND_DATAIN; 3478c2ecf20Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 3488c2ecf20Sopenharmony_ci return 0; 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete_none( 3528c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 3538c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cmd->conn; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 3588c2ecf20Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 3598c2ecf20Sopenharmony_ci return 0; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete_scsi_cmnd( 3638c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 3648c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd = tmr_req->ref_cmd; 3678c2ecf20Sopenharmony_ci struct iscsi_conn_recovery *cr; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci if (!cmd->cr) { 3708c2ecf20Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 3718c2ecf20Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 3728c2ecf20Sopenharmony_ci return -1; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci cr = cmd->cr; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci /* 3778c2ecf20Sopenharmony_ci * Reset the StatSN so a new one for this commands new connection 3788c2ecf20Sopenharmony_ci * will be assigned. 3798c2ecf20Sopenharmony_ci * Reset the ExpStatSN as well so we may receive Status SNACKs. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_ci cmd->stat_sn = cmd->exp_stat_sn = 0; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 3868c2ecf20Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); 3878c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci if (cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) { 3908c2ecf20Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 3918c2ecf20Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 3928c2ecf20Sopenharmony_ci return 0; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci switch (cmd->data_direction) { 3968c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 3978c2ecf20Sopenharmony_ci return iscsit_task_reassign_complete_write(cmd, tmr_req); 3988c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 3998c2ecf20Sopenharmony_ci return iscsit_task_reassign_complete_read(cmd, tmr_req); 4008c2ecf20Sopenharmony_ci case DMA_NONE: 4018c2ecf20Sopenharmony_ci return iscsit_task_reassign_complete_none(cmd, tmr_req); 4028c2ecf20Sopenharmony_ci default: 4038c2ecf20Sopenharmony_ci pr_err("Unknown cmd->data_direction: 0x%02x\n", 4048c2ecf20Sopenharmony_ci cmd->data_direction); 4058c2ecf20Sopenharmony_ci return -1; 4068c2ecf20Sopenharmony_ci } 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_complete( 4128c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 4138c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 4148c2ecf20Sopenharmony_ci{ 4158c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd; 4168c2ecf20Sopenharmony_ci int ret = 0; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (!tmr_req->ref_cmd) { 4198c2ecf20Sopenharmony_ci pr_err("TMR Request is missing a RefCmd struct iscsi_cmd.\n"); 4208c2ecf20Sopenharmony_ci return -1; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci cmd = tmr_req->ref_cmd; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci cmd->conn = conn; 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci switch (cmd->iscsi_opcode) { 4278c2ecf20Sopenharmony_ci case ISCSI_OP_NOOP_OUT: 4288c2ecf20Sopenharmony_ci ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn); 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_CMD: 4318c2ecf20Sopenharmony_ci ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn); 4328c2ecf20Sopenharmony_ci break; 4338c2ecf20Sopenharmony_ci default: 4348c2ecf20Sopenharmony_ci pr_err("Illegal iSCSI Opcode 0x%02x during" 4358c2ecf20Sopenharmony_ci " command reallegiance\n", cmd->iscsi_opcode); 4368c2ecf20Sopenharmony_ci return -1; 4378c2ecf20Sopenharmony_ci } 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (ret != 0) 4408c2ecf20Sopenharmony_ci return ret; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci pr_debug("Completed connection reallegiance for Opcode: 0x%02x," 4438c2ecf20Sopenharmony_ci " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode, 4448c2ecf20Sopenharmony_ci cmd->init_task_tag, conn->cid); 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci return 0; 4478c2ecf20Sopenharmony_ci} 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci/* 4508c2ecf20Sopenharmony_ci * Handles special after-the-fact actions related to TMRs. 4518c2ecf20Sopenharmony_ci * Right now the only one that its really needed for is 4528c2ecf20Sopenharmony_ci * connection recovery releated TASK_REASSIGN. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_ciint iscsit_tmr_post_handler(struct iscsi_cmd *cmd, struct iscsi_conn *conn) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 4578c2ecf20Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ci if (tmr_req->task_reassign && 4608c2ecf20Sopenharmony_ci (se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) 4618c2ecf20Sopenharmony_ci return iscsit_task_reassign_complete(tmr_req, conn); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci return 0; 4648c2ecf20Sopenharmony_ci} 4658c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_tmr_post_handler); 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/* 4688c2ecf20Sopenharmony_ci * Nothing to do here, but leave it for good measure. :-) 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_prepare_read( 4718c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 4728c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci return 0; 4758c2ecf20Sopenharmony_ci} 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_cistatic void iscsit_task_reassign_prepare_unsolicited_dataout( 4788c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd, 4798c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 4808c2ecf20Sopenharmony_ci{ 4818c2ecf20Sopenharmony_ci int i, j; 4828c2ecf20Sopenharmony_ci struct iscsi_pdu *pdu = NULL; 4838c2ecf20Sopenharmony_ci struct iscsi_seq *seq = NULL; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 4868c2ecf20Sopenharmony_ci cmd->data_sn = 0; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci if (cmd->immediate_data) 4898c2ecf20Sopenharmony_ci cmd->r2t_offset += (cmd->first_burst_len - 4908c2ecf20Sopenharmony_ci cmd->seq_start_offset); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) { 4938c2ecf20Sopenharmony_ci cmd->write_data_done -= (cmd->immediate_data) ? 4948c2ecf20Sopenharmony_ci (cmd->first_burst_len - 4958c2ecf20Sopenharmony_ci cmd->seq_start_offset) : 4968c2ecf20Sopenharmony_ci cmd->first_burst_len; 4978c2ecf20Sopenharmony_ci cmd->first_burst_len = 0; 4988c2ecf20Sopenharmony_ci return; 4998c2ecf20Sopenharmony_ci } 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci for (i = 0; i < cmd->pdu_count; i++) { 5028c2ecf20Sopenharmony_ci pdu = &cmd->pdu_list[i]; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 5058c2ecf20Sopenharmony_ci continue; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if ((pdu->offset >= cmd->seq_start_offset) && 5088c2ecf20Sopenharmony_ci ((pdu->offset + pdu->length) <= 5098c2ecf20Sopenharmony_ci cmd->seq_end_offset)) { 5108c2ecf20Sopenharmony_ci cmd->first_burst_len -= pdu->length; 5118c2ecf20Sopenharmony_ci cmd->write_data_done -= pdu->length; 5128c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci } 5158c2ecf20Sopenharmony_ci } else { 5168c2ecf20Sopenharmony_ci for (i = 0; i < cmd->seq_count; i++) { 5178c2ecf20Sopenharmony_ci seq = &cmd->seq_list[i]; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci if (seq->type != SEQTYPE_UNSOLICITED) 5208c2ecf20Sopenharmony_ci continue; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci cmd->write_data_done -= 5238c2ecf20Sopenharmony_ci (seq->offset - seq->orig_offset); 5248c2ecf20Sopenharmony_ci cmd->first_burst_len = 0; 5258c2ecf20Sopenharmony_ci seq->data_sn = 0; 5268c2ecf20Sopenharmony_ci seq->offset = seq->orig_offset; 5278c2ecf20Sopenharmony_ci seq->next_burst_len = 0; 5288c2ecf20Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) 5318c2ecf20Sopenharmony_ci continue; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci for (j = 0; j < seq->pdu_count; j++) { 5348c2ecf20Sopenharmony_ci pdu = &cmd->pdu_list[j+seq->pdu_start]; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 5378c2ecf20Sopenharmony_ci continue; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci } 5428c2ecf20Sopenharmony_ci } 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic int iscsit_task_reassign_prepare_write( 5468c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 5478c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 5488c2ecf20Sopenharmony_ci{ 5498c2ecf20Sopenharmony_ci struct iscsi_cmd *cmd = tmr_req->ref_cmd; 5508c2ecf20Sopenharmony_ci struct iscsi_pdu *pdu = NULL; 5518c2ecf20Sopenharmony_ci struct iscsi_r2t *r2t = NULL, *r2t_tmp; 5528c2ecf20Sopenharmony_ci int first_incomplete_r2t = 1, i = 0; 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* 5558c2ecf20Sopenharmony_ci * The command was in the process of receiving Unsolicited DataOUT when 5568c2ecf20Sopenharmony_ci * the connection failed. 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_ci if (cmd->unsolicited_data) 5598c2ecf20Sopenharmony_ci iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn); 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci /* 5628c2ecf20Sopenharmony_ci * The Initiator is requesting R2Ts starting from zero, skip 5638c2ecf20Sopenharmony_ci * checking acknowledged R2Ts and start checking struct iscsi_r2ts 5648c2ecf20Sopenharmony_ci * greater than zero. 5658c2ecf20Sopenharmony_ci */ 5668c2ecf20Sopenharmony_ci if (!tmr_req->exp_data_sn) 5678c2ecf20Sopenharmony_ci goto drop_unacknowledged_r2ts; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci /* 5708c2ecf20Sopenharmony_ci * We now check that the PDUs in DataOUT sequences below 5718c2ecf20Sopenharmony_ci * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is 5728c2ecf20Sopenharmony_ci * expecting next) have all the DataOUT they require to complete 5738c2ecf20Sopenharmony_ci * the DataOUT sequence. First scan from R2TSN 0 to TMR 5748c2ecf20Sopenharmony_ci * TASK_REASSIGN ExpDataSN-1. 5758c2ecf20Sopenharmony_ci * 5768c2ecf20Sopenharmony_ci * If we have not received all DataOUT in question, we must 5778c2ecf20Sopenharmony_ci * make sure to make the appropriate changes to values in 5788c2ecf20Sopenharmony_ci * struct iscsi_cmd (and elsewhere depending on session parameters) 5798c2ecf20Sopenharmony_ci * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write() 5808c2ecf20Sopenharmony_ci * will resend a new R2T for the DataOUT sequences in question. 5818c2ecf20Sopenharmony_ci */ 5828c2ecf20Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 5838c2ecf20Sopenharmony_ci if (list_empty(&cmd->cmd_r2t_list)) { 5848c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 5858c2ecf20Sopenharmony_ci return -1; 5868c2ecf20Sopenharmony_ci } 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci if (r2t->r2t_sn >= tmr_req->exp_data_sn) 5918c2ecf20Sopenharmony_ci continue; 5928c2ecf20Sopenharmony_ci /* 5938c2ecf20Sopenharmony_ci * Safely ignore Recovery R2Ts and R2Ts that have completed 5948c2ecf20Sopenharmony_ci * DataOUT sequences. 5958c2ecf20Sopenharmony_ci */ 5968c2ecf20Sopenharmony_ci if (r2t->seq_complete) 5978c2ecf20Sopenharmony_ci continue; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if (r2t->recovery_r2t) 6008c2ecf20Sopenharmony_ci continue; 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci /* 6038c2ecf20Sopenharmony_ci * DataSequenceInOrder=Yes: 6048c2ecf20Sopenharmony_ci * 6058c2ecf20Sopenharmony_ci * Taking into account the iSCSI implementation requirement of 6068c2ecf20Sopenharmony_ci * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and 6078c2ecf20Sopenharmony_ci * DataSequenceInOrder=Yes, we must take into consideration 6088c2ecf20Sopenharmony_ci * the following: 6098c2ecf20Sopenharmony_ci * 6108c2ecf20Sopenharmony_ci * DataSequenceInOrder=No: 6118c2ecf20Sopenharmony_ci * 6128c2ecf20Sopenharmony_ci * Taking into account that the Initiator controls the (possibly 6138c2ecf20Sopenharmony_ci * random) PDU Order in (possibly random) Sequence Order of 6148c2ecf20Sopenharmony_ci * DataOUT the target requests with R2Ts, we must take into 6158c2ecf20Sopenharmony_ci * consideration the following: 6168c2ecf20Sopenharmony_ci * 6178c2ecf20Sopenharmony_ci * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]: 6188c2ecf20Sopenharmony_ci * 6198c2ecf20Sopenharmony_ci * While processing non-complete R2T DataOUT sequence requests 6208c2ecf20Sopenharmony_ci * the Target will re-request only the total sequence length 6218c2ecf20Sopenharmony_ci * minus current received offset. This is because we must 6228c2ecf20Sopenharmony_ci * assume the initiator will continue sending DataOUT from the 6238c2ecf20Sopenharmony_ci * last PDU before the connection failed. 6248c2ecf20Sopenharmony_ci * 6258c2ecf20Sopenharmony_ci * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]: 6268c2ecf20Sopenharmony_ci * 6278c2ecf20Sopenharmony_ci * While processing non-complete R2T DataOUT sequence requests 6288c2ecf20Sopenharmony_ci * the Target will re-request the entire DataOUT sequence if 6298c2ecf20Sopenharmony_ci * any single PDU is missing from the sequence. This is because 6308c2ecf20Sopenharmony_ci * we have no logical method to determine the next PDU offset, 6318c2ecf20Sopenharmony_ci * and we must assume the Initiator will be sending any random 6328c2ecf20Sopenharmony_ci * PDU offset in the current sequence after TASK_REASSIGN 6338c2ecf20Sopenharmony_ci * has completed. 6348c2ecf20Sopenharmony_ci */ 6358c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 6368c2ecf20Sopenharmony_ci if (!first_incomplete_r2t) { 6378c2ecf20Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 6388c2ecf20Sopenharmony_ci goto next; 6398c2ecf20Sopenharmony_ci } 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) { 6428c2ecf20Sopenharmony_ci cmd->data_sn = 0; 6438c2ecf20Sopenharmony_ci cmd->r2t_offset -= (r2t->xfer_len - 6448c2ecf20Sopenharmony_ci cmd->next_burst_len); 6458c2ecf20Sopenharmony_ci first_incomplete_r2t = 0; 6468c2ecf20Sopenharmony_ci goto next; 6478c2ecf20Sopenharmony_ci } 6488c2ecf20Sopenharmony_ci 6498c2ecf20Sopenharmony_ci cmd->data_sn = 0; 6508c2ecf20Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 6518c2ecf20Sopenharmony_ci 6528c2ecf20Sopenharmony_ci for (i = 0; i < cmd->pdu_count; i++) { 6538c2ecf20Sopenharmony_ci pdu = &cmd->pdu_list[i]; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 6568c2ecf20Sopenharmony_ci continue; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if ((pdu->offset >= r2t->offset) && 6598c2ecf20Sopenharmony_ci (pdu->offset < (r2t->offset + 6608c2ecf20Sopenharmony_ci r2t->xfer_len))) { 6618c2ecf20Sopenharmony_ci cmd->next_burst_len -= pdu->length; 6628c2ecf20Sopenharmony_ci cmd->write_data_done -= pdu->length; 6638c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 6648c2ecf20Sopenharmony_ci } 6658c2ecf20Sopenharmony_ci } 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci first_incomplete_r2t = 0; 6688c2ecf20Sopenharmony_ci } else { 6698c2ecf20Sopenharmony_ci struct iscsi_seq *seq; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci seq = iscsit_get_seq_holder(cmd, r2t->offset, 6728c2ecf20Sopenharmony_ci r2t->xfer_len); 6738c2ecf20Sopenharmony_ci if (!seq) { 6748c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 6758c2ecf20Sopenharmony_ci return -1; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_ci cmd->write_data_done -= 6798c2ecf20Sopenharmony_ci (seq->offset - seq->orig_offset); 6808c2ecf20Sopenharmony_ci seq->data_sn = 0; 6818c2ecf20Sopenharmony_ci seq->offset = seq->orig_offset; 6828c2ecf20Sopenharmony_ci seq->next_burst_len = 0; 6838c2ecf20Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci cmd->seq_send_order--; 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) 6888c2ecf20Sopenharmony_ci goto next; 6898c2ecf20Sopenharmony_ci 6908c2ecf20Sopenharmony_ci for (i = 0; i < seq->pdu_count; i++) { 6918c2ecf20Sopenharmony_ci pdu = &cmd->pdu_list[i+seq->pdu_start]; 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 6948c2ecf20Sopenharmony_ci continue; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cinext: 7018c2ecf20Sopenharmony_ci cmd->outstanding_r2ts--; 7028c2ecf20Sopenharmony_ci } 7038c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* 7068c2ecf20Sopenharmony_ci * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR 7078c2ecf20Sopenharmony_ci * TASK_REASSIGN to the last R2T in the list.. We are also careful 7088c2ecf20Sopenharmony_ci * to check that the Initiator is not requesting R2Ts for DataOUT 7098c2ecf20Sopenharmony_ci * sequences it has already completed. 7108c2ecf20Sopenharmony_ci * 7118c2ecf20Sopenharmony_ci * Free each R2T in question and adjust values in struct iscsi_cmd 7128c2ecf20Sopenharmony_ci * accordingly so iscsit_build_r2ts_for_cmd() do the rest of 7138c2ecf20Sopenharmony_ci * the work after the TMR TASK_REASSIGN Response is sent. 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_cidrop_unacknowledged_r2ts: 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci cmd->cmd_flags &= ~ICF_SENT_LAST_R2T; 7188c2ecf20Sopenharmony_ci cmd->r2t_sn = tmr_req->exp_data_sn; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 7218c2ecf20Sopenharmony_ci list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) { 7228c2ecf20Sopenharmony_ci /* 7238c2ecf20Sopenharmony_ci * Skip up to the R2T Sequence number provided by the 7248c2ecf20Sopenharmony_ci * iSCSI TASK_REASSIGN TMR 7258c2ecf20Sopenharmony_ci */ 7268c2ecf20Sopenharmony_ci if (r2t->r2t_sn < tmr_req->exp_data_sn) 7278c2ecf20Sopenharmony_ci continue; 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (r2t->seq_complete) { 7308c2ecf20Sopenharmony_ci pr_err("Initiator is requesting R2Ts from" 7318c2ecf20Sopenharmony_ci " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u," 7328c2ecf20Sopenharmony_ci " Length: %u is already complete." 7338c2ecf20Sopenharmony_ci " BAD INITIATOR ERL=2 IMPLEMENTATION!\n", 7348c2ecf20Sopenharmony_ci tmr_req->exp_data_sn, r2t->r2t_sn, 7358c2ecf20Sopenharmony_ci r2t->offset, r2t->xfer_len); 7368c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 7378c2ecf20Sopenharmony_ci return -1; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci if (r2t->recovery_r2t) { 7418c2ecf20Sopenharmony_ci iscsit_free_r2t(r2t, cmd); 7428c2ecf20Sopenharmony_ci continue; 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_ci /* DataSequenceInOrder=Yes: 7468c2ecf20Sopenharmony_ci * 7478c2ecf20Sopenharmony_ci * Taking into account the iSCSI implementation requirement of 7488c2ecf20Sopenharmony_ci * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and 7498c2ecf20Sopenharmony_ci * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts 7508c2ecf20Sopenharmony_ci * entire transfer length from the commands R2T offset marker. 7518c2ecf20Sopenharmony_ci * 7528c2ecf20Sopenharmony_ci * DataSequenceInOrder=No: 7538c2ecf20Sopenharmony_ci * 7548c2ecf20Sopenharmony_ci * We subtract the difference from struct iscsi_seq between the 7558c2ecf20Sopenharmony_ci * current offset and original offset from cmd->write_data_done 7568c2ecf20Sopenharmony_ci * for account for DataOUT PDUs already received. Then reset 7578c2ecf20Sopenharmony_ci * the current offset to the original and zero out the current 7588c2ecf20Sopenharmony_ci * burst length, to make sure we re-request the entire DataOUT 7598c2ecf20Sopenharmony_ci * sequence. 7608c2ecf20Sopenharmony_ci */ 7618c2ecf20Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) 7628c2ecf20Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 7638c2ecf20Sopenharmony_ci else 7648c2ecf20Sopenharmony_ci cmd->seq_send_order--; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci cmd->outstanding_r2ts--; 7678c2ecf20Sopenharmony_ci iscsit_free_r2t(r2t, cmd); 7688c2ecf20Sopenharmony_ci } 7698c2ecf20Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci return 0; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* 7758c2ecf20Sopenharmony_ci * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for 7768c2ecf20Sopenharmony_ci * a given struct iscsi_cmd. 7778c2ecf20Sopenharmony_ci */ 7788c2ecf20Sopenharmony_ciint iscsit_check_task_reassign_expdatasn( 7798c2ecf20Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 7808c2ecf20Sopenharmony_ci struct iscsi_conn *conn) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci struct iscsi_cmd *ref_cmd = tmr_req->ref_cmd; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) 7858c2ecf20Sopenharmony_ci return 0; 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_ci if (ref_cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) 7888c2ecf20Sopenharmony_ci return 0; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci if (ref_cmd->data_direction == DMA_NONE) 7918c2ecf20Sopenharmony_ci return 0; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* 7948c2ecf20Sopenharmony_ci * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN 7958c2ecf20Sopenharmony_ci * of DataIN the Initiator is expecting. 7968c2ecf20Sopenharmony_ci * 7978c2ecf20Sopenharmony_ci * Also check that the Initiator is not re-requesting DataIN that has 7988c2ecf20Sopenharmony_ci * already been acknowledged with a DataAck SNACK. 7998c2ecf20Sopenharmony_ci */ 8008c2ecf20Sopenharmony_ci if (ref_cmd->data_direction == DMA_FROM_DEVICE) { 8018c2ecf20Sopenharmony_ci if (tmr_req->exp_data_sn > ref_cmd->data_sn) { 8028c2ecf20Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for READ" 8038c2ecf20Sopenharmony_ci " in TMR TASK_REASSIGN greater than command's" 8048c2ecf20Sopenharmony_ci " DataSN: 0x%08x.\n", tmr_req->exp_data_sn, 8058c2ecf20Sopenharmony_ci ref_cmd->data_sn); 8068c2ecf20Sopenharmony_ci return -1; 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && 8098c2ecf20Sopenharmony_ci (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) { 8108c2ecf20Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for READ" 8118c2ecf20Sopenharmony_ci " in TMR TASK_REASSIGN for previously" 8128c2ecf20Sopenharmony_ci " acknowledged DataIN: 0x%08x," 8138c2ecf20Sopenharmony_ci " protocol error\n", tmr_req->exp_data_sn, 8148c2ecf20Sopenharmony_ci ref_cmd->acked_data_sn); 8158c2ecf20Sopenharmony_ci return -1; 8168c2ecf20Sopenharmony_ci } 8178c2ecf20Sopenharmony_ci return iscsit_task_reassign_prepare_read(tmr_req, conn); 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci /* 8218c2ecf20Sopenharmony_ci * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN 8228c2ecf20Sopenharmony_ci * for R2Ts the Initiator is expecting. 8238c2ecf20Sopenharmony_ci * 8248c2ecf20Sopenharmony_ci * Do the magic in iscsit_task_reassign_prepare_write(). 8258c2ecf20Sopenharmony_ci */ 8268c2ecf20Sopenharmony_ci if (ref_cmd->data_direction == DMA_TO_DEVICE) { 8278c2ecf20Sopenharmony_ci if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) { 8288c2ecf20Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for WRITE" 8298c2ecf20Sopenharmony_ci " in TMR TASK_REASSIGN greater than command's" 8308c2ecf20Sopenharmony_ci " R2TSN: 0x%08x.\n", tmr_req->exp_data_sn, 8318c2ecf20Sopenharmony_ci ref_cmd->r2t_sn); 8328c2ecf20Sopenharmony_ci return -1; 8338c2ecf20Sopenharmony_ci } 8348c2ecf20Sopenharmony_ci return iscsit_task_reassign_prepare_write(tmr_req, conn); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci pr_err("Unknown iSCSI data_direction: 0x%02x\n", 8388c2ecf20Sopenharmony_ci ref_cmd->data_direction); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci return -1; 8418c2ecf20Sopenharmony_ci} 842