162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci * This file contains error recovery level zero functions used by
462306a36Sopenharmony_ci * the iSCSI Target driver.
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci ******************************************************************************/
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/sched/signal.h>
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <scsi/iscsi_proto.h>
1562306a36Sopenharmony_ci#include <target/target_core_base.h>
1662306a36Sopenharmony_ci#include <target/target_core_fabric.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_erl0.h"
2162306a36Sopenharmony_ci#include "iscsi_target_erl1.h"
2262306a36Sopenharmony_ci#include "iscsi_target_erl2.h"
2362306a36Sopenharmony_ci#include "iscsi_target_util.h"
2462306a36Sopenharmony_ci#include "iscsi_target.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/*
2762306a36Sopenharmony_ci *	Used to set values in struct iscsit_cmd that iscsit_dataout_check_sequence()
2862306a36Sopenharmony_ci *	checks against to determine a PDU's Offset+Length is within the current
2962306a36Sopenharmony_ci *	DataOUT Sequence.  Used for DataSequenceInOrder=Yes only.
3062306a36Sopenharmony_ci */
3162306a36Sopenharmony_civoid iscsit_set_dataout_sequence_values(
3262306a36Sopenharmony_ci	struct iscsit_cmd *cmd)
3362306a36Sopenharmony_ci{
3462306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
3562306a36Sopenharmony_ci	/*
3662306a36Sopenharmony_ci	 * Still set seq_start_offset and seq_end_offset for Unsolicited
3762306a36Sopenharmony_ci	 * DataOUT, even if DataSequenceInOrder=No.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	if (cmd->unsolicited_data) {
4062306a36Sopenharmony_ci		cmd->seq_start_offset = cmd->write_data_done;
4162306a36Sopenharmony_ci		cmd->seq_end_offset = min(cmd->se_cmd.data_length,
4262306a36Sopenharmony_ci					conn->sess->sess_ops->FirstBurstLength);
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci	}
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (!conn->sess->sess_ops->DataSequenceInOrder)
4762306a36Sopenharmony_ci		return;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	if (!cmd->seq_start_offset && !cmd->seq_end_offset) {
5062306a36Sopenharmony_ci		cmd->seq_start_offset = cmd->write_data_done;
5162306a36Sopenharmony_ci		cmd->seq_end_offset = (cmd->se_cmd.data_length >
5262306a36Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) ?
5362306a36Sopenharmony_ci			(cmd->write_data_done +
5462306a36Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) : cmd->se_cmd.data_length;
5562306a36Sopenharmony_ci	} else {
5662306a36Sopenharmony_ci		cmd->seq_start_offset = cmd->seq_end_offset;
5762306a36Sopenharmony_ci		cmd->seq_end_offset = ((cmd->seq_end_offset +
5862306a36Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) >=
5962306a36Sopenharmony_ci			cmd->se_cmd.data_length) ? cmd->se_cmd.data_length :
6062306a36Sopenharmony_ci			(cmd->seq_end_offset +
6162306a36Sopenharmony_ci			 conn->sess->sess_ops->MaxBurstLength);
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int iscsit_dataout_within_command_recovery_check(
6662306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
6762306a36Sopenharmony_ci	unsigned char *buf)
6862306a36Sopenharmony_ci{
6962306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
7062306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
7162306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/*
7462306a36Sopenharmony_ci	 * We do the within-command recovery checks here as it is
7562306a36Sopenharmony_ci	 * the first function called in iscsi_check_pre_dataout().
7662306a36Sopenharmony_ci	 * Basically, if we are in within-command recovery and
7762306a36Sopenharmony_ci	 * the PDU does not contain the offset the sequence needs,
7862306a36Sopenharmony_ci	 * dump the payload.
7962306a36Sopenharmony_ci	 *
8062306a36Sopenharmony_ci	 * This only applies to DataPDUInOrder=Yes, for
8162306a36Sopenharmony_ci	 * DataPDUInOrder=No we only re-request the failed PDU
8262306a36Sopenharmony_ci	 * and check that all PDUs in a sequence are received
8362306a36Sopenharmony_ci	 * upon end of sequence.
8462306a36Sopenharmony_ci	 */
8562306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
8662306a36Sopenharmony_ci		if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) &&
8762306a36Sopenharmony_ci		    cmd->write_data_done != be32_to_cpu(hdr->offset))
8862306a36Sopenharmony_ci			goto dump;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci		cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY;
9162306a36Sopenharmony_ci	} else {
9262306a36Sopenharmony_ci		struct iscsi_seq *seq;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
9562306a36Sopenharmony_ci					    payload_length);
9662306a36Sopenharmony_ci		if (!seq)
9762306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
9862306a36Sopenharmony_ci		/*
9962306a36Sopenharmony_ci		 * Set the struct iscsi_seq pointer to reuse later.
10062306a36Sopenharmony_ci		 */
10162306a36Sopenharmony_ci		cmd->seq_ptr = seq;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataPDUInOrder) {
10462306a36Sopenharmony_ci			if (seq->status ==
10562306a36Sopenharmony_ci			    DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
10662306a36Sopenharmony_ci			   (seq->offset != be32_to_cpu(hdr->offset) ||
10762306a36Sopenharmony_ci			    seq->data_sn != be32_to_cpu(hdr->datasn)))
10862306a36Sopenharmony_ci				goto dump;
10962306a36Sopenharmony_ci		} else {
11062306a36Sopenharmony_ci			if (seq->status ==
11162306a36Sopenharmony_ci			     DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
11262306a36Sopenharmony_ci			    seq->data_sn != be32_to_cpu(hdr->datasn))
11362306a36Sopenharmony_ci				goto dump;
11462306a36Sopenharmony_ci		}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci		if (seq->status == DATAOUT_SEQUENCE_COMPLETE)
11762306a36Sopenharmony_ci			goto dump;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci		if (seq->status != DATAOUT_SEQUENCE_COMPLETE)
12062306a36Sopenharmony_ci			seq->status = 0;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return DATAOUT_NORMAL;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cidump:
12662306a36Sopenharmony_ci	pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:"
12762306a36Sopenharmony_ci		" 0x%08x\n", hdr->offset, payload_length, hdr->datasn);
12862306a36Sopenharmony_ci	return iscsit_dump_data_payload(conn, payload_length, 1);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic int iscsit_dataout_check_unsolicited_sequence(
13262306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
13362306a36Sopenharmony_ci	unsigned char *buf)
13462306a36Sopenharmony_ci{
13562306a36Sopenharmony_ci	u32 first_burst_len;
13662306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
13762306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
13862306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
14262306a36Sopenharmony_ci	   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
14362306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x with Offset: %u,"
14462306a36Sopenharmony_ci		" Length: %u outside of Unsolicited Sequence %u:%u while"
14562306a36Sopenharmony_ci		" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
14662306a36Sopenharmony_ci		be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
14762306a36Sopenharmony_ci			cmd->seq_end_offset);
14862306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	first_burst_len = (cmd->first_burst_len + payload_length);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) {
15462306a36Sopenharmony_ci		pr_err("Total %u bytes exceeds FirstBurstLength: %u"
15562306a36Sopenharmony_ci			" for this Unsolicited DataOut Burst.\n",
15662306a36Sopenharmony_ci			first_burst_len, conn->sess->sess_ops->FirstBurstLength);
15762306a36Sopenharmony_ci		transport_send_check_condition_and_sense(&cmd->se_cmd,
15862306a36Sopenharmony_ci				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
15962306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/*
16362306a36Sopenharmony_ci	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
16462306a36Sopenharmony_ci	 * checks for the current Unsolicited DataOUT Sequence.
16562306a36Sopenharmony_ci	 */
16662306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
16762306a36Sopenharmony_ci		/*
16862306a36Sopenharmony_ci		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
16962306a36Sopenharmony_ci		 * sequence checks are handled in
17062306a36Sopenharmony_ci		 * iscsit_dataout_datapduinorder_no_fbit().
17162306a36Sopenharmony_ci		 */
17262306a36Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder)
17362306a36Sopenharmony_ci			goto out;
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci		if ((first_burst_len != cmd->se_cmd.data_length) &&
17662306a36Sopenharmony_ci		    (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) {
17762306a36Sopenharmony_ci			pr_err("Unsolicited non-immediate data"
17862306a36Sopenharmony_ci			" received %u does not equal FirstBurstLength: %u, and"
17962306a36Sopenharmony_ci			" does not equal ExpXferLen %u.\n", first_burst_len,
18062306a36Sopenharmony_ci				conn->sess->sess_ops->FirstBurstLength,
18162306a36Sopenharmony_ci				cmd->se_cmd.data_length);
18262306a36Sopenharmony_ci			transport_send_check_condition_and_sense(&cmd->se_cmd,
18362306a36Sopenharmony_ci					TCM_INCORRECT_AMOUNT_OF_DATA, 0);
18462306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
18562306a36Sopenharmony_ci		}
18662306a36Sopenharmony_ci	} else {
18762306a36Sopenharmony_ci		if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) {
18862306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x reached"
18962306a36Sopenharmony_ci			" FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
19062306a36Sopenharmony_ci				" error.\n", cmd->init_task_tag,
19162306a36Sopenharmony_ci				conn->sess->sess_ops->FirstBurstLength);
19262306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
19362306a36Sopenharmony_ci		}
19462306a36Sopenharmony_ci		if (first_burst_len == cmd->se_cmd.data_length) {
19562306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x reached"
19662306a36Sopenharmony_ci			" ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
19762306a36Sopenharmony_ci			" error.\n", cmd->init_task_tag, cmd->se_cmd.data_length);
19862306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
19962306a36Sopenharmony_ci		}
20062306a36Sopenharmony_ci	}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ciout:
20362306a36Sopenharmony_ci	return DATAOUT_NORMAL;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic int iscsit_dataout_check_sequence(
20762306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
20862306a36Sopenharmony_ci	unsigned char *buf)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	u32 next_burst_len;
21162306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
21262306a36Sopenharmony_ci	struct iscsi_seq *seq = NULL;
21362306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
21462306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci	/*
21762306a36Sopenharmony_ci	 * For DataSequenceInOrder=Yes: Check that the offset and offset+length
21862306a36Sopenharmony_ci	 * is within range as defined by iscsi_set_dataout_sequence_values().
21962306a36Sopenharmony_ci	 *
22062306a36Sopenharmony_ci	 * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for
22162306a36Sopenharmony_ci	 * offset+length tuple.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
22462306a36Sopenharmony_ci		/*
22562306a36Sopenharmony_ci		 * Due to possibility of recovery DataOUT sent by the initiator
22662306a36Sopenharmony_ci		 * fullfilling an Recovery R2T, it's best to just dump the
22762306a36Sopenharmony_ci		 * payload here, instead of erroring out.
22862306a36Sopenharmony_ci		 */
22962306a36Sopenharmony_ci		if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
23062306a36Sopenharmony_ci		   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
23162306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x with Offset: %u,"
23262306a36Sopenharmony_ci			" Length: %u outside of Sequence %u:%u while"
23362306a36Sopenharmony_ci			" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
23462306a36Sopenharmony_ci			be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
23562306a36Sopenharmony_ci				cmd->seq_end_offset);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
23862306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
23962306a36Sopenharmony_ci			return DATAOUT_WITHIN_COMMAND_RECOVERY;
24062306a36Sopenharmony_ci		}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci		next_burst_len = (cmd->next_burst_len + payload_length);
24362306a36Sopenharmony_ci	} else {
24462306a36Sopenharmony_ci		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
24562306a36Sopenharmony_ci					    payload_length);
24662306a36Sopenharmony_ci		if (!seq)
24762306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
24862306a36Sopenharmony_ci		/*
24962306a36Sopenharmony_ci		 * Set the struct iscsi_seq pointer to reuse later.
25062306a36Sopenharmony_ci		 */
25162306a36Sopenharmony_ci		cmd->seq_ptr = seq;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci		if (seq->status == DATAOUT_SEQUENCE_COMPLETE) {
25462306a36Sopenharmony_ci			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
25562306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
25662306a36Sopenharmony_ci			return DATAOUT_WITHIN_COMMAND_RECOVERY;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci		next_burst_len = (seq->next_burst_len + payload_length);
26062306a36Sopenharmony_ci	}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) {
26362306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x, NextBurstLength: %u and"
26462306a36Sopenharmony_ci			" Length: %u exceeds MaxBurstLength: %u. protocol"
26562306a36Sopenharmony_ci			" error.\n", cmd->init_task_tag,
26662306a36Sopenharmony_ci			(next_burst_len - payload_length),
26762306a36Sopenharmony_ci			payload_length, conn->sess->sess_ops->MaxBurstLength);
26862306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	/*
27262306a36Sopenharmony_ci	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
27362306a36Sopenharmony_ci	 * checks for the current DataOUT Sequence.
27462306a36Sopenharmony_ci	 */
27562306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
27662306a36Sopenharmony_ci		/*
27762306a36Sopenharmony_ci		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
27862306a36Sopenharmony_ci		 * sequence checks are handled in
27962306a36Sopenharmony_ci		 * iscsit_dataout_datapduinorder_no_fbit().
28062306a36Sopenharmony_ci		 */
28162306a36Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder)
28262306a36Sopenharmony_ci			goto out;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
28562306a36Sopenharmony_ci			if ((next_burst_len <
28662306a36Sopenharmony_ci			     conn->sess->sess_ops->MaxBurstLength) &&
28762306a36Sopenharmony_ci			   ((cmd->write_data_done + payload_length) <
28862306a36Sopenharmony_ci			     cmd->se_cmd.data_length)) {
28962306a36Sopenharmony_ci				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
29062306a36Sopenharmony_ci				" before end of DataOUT sequence, protocol"
29162306a36Sopenharmony_ci				" error.\n", cmd->init_task_tag);
29262306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
29362306a36Sopenharmony_ci			}
29462306a36Sopenharmony_ci		} else {
29562306a36Sopenharmony_ci			if (next_burst_len < seq->xfer_len) {
29662306a36Sopenharmony_ci				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
29762306a36Sopenharmony_ci				" before end of DataOUT sequence, protocol"
29862306a36Sopenharmony_ci				" error.\n", cmd->init_task_tag);
29962306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
30062306a36Sopenharmony_ci			}
30162306a36Sopenharmony_ci		}
30262306a36Sopenharmony_ci	} else {
30362306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
30462306a36Sopenharmony_ci			if (next_burst_len ==
30562306a36Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength) {
30662306a36Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
30762306a36Sopenharmony_ci				" MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is"
30862306a36Sopenharmony_ci				" not set, protocol error.", cmd->init_task_tag,
30962306a36Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength);
31062306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
31162306a36Sopenharmony_ci			}
31262306a36Sopenharmony_ci			if ((cmd->write_data_done + payload_length) ==
31362306a36Sopenharmony_ci					cmd->se_cmd.data_length) {
31462306a36Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
31562306a36Sopenharmony_ci				" last DataOUT PDU in sequence but ISCSI_FLAG_"
31662306a36Sopenharmony_ci				"CMD_FINAL is not set, protocol error.\n",
31762306a36Sopenharmony_ci					cmd->init_task_tag);
31862306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
31962306a36Sopenharmony_ci			}
32062306a36Sopenharmony_ci		} else {
32162306a36Sopenharmony_ci			if (next_burst_len == seq->xfer_len) {
32262306a36Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
32362306a36Sopenharmony_ci				" last DataOUT PDU in sequence but ISCSI_FLAG_"
32462306a36Sopenharmony_ci				"CMD_FINAL is not set, protocol error.\n",
32562306a36Sopenharmony_ci					cmd->init_task_tag);
32662306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
32762306a36Sopenharmony_ci			}
32862306a36Sopenharmony_ci		}
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ciout:
33262306a36Sopenharmony_ci	return DATAOUT_NORMAL;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cistatic int iscsit_dataout_check_datasn(
33662306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
33762306a36Sopenharmony_ci	unsigned char *buf)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	u32 data_sn = 0;
34062306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
34162306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
34262306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	/*
34562306a36Sopenharmony_ci	 * Considering the target has no method of re-requesting DataOUT
34662306a36Sopenharmony_ci	 * by DataSN, if we receieve a greater DataSN than expected we
34762306a36Sopenharmony_ci	 * assume the functions for DataPDUInOrder=[Yes,No] below will
34862306a36Sopenharmony_ci	 * handle it.
34962306a36Sopenharmony_ci	 *
35062306a36Sopenharmony_ci	 * If the DataSN is less than expected, dump the payload.
35162306a36Sopenharmony_ci	 */
35262306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder)
35362306a36Sopenharmony_ci		data_sn = cmd->data_sn;
35462306a36Sopenharmony_ci	else {
35562306a36Sopenharmony_ci		struct iscsi_seq *seq = cmd->seq_ptr;
35662306a36Sopenharmony_ci		data_sn = seq->data_sn;
35762306a36Sopenharmony_ci	}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	if (be32_to_cpu(hdr->datasn) > data_sn) {
36062306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
36162306a36Sopenharmony_ci			" higher than expected 0x%08x.\n", cmd->init_task_tag,
36262306a36Sopenharmony_ci				be32_to_cpu(hdr->datasn), data_sn);
36362306a36Sopenharmony_ci		goto recover;
36462306a36Sopenharmony_ci	} else if (be32_to_cpu(hdr->datasn) < data_sn) {
36562306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
36662306a36Sopenharmony_ci			" lower than expected 0x%08x, discarding payload.\n",
36762306a36Sopenharmony_ci			cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn);
36862306a36Sopenharmony_ci		goto dump;
36962306a36Sopenharmony_ci	}
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return DATAOUT_NORMAL;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cirecover:
37462306a36Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
37562306a36Sopenharmony_ci		pr_err("Unable to perform within-command recovery"
37662306a36Sopenharmony_ci				" while ERL=0.\n");
37762306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
37862306a36Sopenharmony_ci	}
37962306a36Sopenharmony_cidump:
38062306a36Sopenharmony_ci	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
38162306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	return DATAOUT_WITHIN_COMMAND_RECOVERY;
38462306a36Sopenharmony_ci}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_yes(
38762306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
38862306a36Sopenharmony_ci	unsigned char *buf)
38962306a36Sopenharmony_ci{
39062306a36Sopenharmony_ci	int dump = 0, recovery = 0;
39162306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
39262306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
39362306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	/*
39662306a36Sopenharmony_ci	 * For DataSequenceInOrder=Yes: If the offset is greater than the global
39762306a36Sopenharmony_ci	 * DataPDUInOrder=Yes offset counter in struct iscsit_cmd a protcol error has
39862306a36Sopenharmony_ci	 * occurred and fail the connection.
39962306a36Sopenharmony_ci	 *
40062306a36Sopenharmony_ci	 * For DataSequenceInOrder=No: If the offset is greater than the per
40162306a36Sopenharmony_ci	 * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
40262306a36Sopenharmony_ci	 * error has occurred and fail the connection.
40362306a36Sopenharmony_ci	 */
40462306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
40562306a36Sopenharmony_ci		if (be32_to_cpu(hdr->offset) != cmd->write_data_done) {
40662306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
40762306a36Sopenharmony_ci			" %u different than expected %u.\n", cmd->init_task_tag,
40862306a36Sopenharmony_ci				be32_to_cpu(hdr->offset), cmd->write_data_done);
40962306a36Sopenharmony_ci			recovery = 1;
41062306a36Sopenharmony_ci			goto recover;
41162306a36Sopenharmony_ci		}
41262306a36Sopenharmony_ci	} else {
41362306a36Sopenharmony_ci		struct iscsi_seq *seq = cmd->seq_ptr;
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (be32_to_cpu(hdr->offset) > seq->offset) {
41662306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
41762306a36Sopenharmony_ci			" %u greater than expected %u.\n", cmd->init_task_tag,
41862306a36Sopenharmony_ci				be32_to_cpu(hdr->offset), seq->offset);
41962306a36Sopenharmony_ci			recovery = 1;
42062306a36Sopenharmony_ci			goto recover;
42162306a36Sopenharmony_ci		} else if (be32_to_cpu(hdr->offset) < seq->offset) {
42262306a36Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
42362306a36Sopenharmony_ci			" %u less than expected %u, discarding payload.\n",
42462306a36Sopenharmony_ci				cmd->init_task_tag, be32_to_cpu(hdr->offset),
42562306a36Sopenharmony_ci				seq->offset);
42662306a36Sopenharmony_ci			dump = 1;
42762306a36Sopenharmony_ci			goto dump;
42862306a36Sopenharmony_ci		}
42962306a36Sopenharmony_ci	}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return DATAOUT_NORMAL;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_cirecover:
43462306a36Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
43562306a36Sopenharmony_ci		pr_err("Unable to perform within-command recovery"
43662306a36Sopenharmony_ci				" while ERL=0.\n");
43762306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
43862306a36Sopenharmony_ci	}
43962306a36Sopenharmony_cidump:
44062306a36Sopenharmony_ci	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
44162306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	return (recovery) ? iscsit_recover_dataout_sequence(cmd,
44462306a36Sopenharmony_ci		be32_to_cpu(hdr->offset), payload_length) :
44562306a36Sopenharmony_ci	       (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_no(
44962306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
45062306a36Sopenharmony_ci	unsigned char *buf)
45162306a36Sopenharmony_ci{
45262306a36Sopenharmony_ci	struct iscsi_pdu *pdu;
45362306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
45462306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	pdu = iscsit_get_pdu_holder(cmd, be32_to_cpu(hdr->offset),
45762306a36Sopenharmony_ci				    payload_length);
45862306a36Sopenharmony_ci	if (!pdu)
45962306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci	cmd->pdu_ptr = pdu;
46262306a36Sopenharmony_ci
46362306a36Sopenharmony_ci	switch (pdu->status) {
46462306a36Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
46562306a36Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
46662306a36Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
46762306a36Sopenharmony_ci		break;
46862306a36Sopenharmony_ci	case ISCSI_PDU_RECEIVED_OK:
46962306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x received already gotten"
47062306a36Sopenharmony_ci			" Offset: %u, Length: %u\n", cmd->init_task_tag,
47162306a36Sopenharmony_ci				be32_to_cpu(hdr->offset), payload_length);
47262306a36Sopenharmony_ci		return iscsit_dump_data_payload(cmd->conn, payload_length, 1);
47362306a36Sopenharmony_ci	default:
47462306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return DATAOUT_NORMAL;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int iscsit_dataout_update_r2t(struct iscsit_cmd *cmd, u32 offset, u32 length)
48162306a36Sopenharmony_ci{
48262306a36Sopenharmony_ci	struct iscsi_r2t *r2t;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	if (cmd->unsolicited_data)
48562306a36Sopenharmony_ci		return 0;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	r2t = iscsit_get_r2t_for_eos(cmd, offset, length);
48862306a36Sopenharmony_ci	if (!r2t)
48962306a36Sopenharmony_ci		return -1;
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
49262306a36Sopenharmony_ci	r2t->seq_complete = 1;
49362306a36Sopenharmony_ci	cmd->outstanding_r2ts--;
49462306a36Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	return 0;
49762306a36Sopenharmony_ci}
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_cistatic int iscsit_dataout_update_datapduinorder_no(
50062306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
50162306a36Sopenharmony_ci	u32 data_sn,
50262306a36Sopenharmony_ci	int f_bit)
50362306a36Sopenharmony_ci{
50462306a36Sopenharmony_ci	int ret = 0;
50562306a36Sopenharmony_ci	struct iscsi_pdu *pdu = cmd->pdu_ptr;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	pdu->data_sn = data_sn;
50862306a36Sopenharmony_ci
50962306a36Sopenharmony_ci	switch (pdu->status) {
51062306a36Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
51162306a36Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
51262306a36Sopenharmony_ci		break;
51362306a36Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
51462306a36Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
51562306a36Sopenharmony_ci		break;
51662306a36Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
51762306a36Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
51862306a36Sopenharmony_ci		break;
51962306a36Sopenharmony_ci	default:
52062306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	if (f_bit) {
52462306a36Sopenharmony_ci		ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu);
52562306a36Sopenharmony_ci		if (ret == DATAOUT_CANNOT_RECOVER)
52662306a36Sopenharmony_ci			return ret;
52762306a36Sopenharmony_ci	}
52862306a36Sopenharmony_ci
52962306a36Sopenharmony_ci	return DATAOUT_NORMAL;
53062306a36Sopenharmony_ci}
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_cistatic int iscsit_dataout_post_crc_passed(
53362306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
53462306a36Sopenharmony_ci	unsigned char *buf)
53562306a36Sopenharmony_ci{
53662306a36Sopenharmony_ci	int ret, send_r2t = 0;
53762306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
53862306a36Sopenharmony_ci	struct iscsi_seq *seq = NULL;
53962306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
54062306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	if (cmd->unsolicited_data) {
54362306a36Sopenharmony_ci		if ((cmd->first_burst_len + payload_length) ==
54462306a36Sopenharmony_ci		     conn->sess->sess_ops->FirstBurstLength) {
54562306a36Sopenharmony_ci			if (iscsit_dataout_update_r2t(cmd, be32_to_cpu(hdr->offset),
54662306a36Sopenharmony_ci					payload_length) < 0)
54762306a36Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
54862306a36Sopenharmony_ci			send_r2t = 1;
54962306a36Sopenharmony_ci		}
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder) {
55262306a36Sopenharmony_ci			ret = iscsit_dataout_update_datapduinorder_no(cmd,
55362306a36Sopenharmony_ci				be32_to_cpu(hdr->datasn),
55462306a36Sopenharmony_ci				(hdr->flags & ISCSI_FLAG_CMD_FINAL));
55562306a36Sopenharmony_ci			if (ret == DATAOUT_CANNOT_RECOVER)
55662306a36Sopenharmony_ci				return ret;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		cmd->first_burst_len += payload_length;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder)
56262306a36Sopenharmony_ci			cmd->data_sn++;
56362306a36Sopenharmony_ci		else {
56462306a36Sopenharmony_ci			seq = cmd->seq_ptr;
56562306a36Sopenharmony_ci			seq->data_sn++;
56662306a36Sopenharmony_ci			seq->offset += payload_length;
56762306a36Sopenharmony_ci		}
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci		if (send_r2t) {
57062306a36Sopenharmony_ci			if (seq)
57162306a36Sopenharmony_ci				seq->status = DATAOUT_SEQUENCE_COMPLETE;
57262306a36Sopenharmony_ci			cmd->first_burst_len = 0;
57362306a36Sopenharmony_ci			cmd->unsolicited_data = 0;
57462306a36Sopenharmony_ci		}
57562306a36Sopenharmony_ci	} else {
57662306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
57762306a36Sopenharmony_ci			if ((cmd->next_burst_len + payload_length) ==
57862306a36Sopenharmony_ci			     conn->sess->sess_ops->MaxBurstLength) {
57962306a36Sopenharmony_ci				if (iscsit_dataout_update_r2t(cmd,
58062306a36Sopenharmony_ci						be32_to_cpu(hdr->offset),
58162306a36Sopenharmony_ci						payload_length) < 0)
58262306a36Sopenharmony_ci					return DATAOUT_CANNOT_RECOVER;
58362306a36Sopenharmony_ci				send_r2t = 1;
58462306a36Sopenharmony_ci			}
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci			if (!conn->sess->sess_ops->DataPDUInOrder) {
58762306a36Sopenharmony_ci				ret = iscsit_dataout_update_datapduinorder_no(
58862306a36Sopenharmony_ci						cmd, be32_to_cpu(hdr->datasn),
58962306a36Sopenharmony_ci						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
59062306a36Sopenharmony_ci				if (ret == DATAOUT_CANNOT_RECOVER)
59162306a36Sopenharmony_ci					return ret;
59262306a36Sopenharmony_ci			}
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci			cmd->next_burst_len += payload_length;
59562306a36Sopenharmony_ci			cmd->data_sn++;
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci			if (send_r2t)
59862306a36Sopenharmony_ci				cmd->next_burst_len = 0;
59962306a36Sopenharmony_ci		} else {
60062306a36Sopenharmony_ci			seq = cmd->seq_ptr;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci			if ((seq->next_burst_len + payload_length) ==
60362306a36Sopenharmony_ci			     seq->xfer_len) {
60462306a36Sopenharmony_ci				if (iscsit_dataout_update_r2t(cmd,
60562306a36Sopenharmony_ci						be32_to_cpu(hdr->offset),
60662306a36Sopenharmony_ci						payload_length) < 0)
60762306a36Sopenharmony_ci					return DATAOUT_CANNOT_RECOVER;
60862306a36Sopenharmony_ci				send_r2t = 1;
60962306a36Sopenharmony_ci			}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci			if (!conn->sess->sess_ops->DataPDUInOrder) {
61262306a36Sopenharmony_ci				ret = iscsit_dataout_update_datapduinorder_no(
61362306a36Sopenharmony_ci						cmd, be32_to_cpu(hdr->datasn),
61462306a36Sopenharmony_ci						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
61562306a36Sopenharmony_ci				if (ret == DATAOUT_CANNOT_RECOVER)
61662306a36Sopenharmony_ci					return ret;
61762306a36Sopenharmony_ci			}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_ci			seq->data_sn++;
62062306a36Sopenharmony_ci			seq->offset += payload_length;
62162306a36Sopenharmony_ci			seq->next_burst_len += payload_length;
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci			if (send_r2t) {
62462306a36Sopenharmony_ci				seq->next_burst_len = 0;
62562306a36Sopenharmony_ci				seq->status = DATAOUT_SEQUENCE_COMPLETE;
62662306a36Sopenharmony_ci			}
62762306a36Sopenharmony_ci		}
62862306a36Sopenharmony_ci	}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder)
63162306a36Sopenharmony_ci		cmd->data_sn = 0;
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	cmd->write_data_done += payload_length;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	if (cmd->write_data_done == cmd->se_cmd.data_length)
63662306a36Sopenharmony_ci		return DATAOUT_SEND_TO_TRANSPORT;
63762306a36Sopenharmony_ci	else if (send_r2t)
63862306a36Sopenharmony_ci		return DATAOUT_SEND_R2T;
63962306a36Sopenharmony_ci	else
64062306a36Sopenharmony_ci		return DATAOUT_NORMAL;
64162306a36Sopenharmony_ci}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_cistatic int iscsit_dataout_post_crc_failed(
64462306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
64562306a36Sopenharmony_ci	unsigned char *buf)
64662306a36Sopenharmony_ci{
64762306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
64862306a36Sopenharmony_ci	struct iscsi_pdu *pdu;
64962306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
65062306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
65162306a36Sopenharmony_ci
65262306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataPDUInOrder)
65362306a36Sopenharmony_ci		goto recover;
65462306a36Sopenharmony_ci	/*
65562306a36Sopenharmony_ci	 * The rest of this function is only called when DataPDUInOrder=No.
65662306a36Sopenharmony_ci	 */
65762306a36Sopenharmony_ci	pdu = cmd->pdu_ptr;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	switch (pdu->status) {
66062306a36Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
66162306a36Sopenharmony_ci		pdu->status = ISCSI_PDU_CRC_FAILED;
66262306a36Sopenharmony_ci		break;
66362306a36Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
66462306a36Sopenharmony_ci		break;
66562306a36Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
66662306a36Sopenharmony_ci		pdu->status = ISCSI_PDU_CRC_FAILED;
66762306a36Sopenharmony_ci		break;
66862306a36Sopenharmony_ci	default:
66962306a36Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cirecover:
67362306a36Sopenharmony_ci	return iscsit_recover_dataout_sequence(cmd, be32_to_cpu(hdr->offset),
67462306a36Sopenharmony_ci						payload_length);
67562306a36Sopenharmony_ci}
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci/*
67862306a36Sopenharmony_ci *	Called from iscsit_handle_data_out() before DataOUT Payload is received
67962306a36Sopenharmony_ci *	and CRC computed.
68062306a36Sopenharmony_ci */
68162306a36Sopenharmony_ciint iscsit_check_pre_dataout(
68262306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
68362306a36Sopenharmony_ci	unsigned char *buf)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	int ret;
68662306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	ret = iscsit_dataout_within_command_recovery_check(cmd, buf);
68962306a36Sopenharmony_ci	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
69062306a36Sopenharmony_ci	    (ret == DATAOUT_CANNOT_RECOVER))
69162306a36Sopenharmony_ci		return ret;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	ret = iscsit_dataout_check_datasn(cmd, buf);
69462306a36Sopenharmony_ci	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
69562306a36Sopenharmony_ci	    (ret == DATAOUT_CANNOT_RECOVER))
69662306a36Sopenharmony_ci		return ret;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	if (cmd->unsolicited_data) {
69962306a36Sopenharmony_ci		ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf);
70062306a36Sopenharmony_ci		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
70162306a36Sopenharmony_ci		    (ret == DATAOUT_CANNOT_RECOVER))
70262306a36Sopenharmony_ci			return ret;
70362306a36Sopenharmony_ci	} else {
70462306a36Sopenharmony_ci		ret = iscsit_dataout_check_sequence(cmd, buf);
70562306a36Sopenharmony_ci		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
70662306a36Sopenharmony_ci		    (ret == DATAOUT_CANNOT_RECOVER))
70762306a36Sopenharmony_ci			return ret;
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	return (conn->sess->sess_ops->DataPDUInOrder) ?
71162306a36Sopenharmony_ci		iscsit_dataout_pre_datapduinorder_yes(cmd, buf) :
71262306a36Sopenharmony_ci		iscsit_dataout_pre_datapduinorder_no(cmd, buf);
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci/*
71662306a36Sopenharmony_ci *	Called from iscsit_handle_data_out() after DataOUT Payload is received
71762306a36Sopenharmony_ci *	and CRC computed.
71862306a36Sopenharmony_ci */
71962306a36Sopenharmony_ciint iscsit_check_post_dataout(
72062306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
72162306a36Sopenharmony_ci	unsigned char *buf,
72262306a36Sopenharmony_ci	u8 data_crc_failed)
72362306a36Sopenharmony_ci{
72462306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci	cmd->dataout_timeout_retries = 0;
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (!data_crc_failed)
72962306a36Sopenharmony_ci		return iscsit_dataout_post_crc_passed(cmd, buf);
73062306a36Sopenharmony_ci	else {
73162306a36Sopenharmony_ci		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
73262306a36Sopenharmony_ci			pr_err("Unable to recover from DataOUT CRC"
73362306a36Sopenharmony_ci				" failure while ERL=0, closing session.\n");
73462306a36Sopenharmony_ci			iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR,
73562306a36Sopenharmony_ci					  buf);
73662306a36Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
73762306a36Sopenharmony_ci		}
73862306a36Sopenharmony_ci
73962306a36Sopenharmony_ci		iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf);
74062306a36Sopenharmony_ci		return iscsit_dataout_post_crc_failed(cmd, buf);
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_civoid iscsit_handle_time2retain_timeout(struct timer_list *t)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	struct iscsit_session *sess = from_timer(sess, t, time2retain_timer);
74762306a36Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
74862306a36Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
75162306a36Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_STOP) {
75262306a36Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
75362306a36Sopenharmony_ci		return;
75462306a36Sopenharmony_ci	}
75562306a36Sopenharmony_ci	if (atomic_read(&sess->session_reinstatement)) {
75662306a36Sopenharmony_ci		pr_err("Exiting Time2Retain handler because"
75762306a36Sopenharmony_ci				" session_reinstatement=1\n");
75862306a36Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
75962306a36Sopenharmony_ci		return;
76062306a36Sopenharmony_ci	}
76162306a36Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	pr_err("Time2Retain timer expired for SID: %u, cleaning up"
76462306a36Sopenharmony_ci			" iSCSI session.\n", sess->sid);
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	iscsit_fill_cxn_timeout_err_stats(sess);
76762306a36Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
76862306a36Sopenharmony_ci	iscsit_close_session(sess, false);
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_civoid iscsit_start_time2retain_handler(struct iscsit_session *sess)
77262306a36Sopenharmony_ci{
77362306a36Sopenharmony_ci	int tpg_active;
77462306a36Sopenharmony_ci	/*
77562306a36Sopenharmony_ci	 * Only start Time2Retain timer when the associated TPG is still in
77662306a36Sopenharmony_ci	 * an ACTIVE (eg: not disabled or shutdown) state.
77762306a36Sopenharmony_ci	 */
77862306a36Sopenharmony_ci	spin_lock(&sess->tpg->tpg_state_lock);
77962306a36Sopenharmony_ci	tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE);
78062306a36Sopenharmony_ci	spin_unlock(&sess->tpg->tpg_state_lock);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (!tpg_active)
78362306a36Sopenharmony_ci		return;
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING)
78662306a36Sopenharmony_ci		return;
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	pr_debug("Starting Time2Retain timer for %u seconds on"
78962306a36Sopenharmony_ci		" SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid);
79062306a36Sopenharmony_ci
79162306a36Sopenharmony_ci	sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
79262306a36Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
79362306a36Sopenharmony_ci	mod_timer(&sess->time2retain_timer,
79462306a36Sopenharmony_ci		  jiffies + sess->sess_ops->DefaultTime2Retain * HZ);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciint iscsit_stop_time2retain_timer(struct iscsit_session *sess)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
80062306a36Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci	lockdep_assert_held(&se_tpg->session_lock);
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
80562306a36Sopenharmony_ci		return -1;
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING))
80862306a36Sopenharmony_ci		return 0;
80962306a36Sopenharmony_ci
81062306a36Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_STOP;
81162306a36Sopenharmony_ci	spin_unlock(&se_tpg->session_lock);
81262306a36Sopenharmony_ci
81362306a36Sopenharmony_ci	del_timer_sync(&sess->time2retain_timer);
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci	spin_lock(&se_tpg->session_lock);
81662306a36Sopenharmony_ci	sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
81762306a36Sopenharmony_ci	pr_debug("Stopped Time2Retain Timer for SID: %u\n",
81862306a36Sopenharmony_ci			sess->sid);
81962306a36Sopenharmony_ci	return 0;
82062306a36Sopenharmony_ci}
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_civoid iscsit_connection_reinstatement_rcfr(struct iscsit_conn *conn)
82362306a36Sopenharmony_ci{
82462306a36Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
82562306a36Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
82662306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
82762306a36Sopenharmony_ci		goto sleep;
82862306a36Sopenharmony_ci	}
82962306a36Sopenharmony_ci
83062306a36Sopenharmony_ci	if (atomic_read(&conn->transport_failed)) {
83162306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
83262306a36Sopenharmony_ci		goto sleep;
83362306a36Sopenharmony_ci	}
83462306a36Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	if (conn->tx_thread && conn->tx_thread_active)
83762306a36Sopenharmony_ci		send_sig(SIGINT, conn->tx_thread, 1);
83862306a36Sopenharmony_ci	if (conn->rx_thread && conn->rx_thread_active)
83962306a36Sopenharmony_ci		send_sig(SIGINT, conn->rx_thread, 1);
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_cisleep:
84262306a36Sopenharmony_ci	wait_for_completion(&conn->conn_wait_rcfr_comp);
84362306a36Sopenharmony_ci	complete(&conn->conn_post_wait_comp);
84462306a36Sopenharmony_ci}
84562306a36Sopenharmony_ci
84662306a36Sopenharmony_civoid iscsit_cause_connection_reinstatement(struct iscsit_conn *conn, int sleep)
84762306a36Sopenharmony_ci{
84862306a36Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
84962306a36Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
85062306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
85162306a36Sopenharmony_ci		return;
85262306a36Sopenharmony_ci	}
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	if (atomic_read(&conn->transport_failed)) {
85562306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
85662306a36Sopenharmony_ci		return;
85762306a36Sopenharmony_ci	}
85862306a36Sopenharmony_ci
85962306a36Sopenharmony_ci	if (atomic_read(&conn->connection_reinstatement)) {
86062306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
86162306a36Sopenharmony_ci		return;
86262306a36Sopenharmony_ci	}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_ci	if (conn->tx_thread && conn->tx_thread_active)
86562306a36Sopenharmony_ci		send_sig(SIGINT, conn->tx_thread, 1);
86662306a36Sopenharmony_ci	if (conn->rx_thread && conn->rx_thread_active)
86762306a36Sopenharmony_ci		send_sig(SIGINT, conn->rx_thread, 1);
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	atomic_set(&conn->connection_reinstatement, 1);
87062306a36Sopenharmony_ci	if (!sleep) {
87162306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
87262306a36Sopenharmony_ci		return;
87362306a36Sopenharmony_ci	}
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	atomic_set(&conn->sleep_on_conn_wait_comp, 1);
87662306a36Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	wait_for_completion(&conn->conn_wait_comp);
87962306a36Sopenharmony_ci	complete(&conn->conn_post_wait_comp);
88062306a36Sopenharmony_ci}
88162306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_cause_connection_reinstatement);
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_civoid iscsit_fall_back_to_erl0(struct iscsit_session *sess)
88462306a36Sopenharmony_ci{
88562306a36Sopenharmony_ci	pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:"
88662306a36Sopenharmony_ci			" %u\n", sess->sid);
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	atomic_set(&sess->session_fall_back_to_erl0, 1);
88962306a36Sopenharmony_ci}
89062306a36Sopenharmony_ci
89162306a36Sopenharmony_cistatic void iscsit_handle_connection_cleanup(struct iscsit_conn *conn)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	if ((sess->sess_ops->ErrorRecoveryLevel == 2) &&
89662306a36Sopenharmony_ci	    !atomic_read(&sess->session_reinstatement) &&
89762306a36Sopenharmony_ci	    !atomic_read(&sess->session_fall_back_to_erl0))
89862306a36Sopenharmony_ci		iscsit_connection_recovery_transport_reset(conn);
89962306a36Sopenharmony_ci	else {
90062306a36Sopenharmony_ci		pr_debug("Performing cleanup for failed iSCSI"
90162306a36Sopenharmony_ci			" Connection ID: %hu from %s\n", conn->cid,
90262306a36Sopenharmony_ci			sess->sess_ops->InitiatorName);
90362306a36Sopenharmony_ci		iscsit_close_connection(conn);
90462306a36Sopenharmony_ci	}
90562306a36Sopenharmony_ci}
90662306a36Sopenharmony_ci
90762306a36Sopenharmony_civoid iscsit_take_action_for_connection_exit(struct iscsit_conn *conn, bool *conn_freed)
90862306a36Sopenharmony_ci{
90962306a36Sopenharmony_ci	*conn_freed = false;
91062306a36Sopenharmony_ci
91162306a36Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
91262306a36Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
91362306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
91462306a36Sopenharmony_ci		return;
91562306a36Sopenharmony_ci	}
91662306a36Sopenharmony_ci	atomic_set(&conn->connection_exit, 1);
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
91962306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
92062306a36Sopenharmony_ci		iscsit_close_connection(conn);
92162306a36Sopenharmony_ci		*conn_freed = true;
92262306a36Sopenharmony_ci		return;
92362306a36Sopenharmony_ci	}
92462306a36Sopenharmony_ci
92562306a36Sopenharmony_ci	if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) {
92662306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
92762306a36Sopenharmony_ci		return;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
93162306a36Sopenharmony_ci	conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
93262306a36Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	iscsit_handle_connection_cleanup(conn);
93562306a36Sopenharmony_ci	*conn_freed = true;
93662306a36Sopenharmony_ci}
937