xref: /OK3568_Linux_fs/kernel/drivers/target/iscsi/iscsi_target_erl0.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /******************************************************************************
3*4882a593Smuzhiyun  * This file contains error recovery level zero functions used by
4*4882a593Smuzhiyun  * the iSCSI Target driver.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * (c) Copyright 2007-2013 Datera, Inc.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  ******************************************************************************/
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/sched/signal.h>
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun #include <scsi/iscsi_proto.h>
15*4882a593Smuzhiyun #include <target/target_core_base.h>
16*4882a593Smuzhiyun #include <target/target_core_fabric.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <target/iscsi/iscsi_target_core.h>
19*4882a593Smuzhiyun #include "iscsi_target_seq_pdu_list.h"
20*4882a593Smuzhiyun #include "iscsi_target_erl0.h"
21*4882a593Smuzhiyun #include "iscsi_target_erl1.h"
22*4882a593Smuzhiyun #include "iscsi_target_erl2.h"
23*4882a593Smuzhiyun #include "iscsi_target_util.h"
24*4882a593Smuzhiyun #include "iscsi_target.h"
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun /*
27*4882a593Smuzhiyun  *	Used to set values in struct iscsi_cmd that iscsit_dataout_check_sequence()
28*4882a593Smuzhiyun  *	checks against to determine a PDU's Offset+Length is within the current
29*4882a593Smuzhiyun  *	DataOUT Sequence.  Used for DataSequenceInOrder=Yes only.
30*4882a593Smuzhiyun  */
iscsit_set_dataout_sequence_values(struct iscsi_cmd * cmd)31*4882a593Smuzhiyun void iscsit_set_dataout_sequence_values(
32*4882a593Smuzhiyun 	struct iscsi_cmd *cmd)
33*4882a593Smuzhiyun {
34*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
35*4882a593Smuzhiyun 	/*
36*4882a593Smuzhiyun 	 * Still set seq_start_offset and seq_end_offset for Unsolicited
37*4882a593Smuzhiyun 	 * DataOUT, even if DataSequenceInOrder=No.
38*4882a593Smuzhiyun 	 */
39*4882a593Smuzhiyun 	if (cmd->unsolicited_data) {
40*4882a593Smuzhiyun 		cmd->seq_start_offset = cmd->write_data_done;
41*4882a593Smuzhiyun 		cmd->seq_end_offset = min(cmd->se_cmd.data_length,
42*4882a593Smuzhiyun 					conn->sess->sess_ops->FirstBurstLength);
43*4882a593Smuzhiyun 		return;
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	if (!conn->sess->sess_ops->DataSequenceInOrder)
47*4882a593Smuzhiyun 		return;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (!cmd->seq_start_offset && !cmd->seq_end_offset) {
50*4882a593Smuzhiyun 		cmd->seq_start_offset = cmd->write_data_done;
51*4882a593Smuzhiyun 		cmd->seq_end_offset = (cmd->se_cmd.data_length >
52*4882a593Smuzhiyun 			conn->sess->sess_ops->MaxBurstLength) ?
53*4882a593Smuzhiyun 			(cmd->write_data_done +
54*4882a593Smuzhiyun 			conn->sess->sess_ops->MaxBurstLength) : cmd->se_cmd.data_length;
55*4882a593Smuzhiyun 	} else {
56*4882a593Smuzhiyun 		cmd->seq_start_offset = cmd->seq_end_offset;
57*4882a593Smuzhiyun 		cmd->seq_end_offset = ((cmd->seq_end_offset +
58*4882a593Smuzhiyun 			conn->sess->sess_ops->MaxBurstLength) >=
59*4882a593Smuzhiyun 			cmd->se_cmd.data_length) ? cmd->se_cmd.data_length :
60*4882a593Smuzhiyun 			(cmd->seq_end_offset +
61*4882a593Smuzhiyun 			 conn->sess->sess_ops->MaxBurstLength);
62*4882a593Smuzhiyun 	}
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun 
iscsit_dataout_within_command_recovery_check(struct iscsi_cmd * cmd,unsigned char * buf)65*4882a593Smuzhiyun static int iscsit_dataout_within_command_recovery_check(
66*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
67*4882a593Smuzhiyun 	unsigned char *buf)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
70*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
71*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 	/*
74*4882a593Smuzhiyun 	 * We do the within-command recovery checks here as it is
75*4882a593Smuzhiyun 	 * the first function called in iscsi_check_pre_dataout().
76*4882a593Smuzhiyun 	 * Basically, if we are in within-command recovery and
77*4882a593Smuzhiyun 	 * the PDU does not contain the offset the sequence needs,
78*4882a593Smuzhiyun 	 * dump the payload.
79*4882a593Smuzhiyun 	 *
80*4882a593Smuzhiyun 	 * This only applies to DataPDUInOrder=Yes, for
81*4882a593Smuzhiyun 	 * DataPDUInOrder=No we only re-request the failed PDU
82*4882a593Smuzhiyun 	 * and check that all PDUs in a sequence are received
83*4882a593Smuzhiyun 	 * upon end of sequence.
84*4882a593Smuzhiyun 	 */
85*4882a593Smuzhiyun 	if (conn->sess->sess_ops->DataSequenceInOrder) {
86*4882a593Smuzhiyun 		if ((cmd->cmd_flags & ICF_WITHIN_COMMAND_RECOVERY) &&
87*4882a593Smuzhiyun 		    cmd->write_data_done != be32_to_cpu(hdr->offset))
88*4882a593Smuzhiyun 			goto dump;
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 		cmd->cmd_flags &= ~ICF_WITHIN_COMMAND_RECOVERY;
91*4882a593Smuzhiyun 	} else {
92*4882a593Smuzhiyun 		struct iscsi_seq *seq;
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
95*4882a593Smuzhiyun 					    payload_length);
96*4882a593Smuzhiyun 		if (!seq)
97*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
98*4882a593Smuzhiyun 		/*
99*4882a593Smuzhiyun 		 * Set the struct iscsi_seq pointer to reuse later.
100*4882a593Smuzhiyun 		 */
101*4882a593Smuzhiyun 		cmd->seq_ptr = seq;
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 		if (conn->sess->sess_ops->DataPDUInOrder) {
104*4882a593Smuzhiyun 			if (seq->status ==
105*4882a593Smuzhiyun 			    DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
106*4882a593Smuzhiyun 			   (seq->offset != be32_to_cpu(hdr->offset) ||
107*4882a593Smuzhiyun 			    seq->data_sn != be32_to_cpu(hdr->datasn)))
108*4882a593Smuzhiyun 				goto dump;
109*4882a593Smuzhiyun 		} else {
110*4882a593Smuzhiyun 			if (seq->status ==
111*4882a593Smuzhiyun 			     DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY &&
112*4882a593Smuzhiyun 			    seq->data_sn != be32_to_cpu(hdr->datasn))
113*4882a593Smuzhiyun 				goto dump;
114*4882a593Smuzhiyun 		}
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun 		if (seq->status == DATAOUT_SEQUENCE_COMPLETE)
117*4882a593Smuzhiyun 			goto dump;
118*4882a593Smuzhiyun 
119*4882a593Smuzhiyun 		if (seq->status != DATAOUT_SEQUENCE_COMPLETE)
120*4882a593Smuzhiyun 			seq->status = 0;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun dump:
126*4882a593Smuzhiyun 	pr_err("Dumping DataOUT PDU Offset: %u Length: %d DataSN:"
127*4882a593Smuzhiyun 		" 0x%08x\n", hdr->offset, payload_length, hdr->datasn);
128*4882a593Smuzhiyun 	return iscsit_dump_data_payload(conn, payload_length, 1);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun 
iscsit_dataout_check_unsolicited_sequence(struct iscsi_cmd * cmd,unsigned char * buf)131*4882a593Smuzhiyun static int iscsit_dataout_check_unsolicited_sequence(
132*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
133*4882a593Smuzhiyun 	unsigned char *buf)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	u32 first_burst_len;
136*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
137*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
138*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 	if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
142*4882a593Smuzhiyun 	   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
143*4882a593Smuzhiyun 		pr_err("Command ITT: 0x%08x with Offset: %u,"
144*4882a593Smuzhiyun 		" Length: %u outside of Unsolicited Sequence %u:%u while"
145*4882a593Smuzhiyun 		" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
146*4882a593Smuzhiyun 		be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
147*4882a593Smuzhiyun 			cmd->seq_end_offset);
148*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
149*4882a593Smuzhiyun 	}
150*4882a593Smuzhiyun 
151*4882a593Smuzhiyun 	first_burst_len = (cmd->first_burst_len + payload_length);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) {
154*4882a593Smuzhiyun 		pr_err("Total %u bytes exceeds FirstBurstLength: %u"
155*4882a593Smuzhiyun 			" for this Unsolicited DataOut Burst.\n",
156*4882a593Smuzhiyun 			first_burst_len, conn->sess->sess_ops->FirstBurstLength);
157*4882a593Smuzhiyun 		transport_send_check_condition_and_sense(&cmd->se_cmd,
158*4882a593Smuzhiyun 				TCM_INCORRECT_AMOUNT_OF_DATA, 0);
159*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
160*4882a593Smuzhiyun 	}
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/*
163*4882a593Smuzhiyun 	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
164*4882a593Smuzhiyun 	 * checks for the current Unsolicited DataOUT Sequence.
165*4882a593Smuzhiyun 	 */
166*4882a593Smuzhiyun 	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
167*4882a593Smuzhiyun 		/*
168*4882a593Smuzhiyun 		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
169*4882a593Smuzhiyun 		 * sequence checks are handled in
170*4882a593Smuzhiyun 		 * iscsit_dataout_datapduinorder_no_fbit().
171*4882a593Smuzhiyun 		 */
172*4882a593Smuzhiyun 		if (!conn->sess->sess_ops->DataPDUInOrder)
173*4882a593Smuzhiyun 			goto out;
174*4882a593Smuzhiyun 
175*4882a593Smuzhiyun 		if ((first_burst_len != cmd->se_cmd.data_length) &&
176*4882a593Smuzhiyun 		    (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) {
177*4882a593Smuzhiyun 			pr_err("Unsolicited non-immediate data"
178*4882a593Smuzhiyun 			" received %u does not equal FirstBurstLength: %u, and"
179*4882a593Smuzhiyun 			" does not equal ExpXferLen %u.\n", first_burst_len,
180*4882a593Smuzhiyun 				conn->sess->sess_ops->FirstBurstLength,
181*4882a593Smuzhiyun 				cmd->se_cmd.data_length);
182*4882a593Smuzhiyun 			transport_send_check_condition_and_sense(&cmd->se_cmd,
183*4882a593Smuzhiyun 					TCM_INCORRECT_AMOUNT_OF_DATA, 0);
184*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
185*4882a593Smuzhiyun 		}
186*4882a593Smuzhiyun 	} else {
187*4882a593Smuzhiyun 		if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) {
188*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x reached"
189*4882a593Smuzhiyun 			" FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
190*4882a593Smuzhiyun 				" error.\n", cmd->init_task_tag,
191*4882a593Smuzhiyun 				conn->sess->sess_ops->FirstBurstLength);
192*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
193*4882a593Smuzhiyun 		}
194*4882a593Smuzhiyun 		if (first_burst_len == cmd->se_cmd.data_length) {
195*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x reached"
196*4882a593Smuzhiyun 			" ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol"
197*4882a593Smuzhiyun 			" error.\n", cmd->init_task_tag, cmd->se_cmd.data_length);
198*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
199*4882a593Smuzhiyun 		}
200*4882a593Smuzhiyun 	}
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun out:
203*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun 
iscsit_dataout_check_sequence(struct iscsi_cmd * cmd,unsigned char * buf)206*4882a593Smuzhiyun static int iscsit_dataout_check_sequence(
207*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
208*4882a593Smuzhiyun 	unsigned char *buf)
209*4882a593Smuzhiyun {
210*4882a593Smuzhiyun 	u32 next_burst_len;
211*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
212*4882a593Smuzhiyun 	struct iscsi_seq *seq = NULL;
213*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
214*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun 	/*
217*4882a593Smuzhiyun 	 * For DataSequenceInOrder=Yes: Check that the offset and offset+length
218*4882a593Smuzhiyun 	 * is within range as defined by iscsi_set_dataout_sequence_values().
219*4882a593Smuzhiyun 	 *
220*4882a593Smuzhiyun 	 * For DataSequenceInOrder=No: Check that an struct iscsi_seq exists for
221*4882a593Smuzhiyun 	 * offset+length tuple.
222*4882a593Smuzhiyun 	 */
223*4882a593Smuzhiyun 	if (conn->sess->sess_ops->DataSequenceInOrder) {
224*4882a593Smuzhiyun 		/*
225*4882a593Smuzhiyun 		 * Due to possibility of recovery DataOUT sent by the initiator
226*4882a593Smuzhiyun 		 * fullfilling an Recovery R2T, it's best to just dump the
227*4882a593Smuzhiyun 		 * payload here, instead of erroring out.
228*4882a593Smuzhiyun 		 */
229*4882a593Smuzhiyun 		if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) ||
230*4882a593Smuzhiyun 		   ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) {
231*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x with Offset: %u,"
232*4882a593Smuzhiyun 			" Length: %u outside of Sequence %u:%u while"
233*4882a593Smuzhiyun 			" DataSequenceInOrder=Yes.\n", cmd->init_task_tag,
234*4882a593Smuzhiyun 			be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset,
235*4882a593Smuzhiyun 				cmd->seq_end_offset);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
238*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
239*4882a593Smuzhiyun 			return DATAOUT_WITHIN_COMMAND_RECOVERY;
240*4882a593Smuzhiyun 		}
241*4882a593Smuzhiyun 
242*4882a593Smuzhiyun 		next_burst_len = (cmd->next_burst_len + payload_length);
243*4882a593Smuzhiyun 	} else {
244*4882a593Smuzhiyun 		seq = iscsit_get_seq_holder(cmd, be32_to_cpu(hdr->offset),
245*4882a593Smuzhiyun 					    payload_length);
246*4882a593Smuzhiyun 		if (!seq)
247*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
248*4882a593Smuzhiyun 		/*
249*4882a593Smuzhiyun 		 * Set the struct iscsi_seq pointer to reuse later.
250*4882a593Smuzhiyun 		 */
251*4882a593Smuzhiyun 		cmd->seq_ptr = seq;
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 		if (seq->status == DATAOUT_SEQUENCE_COMPLETE) {
254*4882a593Smuzhiyun 			if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
255*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
256*4882a593Smuzhiyun 			return DATAOUT_WITHIN_COMMAND_RECOVERY;
257*4882a593Smuzhiyun 		}
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 		next_burst_len = (seq->next_burst_len + payload_length);
260*4882a593Smuzhiyun 	}
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	if (next_burst_len > conn->sess->sess_ops->MaxBurstLength) {
263*4882a593Smuzhiyun 		pr_err("Command ITT: 0x%08x, NextBurstLength: %u and"
264*4882a593Smuzhiyun 			" Length: %u exceeds MaxBurstLength: %u. protocol"
265*4882a593Smuzhiyun 			" error.\n", cmd->init_task_tag,
266*4882a593Smuzhiyun 			(next_burst_len - payload_length),
267*4882a593Smuzhiyun 			payload_length, conn->sess->sess_ops->MaxBurstLength);
268*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
269*4882a593Smuzhiyun 	}
270*4882a593Smuzhiyun 
271*4882a593Smuzhiyun 	/*
272*4882a593Smuzhiyun 	 * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity
273*4882a593Smuzhiyun 	 * checks for the current DataOUT Sequence.
274*4882a593Smuzhiyun 	 */
275*4882a593Smuzhiyun 	if (hdr->flags & ISCSI_FLAG_CMD_FINAL) {
276*4882a593Smuzhiyun 		/*
277*4882a593Smuzhiyun 		 * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of
278*4882a593Smuzhiyun 		 * sequence checks are handled in
279*4882a593Smuzhiyun 		 * iscsit_dataout_datapduinorder_no_fbit().
280*4882a593Smuzhiyun 		 */
281*4882a593Smuzhiyun 		if (!conn->sess->sess_ops->DataPDUInOrder)
282*4882a593Smuzhiyun 			goto out;
283*4882a593Smuzhiyun 
284*4882a593Smuzhiyun 		if (conn->sess->sess_ops->DataSequenceInOrder) {
285*4882a593Smuzhiyun 			if ((next_burst_len <
286*4882a593Smuzhiyun 			     conn->sess->sess_ops->MaxBurstLength) &&
287*4882a593Smuzhiyun 			   ((cmd->write_data_done + payload_length) <
288*4882a593Smuzhiyun 			     cmd->se_cmd.data_length)) {
289*4882a593Smuzhiyun 				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
290*4882a593Smuzhiyun 				" before end of DataOUT sequence, protocol"
291*4882a593Smuzhiyun 				" error.\n", cmd->init_task_tag);
292*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
293*4882a593Smuzhiyun 			}
294*4882a593Smuzhiyun 		} else {
295*4882a593Smuzhiyun 			if (next_burst_len < seq->xfer_len) {
296*4882a593Smuzhiyun 				pr_err("Command ITT: 0x%08x set ISCSI_FLAG_CMD_FINAL"
297*4882a593Smuzhiyun 				" before end of DataOUT sequence, protocol"
298*4882a593Smuzhiyun 				" error.\n", cmd->init_task_tag);
299*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
300*4882a593Smuzhiyun 			}
301*4882a593Smuzhiyun 		}
302*4882a593Smuzhiyun 	} else {
303*4882a593Smuzhiyun 		if (conn->sess->sess_ops->DataSequenceInOrder) {
304*4882a593Smuzhiyun 			if (next_burst_len ==
305*4882a593Smuzhiyun 					conn->sess->sess_ops->MaxBurstLength) {
306*4882a593Smuzhiyun 				pr_err("Command ITT: 0x%08x reached"
307*4882a593Smuzhiyun 				" MaxBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is"
308*4882a593Smuzhiyun 				" not set, protocol error.", cmd->init_task_tag,
309*4882a593Smuzhiyun 					conn->sess->sess_ops->MaxBurstLength);
310*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
311*4882a593Smuzhiyun 			}
312*4882a593Smuzhiyun 			if ((cmd->write_data_done + payload_length) ==
313*4882a593Smuzhiyun 					cmd->se_cmd.data_length) {
314*4882a593Smuzhiyun 				pr_err("Command ITT: 0x%08x reached"
315*4882a593Smuzhiyun 				" last DataOUT PDU in sequence but ISCSI_FLAG_"
316*4882a593Smuzhiyun 				"CMD_FINAL is not set, protocol error.\n",
317*4882a593Smuzhiyun 					cmd->init_task_tag);
318*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
319*4882a593Smuzhiyun 			}
320*4882a593Smuzhiyun 		} else {
321*4882a593Smuzhiyun 			if (next_burst_len == seq->xfer_len) {
322*4882a593Smuzhiyun 				pr_err("Command ITT: 0x%08x reached"
323*4882a593Smuzhiyun 				" last DataOUT PDU in sequence but ISCSI_FLAG_"
324*4882a593Smuzhiyun 				"CMD_FINAL is not set, protocol error.\n",
325*4882a593Smuzhiyun 					cmd->init_task_tag);
326*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
327*4882a593Smuzhiyun 			}
328*4882a593Smuzhiyun 		}
329*4882a593Smuzhiyun 	}
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun out:
332*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
333*4882a593Smuzhiyun }
334*4882a593Smuzhiyun 
iscsit_dataout_check_datasn(struct iscsi_cmd * cmd,unsigned char * buf)335*4882a593Smuzhiyun static int iscsit_dataout_check_datasn(
336*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
337*4882a593Smuzhiyun 	unsigned char *buf)
338*4882a593Smuzhiyun {
339*4882a593Smuzhiyun 	u32 data_sn = 0;
340*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
341*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
342*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/*
345*4882a593Smuzhiyun 	 * Considering the target has no method of re-requesting DataOUT
346*4882a593Smuzhiyun 	 * by DataSN, if we receieve a greater DataSN than expected we
347*4882a593Smuzhiyun 	 * assume the functions for DataPDUInOrder=[Yes,No] below will
348*4882a593Smuzhiyun 	 * handle it.
349*4882a593Smuzhiyun 	 *
350*4882a593Smuzhiyun 	 * If the DataSN is less than expected, dump the payload.
351*4882a593Smuzhiyun 	 */
352*4882a593Smuzhiyun 	if (conn->sess->sess_ops->DataSequenceInOrder)
353*4882a593Smuzhiyun 		data_sn = cmd->data_sn;
354*4882a593Smuzhiyun 	else {
355*4882a593Smuzhiyun 		struct iscsi_seq *seq = cmd->seq_ptr;
356*4882a593Smuzhiyun 		data_sn = seq->data_sn;
357*4882a593Smuzhiyun 	}
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	if (be32_to_cpu(hdr->datasn) > data_sn) {
360*4882a593Smuzhiyun 		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
361*4882a593Smuzhiyun 			" higher than expected 0x%08x.\n", cmd->init_task_tag,
362*4882a593Smuzhiyun 				be32_to_cpu(hdr->datasn), data_sn);
363*4882a593Smuzhiyun 		goto recover;
364*4882a593Smuzhiyun 	} else if (be32_to_cpu(hdr->datasn) < data_sn) {
365*4882a593Smuzhiyun 		pr_err("Command ITT: 0x%08x, received DataSN: 0x%08x"
366*4882a593Smuzhiyun 			" lower than expected 0x%08x, discarding payload.\n",
367*4882a593Smuzhiyun 			cmd->init_task_tag, be32_to_cpu(hdr->datasn), data_sn);
368*4882a593Smuzhiyun 		goto dump;
369*4882a593Smuzhiyun 	}
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun recover:
374*4882a593Smuzhiyun 	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
375*4882a593Smuzhiyun 		pr_err("Unable to perform within-command recovery"
376*4882a593Smuzhiyun 				" while ERL=0.\n");
377*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun dump:
380*4882a593Smuzhiyun 	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
381*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	return DATAOUT_WITHIN_COMMAND_RECOVERY;
384*4882a593Smuzhiyun }
385*4882a593Smuzhiyun 
iscsit_dataout_pre_datapduinorder_yes(struct iscsi_cmd * cmd,unsigned char * buf)386*4882a593Smuzhiyun static int iscsit_dataout_pre_datapduinorder_yes(
387*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
388*4882a593Smuzhiyun 	unsigned char *buf)
389*4882a593Smuzhiyun {
390*4882a593Smuzhiyun 	int dump = 0, recovery = 0;
391*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
392*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
393*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	/*
396*4882a593Smuzhiyun 	 * For DataSequenceInOrder=Yes: If the offset is greater than the global
397*4882a593Smuzhiyun 	 * DataPDUInOrder=Yes offset counter in struct iscsi_cmd a protcol error has
398*4882a593Smuzhiyun 	 * occurred and fail the connection.
399*4882a593Smuzhiyun 	 *
400*4882a593Smuzhiyun 	 * For DataSequenceInOrder=No: If the offset is greater than the per
401*4882a593Smuzhiyun 	 * sequence DataPDUInOrder=Yes offset counter in struct iscsi_seq a protocol
402*4882a593Smuzhiyun 	 * error has occurred and fail the connection.
403*4882a593Smuzhiyun 	 */
404*4882a593Smuzhiyun 	if (conn->sess->sess_ops->DataSequenceInOrder) {
405*4882a593Smuzhiyun 		if (be32_to_cpu(hdr->offset) != cmd->write_data_done) {
406*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x, received offset"
407*4882a593Smuzhiyun 			" %u different than expected %u.\n", cmd->init_task_tag,
408*4882a593Smuzhiyun 				be32_to_cpu(hdr->offset), cmd->write_data_done);
409*4882a593Smuzhiyun 			recovery = 1;
410*4882a593Smuzhiyun 			goto recover;
411*4882a593Smuzhiyun 		}
412*4882a593Smuzhiyun 	} else {
413*4882a593Smuzhiyun 		struct iscsi_seq *seq = cmd->seq_ptr;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 		if (be32_to_cpu(hdr->offset) > seq->offset) {
416*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x, received offset"
417*4882a593Smuzhiyun 			" %u greater than expected %u.\n", cmd->init_task_tag,
418*4882a593Smuzhiyun 				be32_to_cpu(hdr->offset), seq->offset);
419*4882a593Smuzhiyun 			recovery = 1;
420*4882a593Smuzhiyun 			goto recover;
421*4882a593Smuzhiyun 		} else if (be32_to_cpu(hdr->offset) < seq->offset) {
422*4882a593Smuzhiyun 			pr_err("Command ITT: 0x%08x, received offset"
423*4882a593Smuzhiyun 			" %u less than expected %u, discarding payload.\n",
424*4882a593Smuzhiyun 				cmd->init_task_tag, be32_to_cpu(hdr->offset),
425*4882a593Smuzhiyun 				seq->offset);
426*4882a593Smuzhiyun 			dump = 1;
427*4882a593Smuzhiyun 			goto dump;
428*4882a593Smuzhiyun 		}
429*4882a593Smuzhiyun 	}
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun recover:
434*4882a593Smuzhiyun 	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
435*4882a593Smuzhiyun 		pr_err("Unable to perform within-command recovery"
436*4882a593Smuzhiyun 				" while ERL=0.\n");
437*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun dump:
440*4882a593Smuzhiyun 	if (iscsit_dump_data_payload(conn, payload_length, 1) < 0)
441*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 	return (recovery) ? iscsit_recover_dataout_sequence(cmd,
444*4882a593Smuzhiyun 		be32_to_cpu(hdr->offset), payload_length) :
445*4882a593Smuzhiyun 	       (dump) ? DATAOUT_WITHIN_COMMAND_RECOVERY : DATAOUT_NORMAL;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun 
iscsit_dataout_pre_datapduinorder_no(struct iscsi_cmd * cmd,unsigned char * buf)448*4882a593Smuzhiyun static int iscsit_dataout_pre_datapduinorder_no(
449*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
450*4882a593Smuzhiyun 	unsigned char *buf)
451*4882a593Smuzhiyun {
452*4882a593Smuzhiyun 	struct iscsi_pdu *pdu;
453*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
454*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	pdu = iscsit_get_pdu_holder(cmd, be32_to_cpu(hdr->offset),
457*4882a593Smuzhiyun 				    payload_length);
458*4882a593Smuzhiyun 	if (!pdu)
459*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
460*4882a593Smuzhiyun 
461*4882a593Smuzhiyun 	cmd->pdu_ptr = pdu;
462*4882a593Smuzhiyun 
463*4882a593Smuzhiyun 	switch (pdu->status) {
464*4882a593Smuzhiyun 	case ISCSI_PDU_NOT_RECEIVED:
465*4882a593Smuzhiyun 	case ISCSI_PDU_CRC_FAILED:
466*4882a593Smuzhiyun 	case ISCSI_PDU_TIMED_OUT:
467*4882a593Smuzhiyun 		break;
468*4882a593Smuzhiyun 	case ISCSI_PDU_RECEIVED_OK:
469*4882a593Smuzhiyun 		pr_err("Command ITT: 0x%08x received already gotten"
470*4882a593Smuzhiyun 			" Offset: %u, Length: %u\n", cmd->init_task_tag,
471*4882a593Smuzhiyun 				be32_to_cpu(hdr->offset), payload_length);
472*4882a593Smuzhiyun 		return iscsit_dump_data_payload(cmd->conn, payload_length, 1);
473*4882a593Smuzhiyun 	default:
474*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
475*4882a593Smuzhiyun 	}
476*4882a593Smuzhiyun 
477*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
478*4882a593Smuzhiyun }
479*4882a593Smuzhiyun 
iscsit_dataout_update_r2t(struct iscsi_cmd * cmd,u32 offset,u32 length)480*4882a593Smuzhiyun static int iscsit_dataout_update_r2t(struct iscsi_cmd *cmd, u32 offset, u32 length)
481*4882a593Smuzhiyun {
482*4882a593Smuzhiyun 	struct iscsi_r2t *r2t;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	if (cmd->unsolicited_data)
485*4882a593Smuzhiyun 		return 0;
486*4882a593Smuzhiyun 
487*4882a593Smuzhiyun 	r2t = iscsit_get_r2t_for_eos(cmd, offset, length);
488*4882a593Smuzhiyun 	if (!r2t)
489*4882a593Smuzhiyun 		return -1;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	spin_lock_bh(&cmd->r2t_lock);
492*4882a593Smuzhiyun 	r2t->seq_complete = 1;
493*4882a593Smuzhiyun 	cmd->outstanding_r2ts--;
494*4882a593Smuzhiyun 	spin_unlock_bh(&cmd->r2t_lock);
495*4882a593Smuzhiyun 
496*4882a593Smuzhiyun 	return 0;
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun 
iscsit_dataout_update_datapduinorder_no(struct iscsi_cmd * cmd,u32 data_sn,int f_bit)499*4882a593Smuzhiyun static int iscsit_dataout_update_datapduinorder_no(
500*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
501*4882a593Smuzhiyun 	u32 data_sn,
502*4882a593Smuzhiyun 	int f_bit)
503*4882a593Smuzhiyun {
504*4882a593Smuzhiyun 	int ret = 0;
505*4882a593Smuzhiyun 	struct iscsi_pdu *pdu = cmd->pdu_ptr;
506*4882a593Smuzhiyun 
507*4882a593Smuzhiyun 	pdu->data_sn = data_sn;
508*4882a593Smuzhiyun 
509*4882a593Smuzhiyun 	switch (pdu->status) {
510*4882a593Smuzhiyun 	case ISCSI_PDU_NOT_RECEIVED:
511*4882a593Smuzhiyun 		pdu->status = ISCSI_PDU_RECEIVED_OK;
512*4882a593Smuzhiyun 		break;
513*4882a593Smuzhiyun 	case ISCSI_PDU_CRC_FAILED:
514*4882a593Smuzhiyun 		pdu->status = ISCSI_PDU_RECEIVED_OK;
515*4882a593Smuzhiyun 		break;
516*4882a593Smuzhiyun 	case ISCSI_PDU_TIMED_OUT:
517*4882a593Smuzhiyun 		pdu->status = ISCSI_PDU_RECEIVED_OK;
518*4882a593Smuzhiyun 		break;
519*4882a593Smuzhiyun 	default:
520*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	if (f_bit) {
524*4882a593Smuzhiyun 		ret = iscsit_dataout_datapduinorder_no_fbit(cmd, pdu);
525*4882a593Smuzhiyun 		if (ret == DATAOUT_CANNOT_RECOVER)
526*4882a593Smuzhiyun 			return ret;
527*4882a593Smuzhiyun 	}
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	return DATAOUT_NORMAL;
530*4882a593Smuzhiyun }
531*4882a593Smuzhiyun 
iscsit_dataout_post_crc_passed(struct iscsi_cmd * cmd,unsigned char * buf)532*4882a593Smuzhiyun static int iscsit_dataout_post_crc_passed(
533*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
534*4882a593Smuzhiyun 	unsigned char *buf)
535*4882a593Smuzhiyun {
536*4882a593Smuzhiyun 	int ret, send_r2t = 0;
537*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
538*4882a593Smuzhiyun 	struct iscsi_seq *seq = NULL;
539*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
540*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
541*4882a593Smuzhiyun 
542*4882a593Smuzhiyun 	if (cmd->unsolicited_data) {
543*4882a593Smuzhiyun 		if ((cmd->first_burst_len + payload_length) ==
544*4882a593Smuzhiyun 		     conn->sess->sess_ops->FirstBurstLength) {
545*4882a593Smuzhiyun 			if (iscsit_dataout_update_r2t(cmd, be32_to_cpu(hdr->offset),
546*4882a593Smuzhiyun 					payload_length) < 0)
547*4882a593Smuzhiyun 				return DATAOUT_CANNOT_RECOVER;
548*4882a593Smuzhiyun 			send_r2t = 1;
549*4882a593Smuzhiyun 		}
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 		if (!conn->sess->sess_ops->DataPDUInOrder) {
552*4882a593Smuzhiyun 			ret = iscsit_dataout_update_datapduinorder_no(cmd,
553*4882a593Smuzhiyun 				be32_to_cpu(hdr->datasn),
554*4882a593Smuzhiyun 				(hdr->flags & ISCSI_FLAG_CMD_FINAL));
555*4882a593Smuzhiyun 			if (ret == DATAOUT_CANNOT_RECOVER)
556*4882a593Smuzhiyun 				return ret;
557*4882a593Smuzhiyun 		}
558*4882a593Smuzhiyun 
559*4882a593Smuzhiyun 		cmd->first_burst_len += payload_length;
560*4882a593Smuzhiyun 
561*4882a593Smuzhiyun 		if (conn->sess->sess_ops->DataSequenceInOrder)
562*4882a593Smuzhiyun 			cmd->data_sn++;
563*4882a593Smuzhiyun 		else {
564*4882a593Smuzhiyun 			seq = cmd->seq_ptr;
565*4882a593Smuzhiyun 			seq->data_sn++;
566*4882a593Smuzhiyun 			seq->offset += payload_length;
567*4882a593Smuzhiyun 		}
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 		if (send_r2t) {
570*4882a593Smuzhiyun 			if (seq)
571*4882a593Smuzhiyun 				seq->status = DATAOUT_SEQUENCE_COMPLETE;
572*4882a593Smuzhiyun 			cmd->first_burst_len = 0;
573*4882a593Smuzhiyun 			cmd->unsolicited_data = 0;
574*4882a593Smuzhiyun 		}
575*4882a593Smuzhiyun 	} else {
576*4882a593Smuzhiyun 		if (conn->sess->sess_ops->DataSequenceInOrder) {
577*4882a593Smuzhiyun 			if ((cmd->next_burst_len + payload_length) ==
578*4882a593Smuzhiyun 			     conn->sess->sess_ops->MaxBurstLength) {
579*4882a593Smuzhiyun 				if (iscsit_dataout_update_r2t(cmd,
580*4882a593Smuzhiyun 						be32_to_cpu(hdr->offset),
581*4882a593Smuzhiyun 						payload_length) < 0)
582*4882a593Smuzhiyun 					return DATAOUT_CANNOT_RECOVER;
583*4882a593Smuzhiyun 				send_r2t = 1;
584*4882a593Smuzhiyun 			}
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 			if (!conn->sess->sess_ops->DataPDUInOrder) {
587*4882a593Smuzhiyun 				ret = iscsit_dataout_update_datapduinorder_no(
588*4882a593Smuzhiyun 						cmd, be32_to_cpu(hdr->datasn),
589*4882a593Smuzhiyun 						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
590*4882a593Smuzhiyun 				if (ret == DATAOUT_CANNOT_RECOVER)
591*4882a593Smuzhiyun 					return ret;
592*4882a593Smuzhiyun 			}
593*4882a593Smuzhiyun 
594*4882a593Smuzhiyun 			cmd->next_burst_len += payload_length;
595*4882a593Smuzhiyun 			cmd->data_sn++;
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun 			if (send_r2t)
598*4882a593Smuzhiyun 				cmd->next_burst_len = 0;
599*4882a593Smuzhiyun 		} else {
600*4882a593Smuzhiyun 			seq = cmd->seq_ptr;
601*4882a593Smuzhiyun 
602*4882a593Smuzhiyun 			if ((seq->next_burst_len + payload_length) ==
603*4882a593Smuzhiyun 			     seq->xfer_len) {
604*4882a593Smuzhiyun 				if (iscsit_dataout_update_r2t(cmd,
605*4882a593Smuzhiyun 						be32_to_cpu(hdr->offset),
606*4882a593Smuzhiyun 						payload_length) < 0)
607*4882a593Smuzhiyun 					return DATAOUT_CANNOT_RECOVER;
608*4882a593Smuzhiyun 				send_r2t = 1;
609*4882a593Smuzhiyun 			}
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 			if (!conn->sess->sess_ops->DataPDUInOrder) {
612*4882a593Smuzhiyun 				ret = iscsit_dataout_update_datapduinorder_no(
613*4882a593Smuzhiyun 						cmd, be32_to_cpu(hdr->datasn),
614*4882a593Smuzhiyun 						(hdr->flags & ISCSI_FLAG_CMD_FINAL));
615*4882a593Smuzhiyun 				if (ret == DATAOUT_CANNOT_RECOVER)
616*4882a593Smuzhiyun 					return ret;
617*4882a593Smuzhiyun 			}
618*4882a593Smuzhiyun 
619*4882a593Smuzhiyun 			seq->data_sn++;
620*4882a593Smuzhiyun 			seq->offset += payload_length;
621*4882a593Smuzhiyun 			seq->next_burst_len += payload_length;
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 			if (send_r2t) {
624*4882a593Smuzhiyun 				seq->next_burst_len = 0;
625*4882a593Smuzhiyun 				seq->status = DATAOUT_SEQUENCE_COMPLETE;
626*4882a593Smuzhiyun 			}
627*4882a593Smuzhiyun 		}
628*4882a593Smuzhiyun 	}
629*4882a593Smuzhiyun 
630*4882a593Smuzhiyun 	if (send_r2t && conn->sess->sess_ops->DataSequenceInOrder)
631*4882a593Smuzhiyun 		cmd->data_sn = 0;
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 	cmd->write_data_done += payload_length;
634*4882a593Smuzhiyun 
635*4882a593Smuzhiyun 	if (cmd->write_data_done == cmd->se_cmd.data_length)
636*4882a593Smuzhiyun 		return DATAOUT_SEND_TO_TRANSPORT;
637*4882a593Smuzhiyun 	else if (send_r2t)
638*4882a593Smuzhiyun 		return DATAOUT_SEND_R2T;
639*4882a593Smuzhiyun 	else
640*4882a593Smuzhiyun 		return DATAOUT_NORMAL;
641*4882a593Smuzhiyun }
642*4882a593Smuzhiyun 
iscsit_dataout_post_crc_failed(struct iscsi_cmd * cmd,unsigned char * buf)643*4882a593Smuzhiyun static int iscsit_dataout_post_crc_failed(
644*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
645*4882a593Smuzhiyun 	unsigned char *buf)
646*4882a593Smuzhiyun {
647*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
648*4882a593Smuzhiyun 	struct iscsi_pdu *pdu;
649*4882a593Smuzhiyun 	struct iscsi_data *hdr = (struct iscsi_data *) buf;
650*4882a593Smuzhiyun 	u32 payload_length = ntoh24(hdr->dlength);
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun 	if (conn->sess->sess_ops->DataPDUInOrder)
653*4882a593Smuzhiyun 		goto recover;
654*4882a593Smuzhiyun 	/*
655*4882a593Smuzhiyun 	 * The rest of this function is only called when DataPDUInOrder=No.
656*4882a593Smuzhiyun 	 */
657*4882a593Smuzhiyun 	pdu = cmd->pdu_ptr;
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	switch (pdu->status) {
660*4882a593Smuzhiyun 	case ISCSI_PDU_NOT_RECEIVED:
661*4882a593Smuzhiyun 		pdu->status = ISCSI_PDU_CRC_FAILED;
662*4882a593Smuzhiyun 		break;
663*4882a593Smuzhiyun 	case ISCSI_PDU_CRC_FAILED:
664*4882a593Smuzhiyun 		break;
665*4882a593Smuzhiyun 	case ISCSI_PDU_TIMED_OUT:
666*4882a593Smuzhiyun 		pdu->status = ISCSI_PDU_CRC_FAILED;
667*4882a593Smuzhiyun 		break;
668*4882a593Smuzhiyun 	default:
669*4882a593Smuzhiyun 		return DATAOUT_CANNOT_RECOVER;
670*4882a593Smuzhiyun 	}
671*4882a593Smuzhiyun 
672*4882a593Smuzhiyun recover:
673*4882a593Smuzhiyun 	return iscsit_recover_dataout_sequence(cmd, be32_to_cpu(hdr->offset),
674*4882a593Smuzhiyun 						payload_length);
675*4882a593Smuzhiyun }
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun /*
678*4882a593Smuzhiyun  *	Called from iscsit_handle_data_out() before DataOUT Payload is received
679*4882a593Smuzhiyun  *	and CRC computed.
680*4882a593Smuzhiyun  */
iscsit_check_pre_dataout(struct iscsi_cmd * cmd,unsigned char * buf)681*4882a593Smuzhiyun int iscsit_check_pre_dataout(
682*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
683*4882a593Smuzhiyun 	unsigned char *buf)
684*4882a593Smuzhiyun {
685*4882a593Smuzhiyun 	int ret;
686*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	ret = iscsit_dataout_within_command_recovery_check(cmd, buf);
689*4882a593Smuzhiyun 	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
690*4882a593Smuzhiyun 	    (ret == DATAOUT_CANNOT_RECOVER))
691*4882a593Smuzhiyun 		return ret;
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 	ret = iscsit_dataout_check_datasn(cmd, buf);
694*4882a593Smuzhiyun 	if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
695*4882a593Smuzhiyun 	    (ret == DATAOUT_CANNOT_RECOVER))
696*4882a593Smuzhiyun 		return ret;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 	if (cmd->unsolicited_data) {
699*4882a593Smuzhiyun 		ret = iscsit_dataout_check_unsolicited_sequence(cmd, buf);
700*4882a593Smuzhiyun 		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
701*4882a593Smuzhiyun 		    (ret == DATAOUT_CANNOT_RECOVER))
702*4882a593Smuzhiyun 			return ret;
703*4882a593Smuzhiyun 	} else {
704*4882a593Smuzhiyun 		ret = iscsit_dataout_check_sequence(cmd, buf);
705*4882a593Smuzhiyun 		if ((ret == DATAOUT_WITHIN_COMMAND_RECOVERY) ||
706*4882a593Smuzhiyun 		    (ret == DATAOUT_CANNOT_RECOVER))
707*4882a593Smuzhiyun 			return ret;
708*4882a593Smuzhiyun 	}
709*4882a593Smuzhiyun 
710*4882a593Smuzhiyun 	return (conn->sess->sess_ops->DataPDUInOrder) ?
711*4882a593Smuzhiyun 		iscsit_dataout_pre_datapduinorder_yes(cmd, buf) :
712*4882a593Smuzhiyun 		iscsit_dataout_pre_datapduinorder_no(cmd, buf);
713*4882a593Smuzhiyun }
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun /*
716*4882a593Smuzhiyun  *	Called from iscsit_handle_data_out() after DataOUT Payload is received
717*4882a593Smuzhiyun  *	and CRC computed.
718*4882a593Smuzhiyun  */
iscsit_check_post_dataout(struct iscsi_cmd * cmd,unsigned char * buf,u8 data_crc_failed)719*4882a593Smuzhiyun int iscsit_check_post_dataout(
720*4882a593Smuzhiyun 	struct iscsi_cmd *cmd,
721*4882a593Smuzhiyun 	unsigned char *buf,
722*4882a593Smuzhiyun 	u8 data_crc_failed)
723*4882a593Smuzhiyun {
724*4882a593Smuzhiyun 	struct iscsi_conn *conn = cmd->conn;
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	cmd->dataout_timeout_retries = 0;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	if (!data_crc_failed)
729*4882a593Smuzhiyun 		return iscsit_dataout_post_crc_passed(cmd, buf);
730*4882a593Smuzhiyun 	else {
731*4882a593Smuzhiyun 		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
732*4882a593Smuzhiyun 			pr_err("Unable to recover from DataOUT CRC"
733*4882a593Smuzhiyun 				" failure while ERL=0, closing session.\n");
734*4882a593Smuzhiyun 			iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR,
735*4882a593Smuzhiyun 					  buf);
736*4882a593Smuzhiyun 			return DATAOUT_CANNOT_RECOVER;
737*4882a593Smuzhiyun 		}
738*4882a593Smuzhiyun 
739*4882a593Smuzhiyun 		iscsit_reject_cmd(cmd, ISCSI_REASON_DATA_DIGEST_ERROR, buf);
740*4882a593Smuzhiyun 		return iscsit_dataout_post_crc_failed(cmd, buf);
741*4882a593Smuzhiyun 	}
742*4882a593Smuzhiyun }
743*4882a593Smuzhiyun 
iscsit_handle_time2retain_timeout(struct timer_list * t)744*4882a593Smuzhiyun void iscsit_handle_time2retain_timeout(struct timer_list *t)
745*4882a593Smuzhiyun {
746*4882a593Smuzhiyun 	struct iscsi_session *sess = from_timer(sess, t, time2retain_timer);
747*4882a593Smuzhiyun 	struct iscsi_portal_group *tpg = sess->tpg;
748*4882a593Smuzhiyun 	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun 	spin_lock_bh(&se_tpg->session_lock);
751*4882a593Smuzhiyun 	if (sess->time2retain_timer_flags & ISCSI_TF_STOP) {
752*4882a593Smuzhiyun 		spin_unlock_bh(&se_tpg->session_lock);
753*4882a593Smuzhiyun 		return;
754*4882a593Smuzhiyun 	}
755*4882a593Smuzhiyun 	if (atomic_read(&sess->session_reinstatement)) {
756*4882a593Smuzhiyun 		pr_err("Exiting Time2Retain handler because"
757*4882a593Smuzhiyun 				" session_reinstatement=1\n");
758*4882a593Smuzhiyun 		spin_unlock_bh(&se_tpg->session_lock);
759*4882a593Smuzhiyun 		return;
760*4882a593Smuzhiyun 	}
761*4882a593Smuzhiyun 	sess->time2retain_timer_flags |= ISCSI_TF_EXPIRED;
762*4882a593Smuzhiyun 
763*4882a593Smuzhiyun 	pr_err("Time2Retain timer expired for SID: %u, cleaning up"
764*4882a593Smuzhiyun 			" iSCSI session.\n", sess->sid);
765*4882a593Smuzhiyun 
766*4882a593Smuzhiyun 	iscsit_fill_cxn_timeout_err_stats(sess);
767*4882a593Smuzhiyun 	spin_unlock_bh(&se_tpg->session_lock);
768*4882a593Smuzhiyun 	iscsit_close_session(sess);
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun 
iscsit_start_time2retain_handler(struct iscsi_session * sess)771*4882a593Smuzhiyun void iscsit_start_time2retain_handler(struct iscsi_session *sess)
772*4882a593Smuzhiyun {
773*4882a593Smuzhiyun 	int tpg_active;
774*4882a593Smuzhiyun 	/*
775*4882a593Smuzhiyun 	 * Only start Time2Retain timer when the associated TPG is still in
776*4882a593Smuzhiyun 	 * an ACTIVE (eg: not disabled or shutdown) state.
777*4882a593Smuzhiyun 	 */
778*4882a593Smuzhiyun 	spin_lock(&sess->tpg->tpg_state_lock);
779*4882a593Smuzhiyun 	tpg_active = (sess->tpg->tpg_state == TPG_STATE_ACTIVE);
780*4882a593Smuzhiyun 	spin_unlock(&sess->tpg->tpg_state_lock);
781*4882a593Smuzhiyun 
782*4882a593Smuzhiyun 	if (!tpg_active)
783*4882a593Smuzhiyun 		return;
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	if (sess->time2retain_timer_flags & ISCSI_TF_RUNNING)
786*4882a593Smuzhiyun 		return;
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	pr_debug("Starting Time2Retain timer for %u seconds on"
789*4882a593Smuzhiyun 		" SID: %u\n", sess->sess_ops->DefaultTime2Retain, sess->sid);
790*4882a593Smuzhiyun 
791*4882a593Smuzhiyun 	sess->time2retain_timer_flags &= ~ISCSI_TF_STOP;
792*4882a593Smuzhiyun 	sess->time2retain_timer_flags |= ISCSI_TF_RUNNING;
793*4882a593Smuzhiyun 	mod_timer(&sess->time2retain_timer,
794*4882a593Smuzhiyun 		  jiffies + sess->sess_ops->DefaultTime2Retain * HZ);
795*4882a593Smuzhiyun }
796*4882a593Smuzhiyun 
iscsit_stop_time2retain_timer(struct iscsi_session * sess)797*4882a593Smuzhiyun int iscsit_stop_time2retain_timer(struct iscsi_session *sess)
798*4882a593Smuzhiyun {
799*4882a593Smuzhiyun 	struct iscsi_portal_group *tpg = sess->tpg;
800*4882a593Smuzhiyun 	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
801*4882a593Smuzhiyun 
802*4882a593Smuzhiyun 	lockdep_assert_held(&se_tpg->session_lock);
803*4882a593Smuzhiyun 
804*4882a593Smuzhiyun 	if (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)
805*4882a593Smuzhiyun 		return -1;
806*4882a593Smuzhiyun 
807*4882a593Smuzhiyun 	if (!(sess->time2retain_timer_flags & ISCSI_TF_RUNNING))
808*4882a593Smuzhiyun 		return 0;
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	sess->time2retain_timer_flags |= ISCSI_TF_STOP;
811*4882a593Smuzhiyun 	spin_unlock(&se_tpg->session_lock);
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	del_timer_sync(&sess->time2retain_timer);
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	spin_lock(&se_tpg->session_lock);
816*4882a593Smuzhiyun 	sess->time2retain_timer_flags &= ~ISCSI_TF_RUNNING;
817*4882a593Smuzhiyun 	pr_debug("Stopped Time2Retain Timer for SID: %u\n",
818*4882a593Smuzhiyun 			sess->sid);
819*4882a593Smuzhiyun 	return 0;
820*4882a593Smuzhiyun }
821*4882a593Smuzhiyun 
iscsit_connection_reinstatement_rcfr(struct iscsi_conn * conn)822*4882a593Smuzhiyun void iscsit_connection_reinstatement_rcfr(struct iscsi_conn *conn)
823*4882a593Smuzhiyun {
824*4882a593Smuzhiyun 	spin_lock_bh(&conn->state_lock);
825*4882a593Smuzhiyun 	if (atomic_read(&conn->connection_exit)) {
826*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
827*4882a593Smuzhiyun 		goto sleep;
828*4882a593Smuzhiyun 	}
829*4882a593Smuzhiyun 
830*4882a593Smuzhiyun 	if (atomic_read(&conn->transport_failed)) {
831*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
832*4882a593Smuzhiyun 		goto sleep;
833*4882a593Smuzhiyun 	}
834*4882a593Smuzhiyun 	spin_unlock_bh(&conn->state_lock);
835*4882a593Smuzhiyun 
836*4882a593Smuzhiyun 	if (conn->tx_thread && conn->tx_thread_active)
837*4882a593Smuzhiyun 		send_sig(SIGINT, conn->tx_thread, 1);
838*4882a593Smuzhiyun 	if (conn->rx_thread && conn->rx_thread_active)
839*4882a593Smuzhiyun 		send_sig(SIGINT, conn->rx_thread, 1);
840*4882a593Smuzhiyun 
841*4882a593Smuzhiyun sleep:
842*4882a593Smuzhiyun 	wait_for_completion(&conn->conn_wait_rcfr_comp);
843*4882a593Smuzhiyun 	complete(&conn->conn_post_wait_comp);
844*4882a593Smuzhiyun }
845*4882a593Smuzhiyun 
iscsit_cause_connection_reinstatement(struct iscsi_conn * conn,int sleep)846*4882a593Smuzhiyun void iscsit_cause_connection_reinstatement(struct iscsi_conn *conn, int sleep)
847*4882a593Smuzhiyun {
848*4882a593Smuzhiyun 	spin_lock_bh(&conn->state_lock);
849*4882a593Smuzhiyun 	if (atomic_read(&conn->connection_exit)) {
850*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
851*4882a593Smuzhiyun 		return;
852*4882a593Smuzhiyun 	}
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	if (atomic_read(&conn->transport_failed)) {
855*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
856*4882a593Smuzhiyun 		return;
857*4882a593Smuzhiyun 	}
858*4882a593Smuzhiyun 
859*4882a593Smuzhiyun 	if (atomic_read(&conn->connection_reinstatement)) {
860*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
861*4882a593Smuzhiyun 		return;
862*4882a593Smuzhiyun 	}
863*4882a593Smuzhiyun 
864*4882a593Smuzhiyun 	if (conn->tx_thread && conn->tx_thread_active)
865*4882a593Smuzhiyun 		send_sig(SIGINT, conn->tx_thread, 1);
866*4882a593Smuzhiyun 	if (conn->rx_thread && conn->rx_thread_active)
867*4882a593Smuzhiyun 		send_sig(SIGINT, conn->rx_thread, 1);
868*4882a593Smuzhiyun 
869*4882a593Smuzhiyun 	atomic_set(&conn->connection_reinstatement, 1);
870*4882a593Smuzhiyun 	if (!sleep) {
871*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
872*4882a593Smuzhiyun 		return;
873*4882a593Smuzhiyun 	}
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	atomic_set(&conn->sleep_on_conn_wait_comp, 1);
876*4882a593Smuzhiyun 	spin_unlock_bh(&conn->state_lock);
877*4882a593Smuzhiyun 
878*4882a593Smuzhiyun 	wait_for_completion(&conn->conn_wait_comp);
879*4882a593Smuzhiyun 	complete(&conn->conn_post_wait_comp);
880*4882a593Smuzhiyun }
881*4882a593Smuzhiyun EXPORT_SYMBOL(iscsit_cause_connection_reinstatement);
882*4882a593Smuzhiyun 
iscsit_fall_back_to_erl0(struct iscsi_session * sess)883*4882a593Smuzhiyun void iscsit_fall_back_to_erl0(struct iscsi_session *sess)
884*4882a593Smuzhiyun {
885*4882a593Smuzhiyun 	pr_debug("Falling back to ErrorRecoveryLevel=0 for SID:"
886*4882a593Smuzhiyun 			" %u\n", sess->sid);
887*4882a593Smuzhiyun 
888*4882a593Smuzhiyun 	atomic_set(&sess->session_fall_back_to_erl0, 1);
889*4882a593Smuzhiyun }
890*4882a593Smuzhiyun 
iscsit_handle_connection_cleanup(struct iscsi_conn * conn)891*4882a593Smuzhiyun static void iscsit_handle_connection_cleanup(struct iscsi_conn *conn)
892*4882a593Smuzhiyun {
893*4882a593Smuzhiyun 	struct iscsi_session *sess = conn->sess;
894*4882a593Smuzhiyun 
895*4882a593Smuzhiyun 	if ((sess->sess_ops->ErrorRecoveryLevel == 2) &&
896*4882a593Smuzhiyun 	    !atomic_read(&sess->session_reinstatement) &&
897*4882a593Smuzhiyun 	    !atomic_read(&sess->session_fall_back_to_erl0))
898*4882a593Smuzhiyun 		iscsit_connection_recovery_transport_reset(conn);
899*4882a593Smuzhiyun 	else {
900*4882a593Smuzhiyun 		pr_debug("Performing cleanup for failed iSCSI"
901*4882a593Smuzhiyun 			" Connection ID: %hu from %s\n", conn->cid,
902*4882a593Smuzhiyun 			sess->sess_ops->InitiatorName);
903*4882a593Smuzhiyun 		iscsit_close_connection(conn);
904*4882a593Smuzhiyun 	}
905*4882a593Smuzhiyun }
906*4882a593Smuzhiyun 
iscsit_take_action_for_connection_exit(struct iscsi_conn * conn,bool * conn_freed)907*4882a593Smuzhiyun void iscsit_take_action_for_connection_exit(struct iscsi_conn *conn, bool *conn_freed)
908*4882a593Smuzhiyun {
909*4882a593Smuzhiyun 	*conn_freed = false;
910*4882a593Smuzhiyun 
911*4882a593Smuzhiyun 	spin_lock_bh(&conn->state_lock);
912*4882a593Smuzhiyun 	if (atomic_read(&conn->connection_exit)) {
913*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
914*4882a593Smuzhiyun 		return;
915*4882a593Smuzhiyun 	}
916*4882a593Smuzhiyun 	atomic_set(&conn->connection_exit, 1);
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun 	if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT) {
919*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
920*4882a593Smuzhiyun 		iscsit_close_connection(conn);
921*4882a593Smuzhiyun 		*conn_freed = true;
922*4882a593Smuzhiyun 		return;
923*4882a593Smuzhiyun 	}
924*4882a593Smuzhiyun 
925*4882a593Smuzhiyun 	if (conn->conn_state == TARG_CONN_STATE_CLEANUP_WAIT) {
926*4882a593Smuzhiyun 		spin_unlock_bh(&conn->state_lock);
927*4882a593Smuzhiyun 		return;
928*4882a593Smuzhiyun 	}
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
931*4882a593Smuzhiyun 	conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
932*4882a593Smuzhiyun 	spin_unlock_bh(&conn->state_lock);
933*4882a593Smuzhiyun 
934*4882a593Smuzhiyun 	iscsit_handle_connection_cleanup(conn);
935*4882a593Smuzhiyun 	*conn_freed = true;
936*4882a593Smuzhiyun }
937