18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/******************************************************************************
38c2ecf20Sopenharmony_ci * This file contains error recovery level zero functions used by
48c2ecf20Sopenharmony_ci * the iSCSI Target driver.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci ******************************************************************************/
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h>
158c2ecf20Sopenharmony_ci#include <target/target_core_base.h>
168c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
198c2ecf20Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h"
208c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h"
218c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h"
228c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h"
238c2ecf20Sopenharmony_ci#include "iscsi_target_util.h"
248c2ecf20Sopenharmony_ci#include "iscsi_target.h"
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci/*
278c2ecf20Sopenharmony_ci *	Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence()
288c2ecf20Sopenharmony_ci *	checks against to determine a PDU's Offset+Length is within the current
298c2ecf20Sopenharmony_ci *	DataOUT Sequence.  Used for DataSequenceInOrder=Yes only.
308c2ecf20Sopenharmony_ci */
318c2ecf20Sopenharmony_civoid iscsit_set_dataout_sequence_values(
328c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
358c2ecf20Sopenharmony_ci	/*
368c2ecf20Sopenharmony_ci	 * Still set seq_start_offset and seq_end_offset for Unsolicited
378c2ecf20Sopenharmony_ci	 * DataOUT, even if DataSequenceInOrder=No.
388c2ecf20Sopenharmony_ci	 */
398c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data) {
408c2ecf20Sopenharmony_ci		cmd->seq_start_offset = cmd->write_data_done;
418c2ecf20Sopenharmony_ci		cmd->seq_end_offset = min(cmd->se_cmd.data_length,
428c2ecf20Sopenharmony_ci					conn->sess->sess_ops->FirstBurstLength);
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	if (!conn->sess->sess_ops->DataSequenceInOrder)
478c2ecf20Sopenharmony_ci		return;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (!cmd->seq_start_offset && !cmd->seq_end_offset) {
508c2ecf20Sopenharmony_ci		cmd->seq_start_offset = cmd->write_data_done;
518c2ecf20Sopenharmony_ci		cmd->seq_end_offset = (cmd->se_cmd.data_length >
528c2ecf20Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) ?
538c2ecf20Sopenharmony_ci			(cmd->write_data_done +
548c2ecf20Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) : cmd->se_cmd.data_length;
558c2ecf20Sopenharmony_ci	} else {
568c2ecf20Sopenharmony_ci		cmd->seq_start_offset = cmd->seq_end_offset;
578c2ecf20Sopenharmony_ci		cmd->seq_end_offset = ((cmd->seq_end_offset +
588c2ecf20Sopenharmony_ci			conn->sess->sess_ops->MaxBurstLength) >=
598c2ecf20Sopenharmony_ci			cmd->se_cmd.data_length) ? cmd->se_cmd.data_length :
608c2ecf20Sopenharmony_ci			(cmd->seq_end_offset +
618c2ecf20Sopenharmony_ci			 conn->sess->sess_ops->MaxBurstLength);
628c2ecf20Sopenharmony_ci	}
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int iscsit_dataout_within_command_recovery_check(
668c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
678c2ecf20Sopenharmony_ci	unsigned char *buf)
688c2ecf20Sopenharmony_ci{
698c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
708c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
718c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	/*
748c2ecf20Sopenharmony_ci	 * We do the within-command recovery checks here as it is
758c2ecf20Sopenharmony_ci	 * the first function called in iscsi_check_pre_dataout().
768c2ecf20Sopenharmony_ci	 * Basically, if we are in within-command recovery and
778c2ecf20Sopenharmony_ci	 * the PDU does not contain the offset the sequence needs,
788c2ecf20Sopenharmony_ci	 * dump the payload.
798c2ecf20Sopenharmony_ci	 *
808c2ecf20Sopenharmony_ci	 * This only applies to DataPDUInOrder=Yes, for
818c2ecf20Sopenharmony_ci	 * DataPDUInOrder=No we only re-request the failed PDU
828c2ecf20Sopenharmony_ci	 * and check that all PDUs in a sequence are received
838c2ecf20Sopenharmony_ci	 * upon end of sequence.
848c2ecf20Sopenharmony_ci	 */
858c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
868c2ecf20Sopenharmony_ci		if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) &&
878c2ecf20Sopenharmony_ci		    cmd->write_data_done != be32_to_cpu(hdr->offset))
888c2ecf20Sopenharmony_ci			goto dump;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci		cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY;
918c2ecf20Sopenharmony_ci	} else {
928c2ecf20Sopenharmony_ci		struct iscsi_seq *seq;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
958c2ecf20Sopenharmony_ci					    payload_length);
968c2ecf20Sopenharmony_ci		if (!seq)
978c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
988c2ecf20Sopenharmony_ci		/*
998c2ecf20Sopenharmony_ci		 * Set the struct iscsi_seq pointer to reuse later.
1008c2ecf20Sopenharmony_ci		 */
1018c2ecf20Sopenharmony_ci		cmd->seq_ptr = seq;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataPDUInOrder) {
1048c2ecf20Sopenharmony_ci			if (seq->status ==
1058c2ecf20Sopenharmony_ci			    DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
1068c2ecf20Sopenharmony_ci			   (seq->offset != be32_to_cpu(hdr->offset) ||
1078c2ecf20Sopenharmony_ci			    seq->data_sn != be32_to_cpu(hdr->datasn)))
1088c2ecf20Sopenharmony_ci				goto dump;
1098c2ecf20Sopenharmony_ci		} else {
1108c2ecf20Sopenharmony_ci			if (seq->status ==
1118c2ecf20Sopenharmony_ci			     DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
1128c2ecf20Sopenharmony_ci			    seq->data_sn != be32_to_cpu(hdr->datasn))
1138c2ecf20Sopenharmony_ci				goto dump;
1148c2ecf20Sopenharmony_ci		}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		if (seq->status == DATAOUT_SEQUENCE_COMPLETE)
1178c2ecf20Sopenharmony_ci			goto dump;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		if (seq->status != DATAOUT_SEQUENCE_COMPLETE)
1208c2ecf20Sopenharmony_ci			seq->status = 0;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cidump:
1268c2ecf20Sopenharmony_ci	pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:"
1278c2ecf20Sopenharmony_ci		" 0x%08x\n", hdr->offset, payload_length, hdr->datasn);
1288c2ecf20Sopenharmony_ci	return iscsit_dump_data_payload(conn, payload_length, 1);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_unsolicited_sequence(
1328c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
1338c2ecf20Sopenharmony_ci	unsigned char *buf)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	u32 first_burst_len;
1368c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
1378c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
1388c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
1428c2ecf20Sopenharmony_ci	   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
1438c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x with Offset: %u,"
1448c2ecf20Sopenharmony_ci		" Length: %u outside of Unsolicited Sequence %u:%u while"
1458c2ecf20Sopenharmony_ci		" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
1468c2ecf20Sopenharmony_ci		be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
1478c2ecf20Sopenharmony_ci			cmd->seq_end_offset);
1488c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	first_burst_len = (cmd->first_burst_len + payload_length);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) {
1548c2ecf20Sopenharmony_ci		pr_err("Total %u bytes exceeds FirstBurstLength: %u"
1558c2ecf20Sopenharmony_ci			" for this Unsolicited DataOut Burst.\n",
1568c2ecf20Sopenharmony_ci			first_burst_len, conn->sess->sess_ops->FirstBurstLength);
1578c2ecf20Sopenharmony_ci		transport_send_check_condition_and_sense(&cmd->se_cmd,
1588c2ecf20Sopenharmony_ci				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
1598c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
1608c2ecf20Sopenharmony_ci	}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	/*
1638c2ecf20Sopenharmony_ci	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
1648c2ecf20Sopenharmony_ci	 * checks for the current Unsolicited DataOUT Sequence.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
1678c2ecf20Sopenharmony_ci		/*
1688c2ecf20Sopenharmony_ci		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
1698c2ecf20Sopenharmony_ci		 * sequence checks are handled in
1708c2ecf20Sopenharmony_ci		 * iscsit_dataout_datapduinorder_no_fbit().
1718c2ecf20Sopenharmony_ci		 */
1728c2ecf20Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder)
1738c2ecf20Sopenharmony_ci			goto out;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci		if ((first_burst_len != cmd->se_cmd.data_length) &&
1768c2ecf20Sopenharmony_ci		    (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) {
1778c2ecf20Sopenharmony_ci			pr_err("Unsolicited non-immediate data"
1788c2ecf20Sopenharmony_ci			" received %u does not equal FirstBurstLength: %u, and"
1798c2ecf20Sopenharmony_ci			" does not equal ExpXferLen %u.\n", first_burst_len,
1808c2ecf20Sopenharmony_ci				conn->sess->sess_ops->FirstBurstLength,
1818c2ecf20Sopenharmony_ci				cmd->se_cmd.data_length);
1828c2ecf20Sopenharmony_ci			transport_send_check_condition_and_sense(&cmd->se_cmd,
1838c2ecf20Sopenharmony_ci					TCM_INCORRECT_AMOUNT_OF_DATA, 0);
1848c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
1858c2ecf20Sopenharmony_ci		}
1868c2ecf20Sopenharmony_ci	} else {
1878c2ecf20Sopenharmony_ci		if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) {
1888c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x reached"
1898c2ecf20Sopenharmony_ci			" FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
1908c2ecf20Sopenharmony_ci				" error.\n", cmd->init_task_tag,
1918c2ecf20Sopenharmony_ci				conn->sess->sess_ops->FirstBurstLength);
1928c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
1938c2ecf20Sopenharmony_ci		}
1948c2ecf20Sopenharmony_ci		if (first_burst_len == cmd->se_cmd.data_length) {
1958c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x reached"
1968c2ecf20Sopenharmony_ci			" ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
1978c2ecf20Sopenharmony_ci			" error.\n", cmd->init_task_tag, cmd->se_cmd.data_length);
1988c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
1998c2ecf20Sopenharmony_ci		}
2008c2ecf20Sopenharmony_ci	}
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ciout:
2038c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_sequence(
2078c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
2088c2ecf20Sopenharmony_ci	unsigned char *buf)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	u32 next_burst_len;
2118c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
2128c2ecf20Sopenharmony_ci	struct iscsi_seq *seq = NULL;
2138c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
2148c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	/*
2178c2ecf20Sopenharmony_ci	 * For DataSequenceInOrder=Yes: Check that the offset and offset+length
2188c2ecf20Sopenharmony_ci	 * is within range as defined by iscsi_set_dataout_sequence_values().
2198c2ecf20Sopenharmony_ci	 *
2208c2ecf20Sopenharmony_ci	 * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for
2218c2ecf20Sopenharmony_ci	 * offset+length tuple.
2228c2ecf20Sopenharmony_ci	 */
2238c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
2248c2ecf20Sopenharmony_ci		/*
2258c2ecf20Sopenharmony_ci		 * Due to possibility of recovery DataOUT sent by the initiator
2268c2ecf20Sopenharmony_ci		 * fullfilling an Recovery R2T, it's best to just dump the
2278c2ecf20Sopenharmony_ci		 * payload here, instead of erroring out.
2288c2ecf20Sopenharmony_ci		 */
2298c2ecf20Sopenharmony_ci		if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
2308c2ecf20Sopenharmony_ci		   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
2318c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x with Offset: %u,"
2328c2ecf20Sopenharmony_ci			" Length: %u outside of Sequence %u:%u while"
2338c2ecf20Sopenharmony_ci			" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
2348c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
2358c2ecf20Sopenharmony_ci				cmd->seq_end_offset);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
2388c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
2398c2ecf20Sopenharmony_ci			return DATAOUT_WITHIN_COMMAND_RECOVERY;
2408c2ecf20Sopenharmony_ci		}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci		next_burst_len = (cmd->next_burst_len + payload_length);
2438c2ecf20Sopenharmony_ci	} else {
2448c2ecf20Sopenharmony_ci		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
2458c2ecf20Sopenharmony_ci					    payload_length);
2468c2ecf20Sopenharmony_ci		if (!seq)
2478c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
2488c2ecf20Sopenharmony_ci		/*
2498c2ecf20Sopenharmony_ci		 * Set the struct iscsi_seq pointer to reuse later.
2508c2ecf20Sopenharmony_ci		 */
2518c2ecf20Sopenharmony_ci		cmd->seq_ptr = seq;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci		if (seq->status == DATAOUT_SEQUENCE_COMPLETE) {
2548c2ecf20Sopenharmony_ci			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
2558c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
2568c2ecf20Sopenharmony_ci			return DATAOUT_WITHIN_COMMAND_RECOVERY;
2578c2ecf20Sopenharmony_ci		}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci		next_burst_len = (seq->next_burst_len + payload_length);
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_ci	if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) {
2638c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x, NextBurstLength: %u and"
2648c2ecf20Sopenharmony_ci			" Length: %u exceeds MaxBurstLength: %u. protocol"
2658c2ecf20Sopenharmony_ci			" error.\n", cmd->init_task_tag,
2668c2ecf20Sopenharmony_ci			(next_burst_len - payload_length),
2678c2ecf20Sopenharmony_ci			payload_length, conn->sess->sess_ops->MaxBurstLength);
2688c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
2698c2ecf20Sopenharmony_ci	}
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	/*
2728c2ecf20Sopenharmony_ci	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
2738c2ecf20Sopenharmony_ci	 * checks for the current DataOUT Sequence.
2748c2ecf20Sopenharmony_ci	 */
2758c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
2768c2ecf20Sopenharmony_ci		/*
2778c2ecf20Sopenharmony_ci		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
2788c2ecf20Sopenharmony_ci		 * sequence checks are handled in
2798c2ecf20Sopenharmony_ci		 * iscsit_dataout_datapduinorder_no_fbit().
2808c2ecf20Sopenharmony_ci		 */
2818c2ecf20Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder)
2828c2ecf20Sopenharmony_ci			goto out;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
2858c2ecf20Sopenharmony_ci			if ((next_burst_len <
2868c2ecf20Sopenharmony_ci			     conn->sess->sess_ops->MaxBurstLength) &&
2878c2ecf20Sopenharmony_ci			   ((cmd->write_data_done + payload_length) <
2888c2ecf20Sopenharmony_ci			     cmd->se_cmd.data_length)) {
2898c2ecf20Sopenharmony_ci				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
2908c2ecf20Sopenharmony_ci				" before end of DataOUT sequence, protocol"
2918c2ecf20Sopenharmony_ci				" error.\n", cmd->init_task_tag);
2928c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
2938c2ecf20Sopenharmony_ci			}
2948c2ecf20Sopenharmony_ci		} else {
2958c2ecf20Sopenharmony_ci			if (next_burst_len < seq->xfer_len) {
2968c2ecf20Sopenharmony_ci				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
2978c2ecf20Sopenharmony_ci				" before end of DataOUT sequence, protocol"
2988c2ecf20Sopenharmony_ci				" error.\n", cmd->init_task_tag);
2998c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
3008c2ecf20Sopenharmony_ci			}
3018c2ecf20Sopenharmony_ci		}
3028c2ecf20Sopenharmony_ci	} else {
3038c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
3048c2ecf20Sopenharmony_ci			if (next_burst_len ==
3058c2ecf20Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength) {
3068c2ecf20Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
3078c2ecf20Sopenharmony_ci				" MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is"
3088c2ecf20Sopenharmony_ci				" not set, protocol error.", cmd->init_task_tag,
3098c2ecf20Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength);
3108c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
3118c2ecf20Sopenharmony_ci			}
3128c2ecf20Sopenharmony_ci			if ((cmd->write_data_done + payload_length) ==
3138c2ecf20Sopenharmony_ci					cmd->se_cmd.data_length) {
3148c2ecf20Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
3158c2ecf20Sopenharmony_ci				" last DataOUT PDU in sequence but ISCSI_FLAG_"
3168c2ecf20Sopenharmony_ci				"CMD_FINAL is not set, protocol error.\n",
3178c2ecf20Sopenharmony_ci					cmd->init_task_tag);
3188c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
3198c2ecf20Sopenharmony_ci			}
3208c2ecf20Sopenharmony_ci		} else {
3218c2ecf20Sopenharmony_ci			if (next_burst_len == seq->xfer_len) {
3228c2ecf20Sopenharmony_ci				pr_err("Command ITT: 0x%08x reached"
3238c2ecf20Sopenharmony_ci				" last DataOUT PDU in sequence but ISCSI_FLAG_"
3248c2ecf20Sopenharmony_ci				"CMD_FINAL is not set, protocol error.\n",
3258c2ecf20Sopenharmony_ci					cmd->init_task_tag);
3268c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
3278c2ecf20Sopenharmony_ci			}
3288c2ecf20Sopenharmony_ci		}
3298c2ecf20Sopenharmony_ci	}
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ciout:
3328c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int iscsit_dataout_check_datasn(
3368c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
3378c2ecf20Sopenharmony_ci	unsigned char *buf)
3388c2ecf20Sopenharmony_ci{
3398c2ecf20Sopenharmony_ci	u32 data_sn = 0;
3408c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
3418c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
3428c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/*
3458c2ecf20Sopenharmony_ci	 * Considering the target has no method of re-requesting DataOUT
3468c2ecf20Sopenharmony_ci	 * by DataSN, if we receieve a greater DataSN than expected we
3478c2ecf20Sopenharmony_ci	 * assume the functions for DataPDUInOrder=[Yes,No] below will
3488c2ecf20Sopenharmony_ci	 * handle it.
3498c2ecf20Sopenharmony_ci	 *
3508c2ecf20Sopenharmony_ci	 * If the DataSN is less than expected, dump the payload.
3518c2ecf20Sopenharmony_ci	 */
3528c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder)
3538c2ecf20Sopenharmony_ci		data_sn = cmd->data_sn;
3548c2ecf20Sopenharmony_ci	else {
3558c2ecf20Sopenharmony_ci		struct iscsi_seq *seq = cmd->seq_ptr;
3568c2ecf20Sopenharmony_ci		data_sn = seq->data_sn;
3578c2ecf20Sopenharmony_ci	}
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	if (be32_to_cpu(hdr->datasn) > data_sn) {
3608c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
3618c2ecf20Sopenharmony_ci			" higher than expected 0x%08x.\n", cmd->init_task_tag,
3628c2ecf20Sopenharmony_ci				be32_to_cpu(hdr->datasn), data_sn);
3638c2ecf20Sopenharmony_ci		goto recover;
3648c2ecf20Sopenharmony_ci	} else if (be32_to_cpu(hdr->datasn) < data_sn) {
3658c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
3668c2ecf20Sopenharmony_ci			" lower than expected 0x%08x, discarding payload.\n",
3678c2ecf20Sopenharmony_ci			cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn);
3688c2ecf20Sopenharmony_ci		goto dump;
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cirecover:
3748c2ecf20Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
3758c2ecf20Sopenharmony_ci		pr_err("Unable to perform within-command recovery"
3768c2ecf20Sopenharmony_ci				" while ERL=0.\n");
3778c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
3788c2ecf20Sopenharmony_ci	}
3798c2ecf20Sopenharmony_cidump:
3808c2ecf20Sopenharmony_ci	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
3818c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	return DATAOUT_WITHIN_COMMAND_RECOVERY;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_yes(
3878c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
3888c2ecf20Sopenharmony_ci	unsigned char *buf)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int dump = 0, recovery = 0;
3918c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
3928c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
3938c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	/*
3968c2ecf20Sopenharmony_ci	 * For DataSequenceInOrder=Yes: If the offset is greater than the global
3978c2ecf20Sopenharmony_ci	 * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has
3988c2ecf20Sopenharmony_ci	 * occurred and fail the connection.
3998c2ecf20Sopenharmony_ci	 *
4008c2ecf20Sopenharmony_ci	 * For DataSequenceInOrder=No: If the offset is greater than the per
4018c2ecf20Sopenharmony_ci	 * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
4028c2ecf20Sopenharmony_ci	 * error has occurred and fail the connection.
4038c2ecf20Sopenharmony_ci	 */
4048c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder) {
4058c2ecf20Sopenharmony_ci		if (be32_to_cpu(hdr->offset) != cmd->write_data_done) {
4068c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
4078c2ecf20Sopenharmony_ci			" %u different than expected %u.\n", cmd->init_task_tag,
4088c2ecf20Sopenharmony_ci				be32_to_cpu(hdr->offset), cmd->write_data_done);
4098c2ecf20Sopenharmony_ci			recovery = 1;
4108c2ecf20Sopenharmony_ci			goto recover;
4118c2ecf20Sopenharmony_ci		}
4128c2ecf20Sopenharmony_ci	} else {
4138c2ecf20Sopenharmony_ci		struct iscsi_seq *seq = cmd->seq_ptr;
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci		if (be32_to_cpu(hdr->offset) > seq->offset) {
4168c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
4178c2ecf20Sopenharmony_ci			" %u greater than expected %u.\n", cmd->init_task_tag,
4188c2ecf20Sopenharmony_ci				be32_to_cpu(hdr->offset), seq->offset);
4198c2ecf20Sopenharmony_ci			recovery = 1;
4208c2ecf20Sopenharmony_ci			goto recover;
4218c2ecf20Sopenharmony_ci		} else if (be32_to_cpu(hdr->offset) < seq->offset) {
4228c2ecf20Sopenharmony_ci			pr_err("Command ITT: 0x%08x, received offset"
4238c2ecf20Sopenharmony_ci			" %u less than expected %u, discarding payload.\n",
4248c2ecf20Sopenharmony_ci				cmd->init_task_tag, be32_to_cpu(hdr->offset),
4258c2ecf20Sopenharmony_ci				seq->offset);
4268c2ecf20Sopenharmony_ci			dump = 1;
4278c2ecf20Sopenharmony_ci			goto dump;
4288c2ecf20Sopenharmony_ci		}
4298c2ecf20Sopenharmony_ci	}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cirecover:
4348c2ecf20Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
4358c2ecf20Sopenharmony_ci		pr_err("Unable to perform within-command recovery"
4368c2ecf20Sopenharmony_ci				" while ERL=0.\n");
4378c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_cidump:
4408c2ecf20Sopenharmony_ci	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
4418c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return (recovery) ? iscsit_recover_dataout_sequence(cmd,
4448c2ecf20Sopenharmony_ci		be32_to_cpu(hdr->offset), payload_length) :
4458c2ecf20Sopenharmony_ci	       (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic int iscsit_dataout_pre_datapduinorder_no(
4498c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
4508c2ecf20Sopenharmony_ci	unsigned char *buf)
4518c2ecf20Sopenharmony_ci{
4528c2ecf20Sopenharmony_ci	struct iscsi_pdu *pdu;
4538c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
4548c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	pdu = iscsit_get_pdu_holder(cmd, be32_to_cpu(hdr->offset),
4578c2ecf20Sopenharmony_ci				    payload_length);
4588c2ecf20Sopenharmony_ci	if (!pdu)
4598c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	cmd->pdu_ptr = pdu;
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	switch (pdu->status) {
4648c2ecf20Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
4658c2ecf20Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
4668c2ecf20Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
4678c2ecf20Sopenharmony_ci		break;
4688c2ecf20Sopenharmony_ci	case ISCSI_PDU_RECEIVED_OK:
4698c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x received already gotten"
4708c2ecf20Sopenharmony_ci			" Offset: %u, Length: %u\n", cmd->init_task_tag,
4718c2ecf20Sopenharmony_ci				be32_to_cpu(hdr->offset), payload_length);
4728c2ecf20Sopenharmony_ci		return iscsit_dump_data_payload(cmd->conn, payload_length, 1);
4738c2ecf20Sopenharmony_ci	default:
4748c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct iscsi_r2t *r2t;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data)
4858c2ecf20Sopenharmony_ci		return 0;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	r2t = iscsit_get_r2t_for_eos(cmd, offset, length);
4888c2ecf20Sopenharmony_ci	if (!r2t)
4898c2ecf20Sopenharmony_ci		return -1;
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
4928c2ecf20Sopenharmony_ci	r2t->seq_complete = 1;
4938c2ecf20Sopenharmony_ci	cmd->outstanding_r2ts--;
4948c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return 0;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_cistatic int iscsit_dataout_update_datapduinorder_no(
5008c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
5018c2ecf20Sopenharmony_ci	u32 data_sn,
5028c2ecf20Sopenharmony_ci	int f_bit)
5038c2ecf20Sopenharmony_ci{
5048c2ecf20Sopenharmony_ci	int ret = 0;
5058c2ecf20Sopenharmony_ci	struct iscsi_pdu *pdu = cmd->pdu_ptr;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	pdu->data_sn = data_sn;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	switch (pdu->status) {
5108c2ecf20Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
5118c2ecf20Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
5128c2ecf20Sopenharmony_ci		break;
5138c2ecf20Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
5148c2ecf20Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
5158c2ecf20Sopenharmony_ci		break;
5168c2ecf20Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
5178c2ecf20Sopenharmony_ci		pdu->status = ISCSI_PDU_RECEIVED_OK;
5188c2ecf20Sopenharmony_ci		break;
5198c2ecf20Sopenharmony_ci	default:
5208c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	if (f_bit) {
5248c2ecf20Sopenharmony_ci		ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu);
5258c2ecf20Sopenharmony_ci		if (ret == DATAOUT_CANNOT_RECOVER)
5268c2ecf20Sopenharmony_ci			return ret;
5278c2ecf20Sopenharmony_ci	}
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	return DATAOUT_NORMAL;
5308c2ecf20Sopenharmony_ci}
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_cistatic int iscsit_dataout_post_crc_passed(
5338c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
5348c2ecf20Sopenharmony_ci	unsigned char *buf)
5358c2ecf20Sopenharmony_ci{
5368c2ecf20Sopenharmony_ci	int ret, send_r2t = 0;
5378c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
5388c2ecf20Sopenharmony_ci	struct iscsi_seq *seq = NULL;
5398c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
5408c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data) {
5438c2ecf20Sopenharmony_ci		if ((cmd->first_burst_len + payload_length) ==
5448c2ecf20Sopenharmony_ci		     conn->sess->sess_ops->FirstBurstLength) {
5458c2ecf20Sopenharmony_ci			if (iscsit_dataout_update_r2t(cmd, be32_to_cpu(hdr->offset),
5468c2ecf20Sopenharmony_ci					payload_length) < 0)
5478c2ecf20Sopenharmony_ci				return DATAOUT_CANNOT_RECOVER;
5488c2ecf20Sopenharmony_ci			send_r2t = 1;
5498c2ecf20Sopenharmony_ci		}
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci		if (!conn->sess->sess_ops->DataPDUInOrder) {
5528c2ecf20Sopenharmony_ci			ret = iscsit_dataout_update_datapduinorder_no(cmd,
5538c2ecf20Sopenharmony_ci				be32_to_cpu(hdr->datasn),
5548c2ecf20Sopenharmony_ci				(hdr->flags & ISCSI_FLAG_CMD_FINAL));
5558c2ecf20Sopenharmony_ci			if (ret == DATAOUT_CANNOT_RECOVER)
5568c2ecf20Sopenharmony_ci				return ret;
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci		cmd->first_burst_len += payload_length;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder)
5628c2ecf20Sopenharmony_ci			cmd->data_sn++;
5638c2ecf20Sopenharmony_ci		else {
5648c2ecf20Sopenharmony_ci			seq = cmd->seq_ptr;
5658c2ecf20Sopenharmony_ci			seq->data_sn++;
5668c2ecf20Sopenharmony_ci			seq->offset += payload_length;
5678c2ecf20Sopenharmony_ci		}
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		if (send_r2t) {
5708c2ecf20Sopenharmony_ci			if (seq)
5718c2ecf20Sopenharmony_ci				seq->status = DATAOUT_SEQUENCE_COMPLETE;
5728c2ecf20Sopenharmony_ci			cmd->first_burst_len = 0;
5738c2ecf20Sopenharmony_ci			cmd->unsolicited_data = 0;
5748c2ecf20Sopenharmony_ci		}
5758c2ecf20Sopenharmony_ci	} else {
5768c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
5778c2ecf20Sopenharmony_ci			if ((cmd->next_burst_len + payload_length) ==
5788c2ecf20Sopenharmony_ci			     conn->sess->sess_ops->MaxBurstLength) {
5798c2ecf20Sopenharmony_ci				if (iscsit_dataout_update_r2t(cmd,
5808c2ecf20Sopenharmony_ci						be32_to_cpu(hdr->offset),
5818c2ecf20Sopenharmony_ci						payload_length) < 0)
5828c2ecf20Sopenharmony_ci					return DATAOUT_CANNOT_RECOVER;
5838c2ecf20Sopenharmony_ci				send_r2t = 1;
5848c2ecf20Sopenharmony_ci			}
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci			if (!conn->sess->sess_ops->DataPDUInOrder) {
5878c2ecf20Sopenharmony_ci				ret = iscsit_dataout_update_datapduinorder_no(
5888c2ecf20Sopenharmony_ci						cmd, be32_to_cpu(hdr->datasn),
5898c2ecf20Sopenharmony_ci						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
5908c2ecf20Sopenharmony_ci				if (ret == DATAOUT_CANNOT_RECOVER)
5918c2ecf20Sopenharmony_ci					return ret;
5928c2ecf20Sopenharmony_ci			}
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci			cmd->next_burst_len += payload_length;
5958c2ecf20Sopenharmony_ci			cmd->data_sn++;
5968c2ecf20Sopenharmony_ci
5978c2ecf20Sopenharmony_ci			if (send_r2t)
5988c2ecf20Sopenharmony_ci				cmd->next_burst_len = 0;
5998c2ecf20Sopenharmony_ci		} else {
6008c2ecf20Sopenharmony_ci			seq = cmd->seq_ptr;
6018c2ecf20Sopenharmony_ci
6028c2ecf20Sopenharmony_ci			if ((seq->next_burst_len + payload_length) ==
6038c2ecf20Sopenharmony_ci			     seq->xfer_len) {
6048c2ecf20Sopenharmony_ci				if (iscsit_dataout_update_r2t(cmd,
6058c2ecf20Sopenharmony_ci						be32_to_cpu(hdr->offset),
6068c2ecf20Sopenharmony_ci						payload_length) < 0)
6078c2ecf20Sopenharmony_ci					return DATAOUT_CANNOT_RECOVER;
6088c2ecf20Sopenharmony_ci				send_r2t = 1;
6098c2ecf20Sopenharmony_ci			}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci			if (!conn->sess->sess_ops->DataPDUInOrder) {
6128c2ecf20Sopenharmony_ci				ret = iscsit_dataout_update_datapduinorder_no(
6138c2ecf20Sopenharmony_ci						cmd, be32_to_cpu(hdr->datasn),
6148c2ecf20Sopenharmony_ci						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
6158c2ecf20Sopenharmony_ci				if (ret == DATAOUT_CANNOT_RECOVER)
6168c2ecf20Sopenharmony_ci					return ret;
6178c2ecf20Sopenharmony_ci			}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci			seq->data_sn++;
6208c2ecf20Sopenharmony_ci			seq->offset += payload_length;
6218c2ecf20Sopenharmony_ci			seq->next_burst_len += payload_length;
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci			if (send_r2t) {
6248c2ecf20Sopenharmony_ci				seq->next_burst_len = 0;
6258c2ecf20Sopenharmony_ci				seq->status = DATAOUT_SEQUENCE_COMPLETE;
6268c2ecf20Sopenharmony_ci			}
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci	}
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci	if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder)
6318c2ecf20Sopenharmony_ci		cmd->data_sn = 0;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	cmd->write_data_done += payload_length;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	if (cmd->write_data_done == cmd->se_cmd.data_length)
6368c2ecf20Sopenharmony_ci		return DATAOUT_SEND_TO_TRANSPORT;
6378c2ecf20Sopenharmony_ci	else if (send_r2t)
6388c2ecf20Sopenharmony_ci		return DATAOUT_SEND_R2T;
6398c2ecf20Sopenharmony_ci	else
6408c2ecf20Sopenharmony_ci		return DATAOUT_NORMAL;
6418c2ecf20Sopenharmony_ci}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_cistatic int iscsit_dataout_post_crc_failed(
6448c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
6458c2ecf20Sopenharmony_ci	unsigned char *buf)
6468c2ecf20Sopenharmony_ci{
6478c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
6488c2ecf20Sopenharmony_ci	struct iscsi_pdu *pdu;
6498c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *) buf;
6508c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataPDUInOrder)
6538c2ecf20Sopenharmony_ci		goto recover;
6548c2ecf20Sopenharmony_ci	/*
6558c2ecf20Sopenharmony_ci	 * The rest of this function is only called when DataPDUInOrder=No.
6568c2ecf20Sopenharmony_ci	 */
6578c2ecf20Sopenharmony_ci	pdu = cmd->pdu_ptr;
6588c2ecf20Sopenharmony_ci
6598c2ecf20Sopenharmony_ci	switch (pdu->status) {
6608c2ecf20Sopenharmony_ci	case ISCSI_PDU_NOT_RECEIVED:
6618c2ecf20Sopenharmony_ci		pdu->status = ISCSI_PDU_CRC_FAILED;
6628c2ecf20Sopenharmony_ci		break;
6638c2ecf20Sopenharmony_ci	case ISCSI_PDU_CRC_FAILED:
6648c2ecf20Sopenharmony_ci		break;
6658c2ecf20Sopenharmony_ci	case ISCSI_PDU_TIMED_OUT:
6668c2ecf20Sopenharmony_ci		pdu->status = ISCSI_PDU_CRC_FAILED;
6678c2ecf20Sopenharmony_ci		break;
6688c2ecf20Sopenharmony_ci	default:
6698c2ecf20Sopenharmony_ci		return DATAOUT_CANNOT_RECOVER;
6708c2ecf20Sopenharmony_ci	}
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cirecover:
6738c2ecf20Sopenharmony_ci	return iscsit_recover_dataout_sequence(cmd, be32_to_cpu(hdr->offset),
6748c2ecf20Sopenharmony_ci						payload_length);
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci/*
6788c2ecf20Sopenharmony_ci *	Called from iscsit_handle_data_out() before DataOUT Payload is received
6798c2ecf20Sopenharmony_ci *	and CRC computed.
6808c2ecf20Sopenharmony_ci */
6818c2ecf20Sopenharmony_ciint iscsit_check_pre_dataout(
6828c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
6838c2ecf20Sopenharmony_ci	unsigned char *buf)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	int ret;
6868c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	ret = iscsit_dataout_within_command_recovery_check(cmd, buf);
6898c2ecf20Sopenharmony_ci	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
6908c2ecf20Sopenharmony_ci	    (ret == DATAOUT_CANNOT_RECOVER))
6918c2ecf20Sopenharmony_ci		return ret;
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	ret = iscsit_dataout_check_datasn(cmd, buf);
6948c2ecf20Sopenharmony_ci	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
6958c2ecf20Sopenharmony_ci	    (ret == DATAOUT_CANNOT_RECOVER))
6968c2ecf20Sopenharmony_ci		return ret;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data) {
6998c2ecf20Sopenharmony_ci		ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf);
7008c2ecf20Sopenharmony_ci		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
7018c2ecf20Sopenharmony_ci		    (ret == DATAOUT_CANNOT_RECOVER))
7028c2ecf20Sopenharmony_ci			return ret;
7038c2ecf20Sopenharmony_ci	} else {
7048c2ecf20Sopenharmony_ci		ret = iscsit_dataout_check_sequence(cmd, buf);
7058c2ecf20Sopenharmony_ci		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
7068c2ecf20Sopenharmony_ci		    (ret == DATAOUT_CANNOT_RECOVER))
7078c2ecf20Sopenharmony_ci			return ret;
7088c2ecf20Sopenharmony_ci	}
7098c2ecf20Sopenharmony_ci
7108c2ecf20Sopenharmony_ci	return (conn->sess->sess_ops->DataPDUInOrder) ?
7118c2ecf20Sopenharmony_ci		iscsit_dataout_pre_datapduinorder_yes(cmd, buf) :
7128c2ecf20Sopenharmony_ci		iscsit_dataout_pre_datapduinorder_no(cmd, buf);
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci/*
7168c2ecf20Sopenharmony_ci *	Called from iscsit_handle_data_out() after DataOUT Payload is received
7178c2ecf20Sopenharmony_ci *	and CRC computed.
7188c2ecf20Sopenharmony_ci */
7198c2ecf20Sopenharmony_ciint iscsit_check_post_dataout(
7208c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
7218c2ecf20Sopenharmony_ci	unsigned char *buf,
7228c2ecf20Sopenharmony_ci	u8 data_crc_failed)
7238c2ecf20Sopenharmony_ci{
7248c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
7258c2ecf20Sopenharmony_ci
7268c2ecf20Sopenharmony_ci	cmd->dataout_timeout_retries = 0;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (!data_crc_failed)
7298c2ecf20Sopenharmony_ci		return iscsit_dataout_post_crc_passed(cmd, buf);
7308c2ecf20Sopenharmony_ci	else {
7318c2ecf20Sopenharmony_ci		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
7328c2ecf20Sopenharmony_ci			pr_err("Unable to recover from DataOUT CRC"
7338c2ecf20Sopenharmony_ci				" failure while ERL=0, closing session.\n");
7348c2ecf20Sopenharmony_ci			iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR,
7358c2ecf20Sopenharmony_ci					  buf);
7368c2ecf20Sopenharmony_ci			return DATAOUT_CANNOT_RECOVER;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci
7398c2ecf20Sopenharmony_ci		iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf);
7408c2ecf20Sopenharmony_ci		return iscsit_dataout_post_crc_failed(cmd, buf);
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_civoid iscsit_handle_time2retain_timeout(struct timer_list *t)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	struct iscsi_session *sess = from_timer(sess, t, time2retain_timer);
7478c2ecf20Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
7488c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
7518c2ecf20Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_STOP) {
7528c2ecf20Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
7538c2ecf20Sopenharmony_ci		return;
7548c2ecf20Sopenharmony_ci	}
7558c2ecf20Sopenharmony_ci	if (atomic_read(&sess->session_reinstatement)) {
7568c2ecf20Sopenharmony_ci		pr_err("Exiting Time2Retain handler because"
7578c2ecf20Sopenharmony_ci				" session_reinstatement=1\n");
7588c2ecf20Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
7598c2ecf20Sopenharmony_ci		return;
7608c2ecf20Sopenharmony_ci	}
7618c2ecf20Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED;
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci	pr_err("Time2Retain timer expired for SID: %u, cleaning up"
7648c2ecf20Sopenharmony_ci			" iSCSI session.\n", sess->sid);
7658c2ecf20Sopenharmony_ci
7668c2ecf20Sopenharmony_ci	iscsit_fill_cxn_timeout_err_stats(sess);
7678c2ecf20Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
7688c2ecf20Sopenharmony_ci	iscsit_close_session(sess);
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_civoid iscsit_start_time2retain_handler(struct iscsi_session *sess)
7728c2ecf20Sopenharmony_ci{
7738c2ecf20Sopenharmony_ci	int tpg_active;
7748c2ecf20Sopenharmony_ci	/*
7758c2ecf20Sopenharmony_ci	 * Only start Time2Retain timer when the associated TPG is still in
7768c2ecf20Sopenharmony_ci	 * an ACTIVE (eg: not disabled or shutdown) state.
7778c2ecf20Sopenharmony_ci	 */
7788c2ecf20Sopenharmony_ci	spin_lock(&sess->tpg->tpg_state_lock);
7798c2ecf20Sopenharmony_ci	tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE);
7808c2ecf20Sopenharmony_ci	spin_unlock(&sess->tpg->tpg_state_lock);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (!tpg_active)
7838c2ecf20Sopenharmony_ci		return;
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING)
7868c2ecf20Sopenharmony_ci		return;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	pr_debug("Starting Time2Retain timer for %u seconds on"
7898c2ecf20Sopenharmony_ci		" SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid);
7908c2ecf20Sopenharmony_ci
7918c2ecf20Sopenharmony_ci	sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
7928c2ecf20Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
7938c2ecf20Sopenharmony_ci	mod_timer(&sess->time2retain_timer,
7948c2ecf20Sopenharmony_ci		  jiffies + sess->sess_ops->DefaultTime2Retain * HZ);
7958c2ecf20Sopenharmony_ci}
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ciint iscsit_stop_time2retain_timer(struct iscsi_session *sess)
7988c2ecf20Sopenharmony_ci{
7998c2ecf20Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
8008c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci	lockdep_assert_held(&se_tpg->session_lock);
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci	if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
8058c2ecf20Sopenharmony_ci		return -1;
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING))
8088c2ecf20Sopenharmony_ci		return 0;
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	sess->time2retain_timer_flags |= ISCSI_TF_STOP;
8118c2ecf20Sopenharmony_ci	spin_unlock(&se_tpg->session_lock);
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci	del_timer_sync(&sess->time2retain_timer);
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci	spin_lock(&se_tpg->session_lock);
8168c2ecf20Sopenharmony_ci	sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
8178c2ecf20Sopenharmony_ci	pr_debug("Stopped Time2Retain Timer for SID: %u\n",
8188c2ecf20Sopenharmony_ci			sess->sid);
8198c2ecf20Sopenharmony_ci	return 0;
8208c2ecf20Sopenharmony_ci}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_civoid iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
8258c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
8268c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8278c2ecf20Sopenharmony_ci		goto sleep;
8288c2ecf20Sopenharmony_ci	}
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (atomic_read(&conn->transport_failed)) {
8318c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8328c2ecf20Sopenharmony_ci		goto sleep;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci	if (conn->tx_thread && conn->tx_thread_active)
8378c2ecf20Sopenharmony_ci		send_sig(SIGINT, conn->tx_thread, 1);
8388c2ecf20Sopenharmony_ci	if (conn->rx_thread && conn->rx_thread_active)
8398c2ecf20Sopenharmony_ci		send_sig(SIGINT, conn->rx_thread, 1);
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_cisleep:
8428c2ecf20Sopenharmony_ci	wait_for_completion(&conn->conn_wait_rcfr_comp);
8438c2ecf20Sopenharmony_ci	complete(&conn->conn_post_wait_comp);
8448c2ecf20Sopenharmony_ci}
8458c2ecf20Sopenharmony_ci
8468c2ecf20Sopenharmony_civoid iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
8498c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
8508c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8518c2ecf20Sopenharmony_ci		return;
8528c2ecf20Sopenharmony_ci	}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci	if (atomic_read(&conn->transport_failed)) {
8558c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8568c2ecf20Sopenharmony_ci		return;
8578c2ecf20Sopenharmony_ci	}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_reinstatement)) {
8608c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8618c2ecf20Sopenharmony_ci		return;
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	if (conn->tx_thread && conn->tx_thread_active)
8658c2ecf20Sopenharmony_ci		send_sig(SIGINT, conn->tx_thread, 1);
8668c2ecf20Sopenharmony_ci	if (conn->rx_thread && conn->rx_thread_active)
8678c2ecf20Sopenharmony_ci		send_sig(SIGINT, conn->rx_thread, 1);
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	atomic_set(&conn->connection_reinstatement, 1);
8708c2ecf20Sopenharmony_ci	if (!sleep) {
8718c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
8728c2ecf20Sopenharmony_ci		return;
8738c2ecf20Sopenharmony_ci	}
8748c2ecf20Sopenharmony_ci
8758c2ecf20Sopenharmony_ci	atomic_set(&conn->sleep_on_conn_wait_comp, 1);
8768c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci	wait_for_completion(&conn->conn_wait_comp);
8798c2ecf20Sopenharmony_ci	complete(&conn->conn_post_wait_comp);
8808c2ecf20Sopenharmony_ci}
8818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_cause_connection_reinstatement);
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_civoid iscsit_fall_back_to_erl0(struct iscsi_session *sess)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:"
8868c2ecf20Sopenharmony_ci			" %u\n", sess->sid);
8878c2ecf20Sopenharmony_ci
8888c2ecf20Sopenharmony_ci	atomic_set(&sess->session_fall_back_to_erl0, 1);
8898c2ecf20Sopenharmony_ci}
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_cistatic void iscsit_handle_connection_cleanup(struct iscsi_conn *conn)
8928c2ecf20Sopenharmony_ci{
8938c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	if ((sess->sess_ops->ErrorRecoveryLevel == 2) &&
8968c2ecf20Sopenharmony_ci	    !atomic_read(&sess->session_reinstatement) &&
8978c2ecf20Sopenharmony_ci	    !atomic_read(&sess->session_fall_back_to_erl0))
8988c2ecf20Sopenharmony_ci		iscsit_connection_recovery_transport_reset(conn);
8998c2ecf20Sopenharmony_ci	else {
9008c2ecf20Sopenharmony_ci		pr_debug("Performing cleanup for failed iSCSI"
9018c2ecf20Sopenharmony_ci			" Connection ID: %hu from %s\n", conn->cid,
9028c2ecf20Sopenharmony_ci			sess->sess_ops->InitiatorName);
9038c2ecf20Sopenharmony_ci		iscsit_close_connection(conn);
9048c2ecf20Sopenharmony_ci	}
9058c2ecf20Sopenharmony_ci}
9068c2ecf20Sopenharmony_ci
9078c2ecf20Sopenharmony_civoid iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool *conn_freed)
9088c2ecf20Sopenharmony_ci{
9098c2ecf20Sopenharmony_ci	*conn_freed = false;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
9128c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_exit)) {
9138c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
9148c2ecf20Sopenharmony_ci		return;
9158c2ecf20Sopenharmony_ci	}
9168c2ecf20Sopenharmony_ci	atomic_set(&conn->connection_exit, 1);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
9198c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
9208c2ecf20Sopenharmony_ci		iscsit_close_connection(conn);
9218c2ecf20Sopenharmony_ci		*conn_freed = true;
9228c2ecf20Sopenharmony_ci		return;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) {
9268c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
9278c2ecf20Sopenharmony_ci		return;
9288c2ecf20Sopenharmony_ci	}
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
9318c2ecf20Sopenharmony_ci	conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
9328c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	iscsit_handle_connection_cleanup(conn);
9358c2ecf20Sopenharmony_ci	*conn_freed = true;
9368c2ecf20Sopenharmony_ci}
937