162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/******************************************************************************* 362306a36Sopenharmony_ci * This file contains the iSCSI Target specific Task Management functions. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci ******************************************************************************/ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci#include <scsi/scsi_proto.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#include <target/iscsi/iscsi_transport.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h> 1962306a36Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h" 2062306a36Sopenharmony_ci#include "iscsi_target_datain_values.h" 2162306a36Sopenharmony_ci#include "iscsi_target_device.h" 2262306a36Sopenharmony_ci#include "iscsi_target_erl0.h" 2362306a36Sopenharmony_ci#include "iscsi_target_erl1.h" 2462306a36Sopenharmony_ci#include "iscsi_target_erl2.h" 2562306a36Sopenharmony_ci#include "iscsi_target_tmr.h" 2662306a36Sopenharmony_ci#include "iscsi_target_tpg.h" 2762306a36Sopenharmony_ci#include "iscsi_target_util.h" 2862306a36Sopenharmony_ci#include "iscsi_target.h" 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ciu8 iscsit_tmr_abort_task( 3162306a36Sopenharmony_ci struct iscsit_cmd *cmd, 3262306a36Sopenharmony_ci unsigned char *buf) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci struct iscsit_cmd *ref_cmd; 3562306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 3662306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 3762306a36Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 3862306a36Sopenharmony_ci struct iscsi_tm *hdr = (struct iscsi_tm *) buf; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt); 4162306a36Sopenharmony_ci if (!ref_cmd) { 4262306a36Sopenharmony_ci pr_err("Unable to locate RefTaskTag: 0x%08x on CID:" 4362306a36Sopenharmony_ci " %hu.\n", hdr->rtt, conn->cid); 4462306a36Sopenharmony_ci return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), conn->sess->exp_cmd_sn) && 4562306a36Sopenharmony_ci iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), (u32) atomic_read(&conn->sess->max_cmd_sn))) ? 4662306a36Sopenharmony_ci ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) { 4962306a36Sopenharmony_ci pr_err("RefCmdSN 0x%08x does not equal" 5062306a36Sopenharmony_ci " task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n", 5162306a36Sopenharmony_ci hdr->refcmdsn, ref_cmd->cmd_sn); 5262306a36Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci se_tmr->ref_task_tag = (__force u32)hdr->rtt; 5662306a36Sopenharmony_ci tmr_req->ref_cmd = ref_cmd; 5762306a36Sopenharmony_ci tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return ISCSI_TMF_RSP_COMPLETE; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/* 6362306a36Sopenharmony_ci * Called from iscsit_handle_task_mgt_cmd(). 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_ciint iscsit_tmr_task_warm_reset( 6662306a36Sopenharmony_ci struct iscsit_conn *conn, 6762306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 6862306a36Sopenharmony_ci unsigned char *buf) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct iscsit_session *sess = conn->sess; 7162306a36Sopenharmony_ci struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (!na->tmr_warm_reset) { 7462306a36Sopenharmony_ci pr_err("TMR Opcode TARGET_WARM_RESET authorization" 7562306a36Sopenharmony_ci " failed for Initiator Node: %s\n", 7662306a36Sopenharmony_ci sess->se_sess->se_node_acl->initiatorname); 7762306a36Sopenharmony_ci return -1; 7862306a36Sopenharmony_ci } 7962306a36Sopenharmony_ci /* 8062306a36Sopenharmony_ci * Do the real work in transport_generic_do_tmr(). 8162306a36Sopenharmony_ci */ 8262306a36Sopenharmony_ci return 0; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ciint iscsit_tmr_task_cold_reset( 8662306a36Sopenharmony_ci struct iscsit_conn *conn, 8762306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 8862306a36Sopenharmony_ci unsigned char *buf) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct iscsit_session *sess = conn->sess; 9162306a36Sopenharmony_ci struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (!na->tmr_cold_reset) { 9462306a36Sopenharmony_ci pr_err("TMR Opcode TARGET_COLD_RESET authorization" 9562306a36Sopenharmony_ci " failed for Initiator Node: %s\n", 9662306a36Sopenharmony_ci sess->se_sess->se_node_acl->initiatorname); 9762306a36Sopenharmony_ci return -1; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci /* 10062306a36Sopenharmony_ci * Do the real work in transport_generic_do_tmr(). 10162306a36Sopenharmony_ci */ 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciu8 iscsit_tmr_task_reassign( 10662306a36Sopenharmony_ci struct iscsit_cmd *cmd, 10762306a36Sopenharmony_ci unsigned char *buf) 10862306a36Sopenharmony_ci{ 10962306a36Sopenharmony_ci struct iscsit_cmd *ref_cmd = NULL; 11062306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 11162306a36Sopenharmony_ci struct iscsi_conn_recovery *cr = NULL; 11262306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 11362306a36Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 11462306a36Sopenharmony_ci struct iscsi_tm *hdr = (struct iscsi_tm *) buf; 11562306a36Sopenharmony_ci u64 ret, ref_lun; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x," 11862306a36Sopenharmony_ci " RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n", 11962306a36Sopenharmony_ci hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) { 12262306a36Sopenharmony_ci pr_err("TMR TASK_REASSIGN not supported in ERL<2," 12362306a36Sopenharmony_ci " ignoring request.\n"); 12462306a36Sopenharmony_ci return ISCSI_TMF_RSP_NOT_SUPPORTED; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt); 12862306a36Sopenharmony_ci if (ret == -2) { 12962306a36Sopenharmony_ci pr_err("Command ITT: 0x%08x is still alligent to CID:" 13062306a36Sopenharmony_ci " %hu\n", ref_cmd->init_task_tag, cr->cid); 13162306a36Sopenharmony_ci return ISCSI_TMF_RSP_TASK_ALLEGIANT; 13262306a36Sopenharmony_ci } else if (ret == -1) { 13362306a36Sopenharmony_ci pr_err("Unable to locate RefTaskTag: 0x%08x in" 13462306a36Sopenharmony_ci " connection recovery command list.\n", hdr->rtt); 13562306a36Sopenharmony_ci return ISCSI_TMF_RSP_NO_TASK; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci /* 13862306a36Sopenharmony_ci * Temporary check to prevent connection recovery for 13962306a36Sopenharmony_ci * connections with a differing Max*DataSegmentLength. 14062306a36Sopenharmony_ci */ 14162306a36Sopenharmony_ci if (cr->maxrecvdatasegmentlength != 14262306a36Sopenharmony_ci conn->conn_ops->MaxRecvDataSegmentLength) { 14362306a36Sopenharmony_ci pr_err("Unable to perform connection recovery for" 14462306a36Sopenharmony_ci " differing MaxRecvDataSegmentLength, rejecting" 14562306a36Sopenharmony_ci " TMR TASK_REASSIGN.\n"); 14662306a36Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci if (cr->maxxmitdatasegmentlength != 14962306a36Sopenharmony_ci conn->conn_ops->MaxXmitDataSegmentLength) { 15062306a36Sopenharmony_ci pr_err("Unable to perform connection recovery for" 15162306a36Sopenharmony_ci " differing MaxXmitDataSegmentLength, rejecting" 15262306a36Sopenharmony_ci " TMR TASK_REASSIGN.\n"); 15362306a36Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ref_lun = scsilun_to_int(&hdr->lun); 15762306a36Sopenharmony_ci if (ref_lun != ref_cmd->se_cmd.orig_fe_lun) { 15862306a36Sopenharmony_ci pr_err("Unable to perform connection recovery for" 15962306a36Sopenharmony_ci " differing ref_lun: %llu ref_cmd orig_fe_lun: %llu\n", 16062306a36Sopenharmony_ci ref_lun, ref_cmd->se_cmd.orig_fe_lun); 16162306a36Sopenharmony_ci return ISCSI_TMF_RSP_REJECTED; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci se_tmr->ref_task_tag = (__force u32)hdr->rtt; 16562306a36Sopenharmony_ci tmr_req->ref_cmd = ref_cmd; 16662306a36Sopenharmony_ci tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); 16762306a36Sopenharmony_ci tmr_req->conn_recovery = cr; 16862306a36Sopenharmony_ci tmr_req->task_reassign = 1; 16962306a36Sopenharmony_ci /* 17062306a36Sopenharmony_ci * Command can now be reassigned to a new connection. 17162306a36Sopenharmony_ci * The task management response must be sent before the 17262306a36Sopenharmony_ci * reassignment actually happens. See iscsi_tmr_post_handler(). 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci return ISCSI_TMF_RSP_COMPLETE; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic void iscsit_task_reassign_remove_cmd( 17862306a36Sopenharmony_ci struct iscsit_cmd *cmd, 17962306a36Sopenharmony_ci struct iscsi_conn_recovery *cr, 18062306a36Sopenharmony_ci struct iscsit_session *sess) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci int ret; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci spin_lock(&cr->conn_recovery_cmd_lock); 18562306a36Sopenharmony_ci ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess); 18662306a36Sopenharmony_ci spin_unlock(&cr->conn_recovery_cmd_lock); 18762306a36Sopenharmony_ci if (!ret) { 18862306a36Sopenharmony_ci pr_debug("iSCSI connection recovery successful for CID:" 18962306a36Sopenharmony_ci " %hu on SID: %u\n", cr->cid, sess->sid); 19062306a36Sopenharmony_ci iscsit_remove_active_connection_recovery_entry(cr, sess); 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_cistatic int iscsit_task_reassign_complete_nop_out( 19562306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 19662306a36Sopenharmony_ci struct iscsit_conn *conn) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct iscsit_cmd *cmd = tmr_req->ref_cmd; 19962306a36Sopenharmony_ci struct iscsi_conn_recovery *cr; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci if (!cmd->cr) { 20262306a36Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 20362306a36Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 20462306a36Sopenharmony_ci return -1; 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci cr = cmd->cr; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* 20962306a36Sopenharmony_ci * Reset the StatSN so a new one for this commands new connection 21062306a36Sopenharmony_ci * will be assigned. 21162306a36Sopenharmony_ci * Reset the ExpStatSN as well so we may receive Status SNACKs. 21262306a36Sopenharmony_ci */ 21362306a36Sopenharmony_ci cmd->stat_sn = cmd->exp_stat_sn = 0; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 21862306a36Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); 21962306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci cmd->i_state = ISTATE_SEND_NOPIN; 22262306a36Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 22362306a36Sopenharmony_ci return 0; 22462306a36Sopenharmony_ci} 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_cistatic int iscsit_task_reassign_complete_write( 22762306a36Sopenharmony_ci struct iscsit_cmd *cmd, 22862306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci int no_build_r2ts = 0; 23162306a36Sopenharmony_ci u32 length = 0, offset = 0; 23262306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 23362306a36Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 23462306a36Sopenharmony_ci /* 23562306a36Sopenharmony_ci * The Initiator must not send a R2T SNACK with a Begrun less than 23662306a36Sopenharmony_ci * the TMR TASK_REASSIGN's ExpDataSN. 23762306a36Sopenharmony_ci */ 23862306a36Sopenharmony_ci if (!tmr_req->exp_data_sn) { 23962306a36Sopenharmony_ci cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; 24062306a36Sopenharmony_ci cmd->acked_data_sn = 0; 24162306a36Sopenharmony_ci } else { 24262306a36Sopenharmony_ci cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; 24362306a36Sopenharmony_ci cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* 24762306a36Sopenharmony_ci * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the 24862306a36Sopenharmony_ci * Initiator is expecting. The Target controls all WRITE operations 24962306a36Sopenharmony_ci * so if we have received all DataOUT we can safety ignore Initiator. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { 25262306a36Sopenharmony_ci if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { 25362306a36Sopenharmony_ci pr_debug("WRITE ITT: 0x%08x: t_state: %d" 25462306a36Sopenharmony_ci " never sent to transport\n", 25562306a36Sopenharmony_ci cmd->init_task_tag, cmd->se_cmd.t_state); 25662306a36Sopenharmony_ci target_execute_cmd(se_cmd); 25762306a36Sopenharmony_ci return 0; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 26162306a36Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci } 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * Special case to deal with DataSequenceInOrder=No and Non-Immeidate 26762306a36Sopenharmony_ci * Unsolicited DataOut. 26862306a36Sopenharmony_ci */ 26962306a36Sopenharmony_ci if (cmd->unsolicited_data) { 27062306a36Sopenharmony_ci cmd->unsolicited_data = 0; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci offset = cmd->next_burst_len = cmd->write_data_done; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci if ((conn->sess->sess_ops->FirstBurstLength - offset) >= 27562306a36Sopenharmony_ci cmd->se_cmd.data_length) { 27662306a36Sopenharmony_ci no_build_r2ts = 1; 27762306a36Sopenharmony_ci length = (cmd->se_cmd.data_length - offset); 27862306a36Sopenharmony_ci } else 27962306a36Sopenharmony_ci length = (conn->sess->sess_ops->FirstBurstLength - offset); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 28262306a36Sopenharmony_ci if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) { 28362306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 28462306a36Sopenharmony_ci return -1; 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci cmd->outstanding_r2ts++; 28762306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci if (no_build_r2ts) 29062306a36Sopenharmony_ci return 0; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci /* 29362306a36Sopenharmony_ci * iscsit_build_r2ts_for_cmd() can handle the rest from here. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci return conn->conn_transport->iscsit_get_dataout(conn, cmd, true); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistatic int iscsit_task_reassign_complete_read( 29962306a36Sopenharmony_ci struct iscsit_cmd *cmd, 30062306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 30362306a36Sopenharmony_ci struct iscsi_datain_req *dr; 30462306a36Sopenharmony_ci struct se_cmd *se_cmd = &cmd->se_cmd; 30562306a36Sopenharmony_ci /* 30662306a36Sopenharmony_ci * The Initiator must not send a Data SNACK with a BegRun less than 30762306a36Sopenharmony_ci * the TMR TASK_REASSIGN's ExpDataSN. 30862306a36Sopenharmony_ci */ 30962306a36Sopenharmony_ci if (!tmr_req->exp_data_sn) { 31062306a36Sopenharmony_ci cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; 31162306a36Sopenharmony_ci cmd->acked_data_sn = 0; 31262306a36Sopenharmony_ci } else { 31362306a36Sopenharmony_ci cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; 31462306a36Sopenharmony_ci cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); 31562306a36Sopenharmony_ci } 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { 31862306a36Sopenharmony_ci pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" 31962306a36Sopenharmony_ci " transport\n", cmd->init_task_tag, 32062306a36Sopenharmony_ci cmd->se_cmd.t_state); 32162306a36Sopenharmony_ci transport_handle_cdb_direct(se_cmd); 32262306a36Sopenharmony_ci return 0; 32362306a36Sopenharmony_ci } 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (!(se_cmd->transport_state & CMD_T_COMPLETE)) { 32662306a36Sopenharmony_ci pr_err("READ ITT: 0x%08x: t_state: %d, never returned" 32762306a36Sopenharmony_ci " from transport\n", cmd->init_task_tag, 32862306a36Sopenharmony_ci cmd->se_cmd.t_state); 32962306a36Sopenharmony_ci return -1; 33062306a36Sopenharmony_ci } 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci dr = iscsit_allocate_datain_req(); 33362306a36Sopenharmony_ci if (!dr) 33462306a36Sopenharmony_ci return -1; 33562306a36Sopenharmony_ci /* 33662306a36Sopenharmony_ci * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the 33762306a36Sopenharmony_ci * Initiator is expecting. 33862306a36Sopenharmony_ci */ 33962306a36Sopenharmony_ci dr->data_sn = dr->begrun = tmr_req->exp_data_sn; 34062306a36Sopenharmony_ci dr->runlength = 0; 34162306a36Sopenharmony_ci dr->generate_recovery_values = 1; 34262306a36Sopenharmony_ci dr->recovery = DATAIN_CONNECTION_RECOVERY; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci iscsit_attach_datain_req(cmd, dr); 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci cmd->i_state = ISTATE_SEND_DATAIN; 34762306a36Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 34862306a36Sopenharmony_ci return 0; 34962306a36Sopenharmony_ci} 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_cistatic int iscsit_task_reassign_complete_none( 35262306a36Sopenharmony_ci struct iscsit_cmd *cmd, 35362306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci struct iscsit_conn *conn = cmd->conn; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 35862306a36Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 35962306a36Sopenharmony_ci return 0; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int iscsit_task_reassign_complete_scsi_cmnd( 36362306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 36462306a36Sopenharmony_ci struct iscsit_conn *conn) 36562306a36Sopenharmony_ci{ 36662306a36Sopenharmony_ci struct iscsit_cmd *cmd = tmr_req->ref_cmd; 36762306a36Sopenharmony_ci struct iscsi_conn_recovery *cr; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (!cmd->cr) { 37062306a36Sopenharmony_ci pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" 37162306a36Sopenharmony_ci " is NULL!\n", cmd->init_task_tag); 37262306a36Sopenharmony_ci return -1; 37362306a36Sopenharmony_ci } 37462306a36Sopenharmony_ci cr = cmd->cr; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci /* 37762306a36Sopenharmony_ci * Reset the StatSN so a new one for this commands new connection 37862306a36Sopenharmony_ci * will be assigned. 37962306a36Sopenharmony_ci * Reset the ExpStatSN as well so we may receive Status SNACKs. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_ci cmd->stat_sn = cmd->exp_stat_sn = 0; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci iscsit_task_reassign_remove_cmd(cmd, cr, conn->sess); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci spin_lock_bh(&conn->cmd_lock); 38662306a36Sopenharmony_ci list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list); 38762306a36Sopenharmony_ci spin_unlock_bh(&conn->cmd_lock); 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) { 39062306a36Sopenharmony_ci cmd->i_state = ISTATE_SEND_STATUS; 39162306a36Sopenharmony_ci iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); 39262306a36Sopenharmony_ci return 0; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci switch (cmd->data_direction) { 39662306a36Sopenharmony_ci case DMA_TO_DEVICE: 39762306a36Sopenharmony_ci return iscsit_task_reassign_complete_write(cmd, tmr_req); 39862306a36Sopenharmony_ci case DMA_FROM_DEVICE: 39962306a36Sopenharmony_ci return iscsit_task_reassign_complete_read(cmd, tmr_req); 40062306a36Sopenharmony_ci case DMA_NONE: 40162306a36Sopenharmony_ci return iscsit_task_reassign_complete_none(cmd, tmr_req); 40262306a36Sopenharmony_ci default: 40362306a36Sopenharmony_ci pr_err("Unknown cmd->data_direction: 0x%02x\n", 40462306a36Sopenharmony_ci cmd->data_direction); 40562306a36Sopenharmony_ci return -1; 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci return 0; 40962306a36Sopenharmony_ci} 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_cistatic int iscsit_task_reassign_complete( 41262306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 41362306a36Sopenharmony_ci struct iscsit_conn *conn) 41462306a36Sopenharmony_ci{ 41562306a36Sopenharmony_ci struct iscsit_cmd *cmd; 41662306a36Sopenharmony_ci int ret = 0; 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci if (!tmr_req->ref_cmd) { 41962306a36Sopenharmony_ci pr_err("TMR Request is missing a RefCmd struct iscsit_cmd.\n"); 42062306a36Sopenharmony_ci return -1; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci cmd = tmr_req->ref_cmd; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci cmd->conn = conn; 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_ci switch (cmd->iscsi_opcode) { 42762306a36Sopenharmony_ci case ISCSI_OP_NOOP_OUT: 42862306a36Sopenharmony_ci ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci case ISCSI_OP_SCSI_CMD: 43162306a36Sopenharmony_ci ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn); 43262306a36Sopenharmony_ci break; 43362306a36Sopenharmony_ci default: 43462306a36Sopenharmony_ci pr_err("Illegal iSCSI Opcode 0x%02x during" 43562306a36Sopenharmony_ci " command reallegiance\n", cmd->iscsi_opcode); 43662306a36Sopenharmony_ci return -1; 43762306a36Sopenharmony_ci } 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (ret != 0) 44062306a36Sopenharmony_ci return ret; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci pr_debug("Completed connection reallegiance for Opcode: 0x%02x," 44362306a36Sopenharmony_ci " ITT: 0x%08x to CID: %hu.\n", cmd->iscsi_opcode, 44462306a36Sopenharmony_ci cmd->init_task_tag, conn->cid); 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci return 0; 44762306a36Sopenharmony_ci} 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci/* 45062306a36Sopenharmony_ci * Handles special after-the-fact actions related to TMRs. 45162306a36Sopenharmony_ci * Right now the only one that its really needed for is 45262306a36Sopenharmony_ci * connection recovery releated TASK_REASSIGN. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_ciint iscsit_tmr_post_handler(struct iscsit_cmd *cmd, struct iscsit_conn *conn) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req = cmd->tmr_req; 45762306a36Sopenharmony_ci struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (tmr_req->task_reassign && 46062306a36Sopenharmony_ci (se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) 46162306a36Sopenharmony_ci return iscsit_task_reassign_complete(tmr_req, conn); 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci return 0; 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_tmr_post_handler); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci/* 46862306a36Sopenharmony_ci * Nothing to do here, but leave it for good measure. :-) 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_cistatic int iscsit_task_reassign_prepare_read( 47162306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 47262306a36Sopenharmony_ci struct iscsit_conn *conn) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci return 0; 47562306a36Sopenharmony_ci} 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_cistatic void iscsit_task_reassign_prepare_unsolicited_dataout( 47862306a36Sopenharmony_ci struct iscsit_cmd *cmd, 47962306a36Sopenharmony_ci struct iscsit_conn *conn) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci int i, j; 48262306a36Sopenharmony_ci struct iscsi_pdu *pdu = NULL; 48362306a36Sopenharmony_ci struct iscsi_seq *seq = NULL; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 48662306a36Sopenharmony_ci cmd->data_sn = 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (cmd->immediate_data) 48962306a36Sopenharmony_ci cmd->r2t_offset += (cmd->first_burst_len - 49062306a36Sopenharmony_ci cmd->seq_start_offset); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) { 49362306a36Sopenharmony_ci cmd->write_data_done -= (cmd->immediate_data) ? 49462306a36Sopenharmony_ci (cmd->first_burst_len - 49562306a36Sopenharmony_ci cmd->seq_start_offset) : 49662306a36Sopenharmony_ci cmd->first_burst_len; 49762306a36Sopenharmony_ci cmd->first_burst_len = 0; 49862306a36Sopenharmony_ci return; 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci for (i = 0; i < cmd->pdu_count; i++) { 50262306a36Sopenharmony_ci pdu = &cmd->pdu_list[i]; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 50562306a36Sopenharmony_ci continue; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if ((pdu->offset >= cmd->seq_start_offset) && 50862306a36Sopenharmony_ci ((pdu->offset + pdu->length) <= 50962306a36Sopenharmony_ci cmd->seq_end_offset)) { 51062306a36Sopenharmony_ci cmd->first_burst_len -= pdu->length; 51162306a36Sopenharmony_ci cmd->write_data_done -= pdu->length; 51262306a36Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci } else { 51662306a36Sopenharmony_ci for (i = 0; i < cmd->seq_count; i++) { 51762306a36Sopenharmony_ci seq = &cmd->seq_list[i]; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (seq->type != SEQTYPE_UNSOLICITED) 52062306a36Sopenharmony_ci continue; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci cmd->write_data_done -= 52362306a36Sopenharmony_ci (seq->offset - seq->orig_offset); 52462306a36Sopenharmony_ci cmd->first_burst_len = 0; 52562306a36Sopenharmony_ci seq->data_sn = 0; 52662306a36Sopenharmony_ci seq->offset = seq->orig_offset; 52762306a36Sopenharmony_ci seq->next_burst_len = 0; 52862306a36Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) 53162306a36Sopenharmony_ci continue; 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci for (j = 0; j < seq->pdu_count; j++) { 53462306a36Sopenharmony_ci pdu = &cmd->pdu_list[j+seq->pdu_start]; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 53762306a36Sopenharmony_ci continue; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 54062306a36Sopenharmony_ci } 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci } 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic int iscsit_task_reassign_prepare_write( 54662306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 54762306a36Sopenharmony_ci struct iscsit_conn *conn) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct iscsit_cmd *cmd = tmr_req->ref_cmd; 55062306a36Sopenharmony_ci struct iscsi_pdu *pdu = NULL; 55162306a36Sopenharmony_ci struct iscsi_r2t *r2t = NULL, *r2t_tmp; 55262306a36Sopenharmony_ci int first_incomplete_r2t = 1, i = 0; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* 55562306a36Sopenharmony_ci * The command was in the process of receiving Unsolicited DataOUT when 55662306a36Sopenharmony_ci * the connection failed. 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_ci if (cmd->unsolicited_data) 55962306a36Sopenharmony_ci iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn); 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * The Initiator is requesting R2Ts starting from zero, skip 56362306a36Sopenharmony_ci * checking acknowledged R2Ts and start checking struct iscsi_r2ts 56462306a36Sopenharmony_ci * greater than zero. 56562306a36Sopenharmony_ci */ 56662306a36Sopenharmony_ci if (!tmr_req->exp_data_sn) 56762306a36Sopenharmony_ci goto drop_unacknowledged_r2ts; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci /* 57062306a36Sopenharmony_ci * We now check that the PDUs in DataOUT sequences below 57162306a36Sopenharmony_ci * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is 57262306a36Sopenharmony_ci * expecting next) have all the DataOUT they require to complete 57362306a36Sopenharmony_ci * the DataOUT sequence. First scan from R2TSN 0 to TMR 57462306a36Sopenharmony_ci * TASK_REASSIGN ExpDataSN-1. 57562306a36Sopenharmony_ci * 57662306a36Sopenharmony_ci * If we have not received all DataOUT in question, we must 57762306a36Sopenharmony_ci * make sure to make the appropriate changes to values in 57862306a36Sopenharmony_ci * struct iscsit_cmd (and elsewhere depending on session parameters) 57962306a36Sopenharmony_ci * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write() 58062306a36Sopenharmony_ci * will resend a new R2T for the DataOUT sequences in question. 58162306a36Sopenharmony_ci */ 58262306a36Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 58362306a36Sopenharmony_ci if (list_empty(&cmd->cmd_r2t_list)) { 58462306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 58562306a36Sopenharmony_ci return -1; 58662306a36Sopenharmony_ci } 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci if (r2t->r2t_sn >= tmr_req->exp_data_sn) 59162306a36Sopenharmony_ci continue; 59262306a36Sopenharmony_ci /* 59362306a36Sopenharmony_ci * Safely ignore Recovery R2Ts and R2Ts that have completed 59462306a36Sopenharmony_ci * DataOUT sequences. 59562306a36Sopenharmony_ci */ 59662306a36Sopenharmony_ci if (r2t->seq_complete) 59762306a36Sopenharmony_ci continue; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (r2t->recovery_r2t) 60062306a36Sopenharmony_ci continue; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci /* 60362306a36Sopenharmony_ci * DataSequenceInOrder=Yes: 60462306a36Sopenharmony_ci * 60562306a36Sopenharmony_ci * Taking into account the iSCSI implementation requirement of 60662306a36Sopenharmony_ci * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and 60762306a36Sopenharmony_ci * DataSequenceInOrder=Yes, we must take into consideration 60862306a36Sopenharmony_ci * the following: 60962306a36Sopenharmony_ci * 61062306a36Sopenharmony_ci * DataSequenceInOrder=No: 61162306a36Sopenharmony_ci * 61262306a36Sopenharmony_ci * Taking into account that the Initiator controls the (possibly 61362306a36Sopenharmony_ci * random) PDU Order in (possibly random) Sequence Order of 61462306a36Sopenharmony_ci * DataOUT the target requests with R2Ts, we must take into 61562306a36Sopenharmony_ci * consideration the following: 61662306a36Sopenharmony_ci * 61762306a36Sopenharmony_ci * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]: 61862306a36Sopenharmony_ci * 61962306a36Sopenharmony_ci * While processing non-complete R2T DataOUT sequence requests 62062306a36Sopenharmony_ci * the Target will re-request only the total sequence length 62162306a36Sopenharmony_ci * minus current received offset. This is because we must 62262306a36Sopenharmony_ci * assume the initiator will continue sending DataOUT from the 62362306a36Sopenharmony_ci * last PDU before the connection failed. 62462306a36Sopenharmony_ci * 62562306a36Sopenharmony_ci * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]: 62662306a36Sopenharmony_ci * 62762306a36Sopenharmony_ci * While processing non-complete R2T DataOUT sequence requests 62862306a36Sopenharmony_ci * the Target will re-request the entire DataOUT sequence if 62962306a36Sopenharmony_ci * any single PDU is missing from the sequence. This is because 63062306a36Sopenharmony_ci * we have no logical method to determine the next PDU offset, 63162306a36Sopenharmony_ci * and we must assume the Initiator will be sending any random 63262306a36Sopenharmony_ci * PDU offset in the current sequence after TASK_REASSIGN 63362306a36Sopenharmony_ci * has completed. 63462306a36Sopenharmony_ci */ 63562306a36Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) { 63662306a36Sopenharmony_ci if (!first_incomplete_r2t) { 63762306a36Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 63862306a36Sopenharmony_ci goto next; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) { 64262306a36Sopenharmony_ci cmd->data_sn = 0; 64362306a36Sopenharmony_ci cmd->r2t_offset -= (r2t->xfer_len - 64462306a36Sopenharmony_ci cmd->next_burst_len); 64562306a36Sopenharmony_ci first_incomplete_r2t = 0; 64662306a36Sopenharmony_ci goto next; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci cmd->data_sn = 0; 65062306a36Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci for (i = 0; i < cmd->pdu_count; i++) { 65362306a36Sopenharmony_ci pdu = &cmd->pdu_list[i]; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 65662306a36Sopenharmony_ci continue; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if ((pdu->offset >= r2t->offset) && 65962306a36Sopenharmony_ci (pdu->offset < (r2t->offset + 66062306a36Sopenharmony_ci r2t->xfer_len))) { 66162306a36Sopenharmony_ci cmd->next_burst_len -= pdu->length; 66262306a36Sopenharmony_ci cmd->write_data_done -= pdu->length; 66362306a36Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 66462306a36Sopenharmony_ci } 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci first_incomplete_r2t = 0; 66862306a36Sopenharmony_ci } else { 66962306a36Sopenharmony_ci struct iscsi_seq *seq; 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci seq = iscsit_get_seq_holder(cmd, r2t->offset, 67262306a36Sopenharmony_ci r2t->xfer_len); 67362306a36Sopenharmony_ci if (!seq) { 67462306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 67562306a36Sopenharmony_ci return -1; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci cmd->write_data_done -= 67962306a36Sopenharmony_ci (seq->offset - seq->orig_offset); 68062306a36Sopenharmony_ci seq->data_sn = 0; 68162306a36Sopenharmony_ci seq->offset = seq->orig_offset; 68262306a36Sopenharmony_ci seq->next_burst_len = 0; 68362306a36Sopenharmony_ci seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci cmd->seq_send_order--; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (conn->sess->sess_ops->DataPDUInOrder) 68862306a36Sopenharmony_ci goto next; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci for (i = 0; i < seq->pdu_count; i++) { 69162306a36Sopenharmony_ci pdu = &cmd->pdu_list[i+seq->pdu_start]; 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_ci if (pdu->status != ISCSI_PDU_RECEIVED_OK) 69462306a36Sopenharmony_ci continue; 69562306a36Sopenharmony_ci 69662306a36Sopenharmony_ci pdu->status = ISCSI_PDU_NOT_RECEIVED; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci } 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cinext: 70162306a36Sopenharmony_ci cmd->outstanding_r2ts--; 70262306a36Sopenharmony_ci } 70362306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci /* 70662306a36Sopenharmony_ci * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR 70762306a36Sopenharmony_ci * TASK_REASSIGN to the last R2T in the list.. We are also careful 70862306a36Sopenharmony_ci * to check that the Initiator is not requesting R2Ts for DataOUT 70962306a36Sopenharmony_ci * sequences it has already completed. 71062306a36Sopenharmony_ci * 71162306a36Sopenharmony_ci * Free each R2T in question and adjust values in struct iscsit_cmd 71262306a36Sopenharmony_ci * accordingly so iscsit_build_r2ts_for_cmd() do the rest of 71362306a36Sopenharmony_ci * the work after the TMR TASK_REASSIGN Response is sent. 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_cidrop_unacknowledged_r2ts: 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci cmd->cmd_flags &= ~ICF_SENT_LAST_R2T; 71862306a36Sopenharmony_ci cmd->r2t_sn = tmr_req->exp_data_sn; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci spin_lock_bh(&cmd->r2t_lock); 72162306a36Sopenharmony_ci list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) { 72262306a36Sopenharmony_ci /* 72362306a36Sopenharmony_ci * Skip up to the R2T Sequence number provided by the 72462306a36Sopenharmony_ci * iSCSI TASK_REASSIGN TMR 72562306a36Sopenharmony_ci */ 72662306a36Sopenharmony_ci if (r2t->r2t_sn < tmr_req->exp_data_sn) 72762306a36Sopenharmony_ci continue; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (r2t->seq_complete) { 73062306a36Sopenharmony_ci pr_err("Initiator is requesting R2Ts from" 73162306a36Sopenharmony_ci " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u," 73262306a36Sopenharmony_ci " Length: %u is already complete." 73362306a36Sopenharmony_ci " BAD INITIATOR ERL=2 IMPLEMENTATION!\n", 73462306a36Sopenharmony_ci tmr_req->exp_data_sn, r2t->r2t_sn, 73562306a36Sopenharmony_ci r2t->offset, r2t->xfer_len); 73662306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 73762306a36Sopenharmony_ci return -1; 73862306a36Sopenharmony_ci } 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci if (r2t->recovery_r2t) { 74162306a36Sopenharmony_ci iscsit_free_r2t(r2t, cmd); 74262306a36Sopenharmony_ci continue; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* DataSequenceInOrder=Yes: 74662306a36Sopenharmony_ci * 74762306a36Sopenharmony_ci * Taking into account the iSCSI implementation requirement of 74862306a36Sopenharmony_ci * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and 74962306a36Sopenharmony_ci * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts 75062306a36Sopenharmony_ci * entire transfer length from the commands R2T offset marker. 75162306a36Sopenharmony_ci * 75262306a36Sopenharmony_ci * DataSequenceInOrder=No: 75362306a36Sopenharmony_ci * 75462306a36Sopenharmony_ci * We subtract the difference from struct iscsi_seq between the 75562306a36Sopenharmony_ci * current offset and original offset from cmd->write_data_done 75662306a36Sopenharmony_ci * for account for DataOUT PDUs already received. Then reset 75762306a36Sopenharmony_ci * the current offset to the original and zero out the current 75862306a36Sopenharmony_ci * burst length, to make sure we re-request the entire DataOUT 75962306a36Sopenharmony_ci * sequence. 76062306a36Sopenharmony_ci */ 76162306a36Sopenharmony_ci if (conn->sess->sess_ops->DataSequenceInOrder) 76262306a36Sopenharmony_ci cmd->r2t_offset -= r2t->xfer_len; 76362306a36Sopenharmony_ci else 76462306a36Sopenharmony_ci cmd->seq_send_order--; 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci cmd->outstanding_r2ts--; 76762306a36Sopenharmony_ci iscsit_free_r2t(r2t, cmd); 76862306a36Sopenharmony_ci } 76962306a36Sopenharmony_ci spin_unlock_bh(&cmd->r2t_lock); 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci return 0; 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* 77562306a36Sopenharmony_ci * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for 77662306a36Sopenharmony_ci * a given struct iscsit_cmd. 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_ciint iscsit_check_task_reassign_expdatasn( 77962306a36Sopenharmony_ci struct iscsi_tmr_req *tmr_req, 78062306a36Sopenharmony_ci struct iscsit_conn *conn) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct iscsit_cmd *ref_cmd = tmr_req->ref_cmd; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) 78562306a36Sopenharmony_ci return 0; 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (ref_cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) 78862306a36Sopenharmony_ci return 0; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci if (ref_cmd->data_direction == DMA_NONE) 79162306a36Sopenharmony_ci return 0; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* 79462306a36Sopenharmony_ci * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN 79562306a36Sopenharmony_ci * of DataIN the Initiator is expecting. 79662306a36Sopenharmony_ci * 79762306a36Sopenharmony_ci * Also check that the Initiator is not re-requesting DataIN that has 79862306a36Sopenharmony_ci * already been acknowledged with a DataAck SNACK. 79962306a36Sopenharmony_ci */ 80062306a36Sopenharmony_ci if (ref_cmd->data_direction == DMA_FROM_DEVICE) { 80162306a36Sopenharmony_ci if (tmr_req->exp_data_sn > ref_cmd->data_sn) { 80262306a36Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for READ" 80362306a36Sopenharmony_ci " in TMR TASK_REASSIGN greater than command's" 80462306a36Sopenharmony_ci " DataSN: 0x%08x.\n", tmr_req->exp_data_sn, 80562306a36Sopenharmony_ci ref_cmd->data_sn); 80662306a36Sopenharmony_ci return -1; 80762306a36Sopenharmony_ci } 80862306a36Sopenharmony_ci if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && 80962306a36Sopenharmony_ci (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) { 81062306a36Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for READ" 81162306a36Sopenharmony_ci " in TMR TASK_REASSIGN for previously" 81262306a36Sopenharmony_ci " acknowledged DataIN: 0x%08x," 81362306a36Sopenharmony_ci " protocol error\n", tmr_req->exp_data_sn, 81462306a36Sopenharmony_ci ref_cmd->acked_data_sn); 81562306a36Sopenharmony_ci return -1; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci return iscsit_task_reassign_prepare_read(tmr_req, conn); 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci /* 82162306a36Sopenharmony_ci * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN 82262306a36Sopenharmony_ci * for R2Ts the Initiator is expecting. 82362306a36Sopenharmony_ci * 82462306a36Sopenharmony_ci * Do the magic in iscsit_task_reassign_prepare_write(). 82562306a36Sopenharmony_ci */ 82662306a36Sopenharmony_ci if (ref_cmd->data_direction == DMA_TO_DEVICE) { 82762306a36Sopenharmony_ci if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) { 82862306a36Sopenharmony_ci pr_err("Received ExpDataSN: 0x%08x for WRITE" 82962306a36Sopenharmony_ci " in TMR TASK_REASSIGN greater than command's" 83062306a36Sopenharmony_ci " R2TSN: 0x%08x.\n", tmr_req->exp_data_sn, 83162306a36Sopenharmony_ci ref_cmd->r2t_sn); 83262306a36Sopenharmony_ci return -1; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci return iscsit_task_reassign_prepare_write(tmr_req, conn); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci pr_err("Unknown iSCSI data_direction: 0x%02x\n", 83862306a36Sopenharmony_ci ref_cmd->data_direction); 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return -1; 84162306a36Sopenharmony_ci} 842