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