1 /*******************************************************************************
2  * This file contains the iSCSI Target DataIN value generation functions.
3  *
4  * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
5  *
6  * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
7  *
8  * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  ******************************************************************************/
20 
21 #include <scsi/iscsi_proto.h>
22 
23 #include "iscsi_target_core.h"
24 #include "iscsi_target_seq_pdu_list.h"
25 #include "iscsi_target_erl1.h"
26 #include "iscsi_target_util.h"
27 #include "iscsi_target.h"
28 #include "iscsi_target_datain_values.h"
29 
30 struct iscsi_datain_req *iscsit_allocate_datain_req(void)
31 {
32 	struct iscsi_datain_req *dr;
33 
34 	dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);
35 	if (!dr) {
36 		pr_err("Unable to allocate memory for"
37 				" struct iscsi_datain_req\n");
38 		return NULL;
39 	}
40 	INIT_LIST_HEAD(&dr->cmd_datain_node);
41 
42 	return dr;
43 }
44 
45 void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
46 {
47 	spin_lock(&cmd->datain_lock);
48 	list_add_tail(&dr->cmd_datain_node, &cmd->datain_list);
49 	spin_unlock(&cmd->datain_lock);
50 }
51 
52 void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
53 {
54 	spin_lock(&cmd->datain_lock);
55 	list_del(&dr->cmd_datain_node);
56 	spin_unlock(&cmd->datain_lock);
57 
58 	kmem_cache_free(lio_dr_cache, dr);
59 }
60 
61 void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd)
62 {
63 	struct iscsi_datain_req *dr, *dr_tmp;
64 
65 	spin_lock(&cmd->datain_lock);
66 	list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, cmd_datain_node) {
67 		list_del(&dr->cmd_datain_node);
68 		kmem_cache_free(lio_dr_cache, dr);
69 	}
70 	spin_unlock(&cmd->datain_lock);
71 }
72 
73 struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
74 {
75 	if (list_empty(&cmd->datain_list)) {
76 		pr_err("cmd->datain_list is empty for ITT:"
77 			" 0x%08x\n", cmd->init_task_tag);
78 		return NULL;
79 	}
80 
81 	return list_first_entry(&cmd->datain_list, struct iscsi_datain_req,
82 				cmd_datain_node);
83 }
84 
85 /*
86  *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
87  */
88 static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
89 	struct iscsi_cmd *cmd,
90 	struct iscsi_datain *datain)
91 {
92 	u32 next_burst_len, read_data_done, read_data_left;
93 	struct iscsi_conn *conn = cmd->conn;
94 	struct iscsi_datain_req *dr;
95 
96 	dr = iscsit_get_datain_req(cmd);
97 	if (!dr)
98 		return NULL;
99 
100 	if (dr->recovery && dr->generate_recovery_values) {
101 		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
102 					cmd, dr) < 0)
103 			return NULL;
104 
105 		dr->generate_recovery_values = 0;
106 	}
107 
108 	next_burst_len = (!dr->recovery) ?
109 			cmd->next_burst_len : dr->next_burst_len;
110 	read_data_done = (!dr->recovery) ?
111 			cmd->read_data_done : dr->read_data_done;
112 
113 	read_data_left = (cmd->se_cmd.data_length - read_data_done);
114 	if (!read_data_left) {
115 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
116 				cmd->init_task_tag);
117 		return NULL;
118 	}
119 
120 	if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
121 	    (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
122 	     next_burst_len))) {
123 		datain->length = read_data_left;
124 
125 		datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
126 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
127 			datain->flags |= ISCSI_FLAG_DATA_ACK;
128 	} else {
129 		if ((next_burst_len +
130 		     conn->conn_ops->MaxRecvDataSegmentLength) <
131 		     conn->sess->sess_ops->MaxBurstLength) {
132 			datain->length =
133 				conn->conn_ops->MaxRecvDataSegmentLength;
134 			next_burst_len += datain->length;
135 		} else {
136 			datain->length = (conn->sess->sess_ops->MaxBurstLength -
137 					  next_burst_len);
138 			next_burst_len = 0;
139 
140 			datain->flags |= ISCSI_FLAG_CMD_FINAL;
141 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
142 				datain->flags |= ISCSI_FLAG_DATA_ACK;
143 		}
144 	}
145 
146 	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
147 	datain->offset = read_data_done;
148 
149 	if (!dr->recovery) {
150 		cmd->next_burst_len = next_burst_len;
151 		cmd->read_data_done += datain->length;
152 	} else {
153 		dr->next_burst_len = next_burst_len;
154 		dr->read_data_done += datain->length;
155 	}
156 
157 	if (!dr->recovery) {
158 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
159 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
160 
161 		return dr;
162 	}
163 
164 	if (!dr->runlength) {
165 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
166 			dr->dr_complete =
167 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
168 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
169 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
170 		}
171 	} else {
172 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
173 			dr->dr_complete =
174 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
175 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
176 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
177 		}
178 	}
179 
180 	return dr;
181 }
182 
183 /*
184  *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
185  */
186 static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
187 	struct iscsi_cmd *cmd,
188 	struct iscsi_datain *datain)
189 {
190 	u32 offset, read_data_done, read_data_left, seq_send_order;
191 	struct iscsi_conn *conn = cmd->conn;
192 	struct iscsi_datain_req *dr;
193 	struct iscsi_seq *seq;
194 
195 	dr = iscsit_get_datain_req(cmd);
196 	if (!dr)
197 		return NULL;
198 
199 	if (dr->recovery && dr->generate_recovery_values) {
200 		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
201 					cmd, dr) < 0)
202 			return NULL;
203 
204 		dr->generate_recovery_values = 0;
205 	}
206 
207 	read_data_done = (!dr->recovery) ?
208 			cmd->read_data_done : dr->read_data_done;
209 	seq_send_order = (!dr->recovery) ?
210 			cmd->seq_send_order : dr->seq_send_order;
211 
212 	read_data_left = (cmd->se_cmd.data_length - read_data_done);
213 	if (!read_data_left) {
214 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
215 				cmd->init_task_tag);
216 		return NULL;
217 	}
218 
219 	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
220 	if (!seq)
221 		return NULL;
222 
223 	seq->sent = 1;
224 
225 	if (!dr->recovery && !seq->next_burst_len)
226 		seq->first_datasn = cmd->data_sn;
227 
228 	offset = (seq->offset + seq->next_burst_len);
229 
230 	if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
231 	     cmd->se_cmd.data_length) {
232 		datain->length = (cmd->se_cmd.data_length - offset);
233 		datain->offset = offset;
234 
235 		datain->flags |= ISCSI_FLAG_CMD_FINAL;
236 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
237 			datain->flags |= ISCSI_FLAG_DATA_ACK;
238 
239 		seq->next_burst_len = 0;
240 		seq_send_order++;
241 	} else {
242 		if ((seq->next_burst_len +
243 		     conn->conn_ops->MaxRecvDataSegmentLength) <
244 		     conn->sess->sess_ops->MaxBurstLength) {
245 			datain->length =
246 				conn->conn_ops->MaxRecvDataSegmentLength;
247 			datain->offset = (seq->offset + seq->next_burst_len);
248 
249 			seq->next_burst_len += datain->length;
250 		} else {
251 			datain->length = (conn->sess->sess_ops->MaxBurstLength -
252 					  seq->next_burst_len);
253 			datain->offset = (seq->offset + seq->next_burst_len);
254 
255 			datain->flags |= ISCSI_FLAG_CMD_FINAL;
256 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
257 				datain->flags |= ISCSI_FLAG_DATA_ACK;
258 
259 			seq->next_burst_len = 0;
260 			seq_send_order++;
261 		}
262 	}
263 
264 	if ((read_data_done + datain->length) == cmd->se_cmd.data_length)
265 		datain->flags |= ISCSI_FLAG_DATA_STATUS;
266 
267 	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
268 	if (!dr->recovery) {
269 		cmd->seq_send_order = seq_send_order;
270 		cmd->read_data_done += datain->length;
271 	} else {
272 		dr->seq_send_order = seq_send_order;
273 		dr->read_data_done += datain->length;
274 	}
275 
276 	if (!dr->recovery) {
277 		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
278 			seq->last_datasn = datain->data_sn;
279 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
280 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
281 
282 		return dr;
283 	}
284 
285 	if (!dr->runlength) {
286 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
287 			dr->dr_complete =
288 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
289 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
290 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
291 		}
292 	} else {
293 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
294 			dr->dr_complete =
295 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
296 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
297 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
298 		}
299 	}
300 
301 	return dr;
302 }
303 
304 /*
305  *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
306  */
307 static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
308 	struct iscsi_cmd *cmd,
309 	struct iscsi_datain *datain)
310 {
311 	u32 next_burst_len, read_data_done, read_data_left;
312 	struct iscsi_conn *conn = cmd->conn;
313 	struct iscsi_datain_req *dr;
314 	struct iscsi_pdu *pdu;
315 
316 	dr = iscsit_get_datain_req(cmd);
317 	if (!dr)
318 		return NULL;
319 
320 	if (dr->recovery && dr->generate_recovery_values) {
321 		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
322 					cmd, dr) < 0)
323 			return NULL;
324 
325 		dr->generate_recovery_values = 0;
326 	}
327 
328 	next_burst_len = (!dr->recovery) ?
329 			cmd->next_burst_len : dr->next_burst_len;
330 	read_data_done = (!dr->recovery) ?
331 			cmd->read_data_done : dr->read_data_done;
332 
333 	read_data_left = (cmd->se_cmd.data_length - read_data_done);
334 	if (!read_data_left) {
335 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
336 				cmd->init_task_tag);
337 		return dr;
338 	}
339 
340 	pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
341 	if (!pdu)
342 		return dr;
343 
344 	if ((read_data_done + pdu->length) == cmd->se_cmd.data_length) {
345 		pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
346 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
347 			pdu->flags |= ISCSI_FLAG_DATA_ACK;
348 
349 		next_burst_len = 0;
350 	} else {
351 		if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
352 		     conn->sess->sess_ops->MaxBurstLength)
353 			next_burst_len += pdu->length;
354 		else {
355 			pdu->flags |= ISCSI_FLAG_CMD_FINAL;
356 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
357 				pdu->flags |= ISCSI_FLAG_DATA_ACK;
358 
359 			next_burst_len = 0;
360 		}
361 	}
362 
363 	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
364 	if (!dr->recovery) {
365 		cmd->next_burst_len = next_burst_len;
366 		cmd->read_data_done += pdu->length;
367 	} else {
368 		dr->next_burst_len = next_burst_len;
369 		dr->read_data_done += pdu->length;
370 	}
371 
372 	datain->flags = pdu->flags;
373 	datain->length = pdu->length;
374 	datain->offset = pdu->offset;
375 	datain->data_sn = pdu->data_sn;
376 
377 	if (!dr->recovery) {
378 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
379 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
380 
381 		return dr;
382 	}
383 
384 	if (!dr->runlength) {
385 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
386 			dr->dr_complete =
387 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
388 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
389 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
390 		}
391 	} else {
392 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
393 			dr->dr_complete =
394 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
395 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
396 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
397 		}
398 	}
399 
400 	return dr;
401 }
402 
403 /*
404  *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
405  */
406 static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
407 	struct iscsi_cmd *cmd,
408 	struct iscsi_datain *datain)
409 {
410 	u32 read_data_done, read_data_left, seq_send_order;
411 	struct iscsi_conn *conn = cmd->conn;
412 	struct iscsi_datain_req *dr;
413 	struct iscsi_pdu *pdu;
414 	struct iscsi_seq *seq = NULL;
415 
416 	dr = iscsit_get_datain_req(cmd);
417 	if (!dr)
418 		return NULL;
419 
420 	if (dr->recovery && dr->generate_recovery_values) {
421 		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
422 					cmd, dr) < 0)
423 			return NULL;
424 
425 		dr->generate_recovery_values = 0;
426 	}
427 
428 	read_data_done = (!dr->recovery) ?
429 			cmd->read_data_done : dr->read_data_done;
430 	seq_send_order = (!dr->recovery) ?
431 			cmd->seq_send_order : dr->seq_send_order;
432 
433 	read_data_left = (cmd->se_cmd.data_length - read_data_done);
434 	if (!read_data_left) {
435 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
436 				cmd->init_task_tag);
437 		return NULL;
438 	}
439 
440 	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
441 	if (!seq)
442 		return NULL;
443 
444 	seq->sent = 1;
445 
446 	if (!dr->recovery && !seq->next_burst_len)
447 		seq->first_datasn = cmd->data_sn;
448 
449 	pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
450 	if (!pdu)
451 		return NULL;
452 
453 	if (seq->pdu_send_order == seq->pdu_count) {
454 		pdu->flags |= ISCSI_FLAG_CMD_FINAL;
455 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
456 			pdu->flags |= ISCSI_FLAG_DATA_ACK;
457 
458 		seq->next_burst_len = 0;
459 		seq_send_order++;
460 	} else
461 		seq->next_burst_len += pdu->length;
462 
463 	if ((read_data_done + pdu->length) == cmd->se_cmd.data_length)
464 		pdu->flags |= ISCSI_FLAG_DATA_STATUS;
465 
466 	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
467 	if (!dr->recovery) {
468 		cmd->seq_send_order = seq_send_order;
469 		cmd->read_data_done += pdu->length;
470 	} else {
471 		dr->seq_send_order = seq_send_order;
472 		dr->read_data_done += pdu->length;
473 	}
474 
475 	datain->flags = pdu->flags;
476 	datain->length = pdu->length;
477 	datain->offset = pdu->offset;
478 	datain->data_sn = pdu->data_sn;
479 
480 	if (!dr->recovery) {
481 		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
482 			seq->last_datasn = datain->data_sn;
483 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
484 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
485 
486 		return dr;
487 	}
488 
489 	if (!dr->runlength) {
490 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
491 			dr->dr_complete =
492 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
493 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
494 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
495 		}
496 	} else {
497 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
498 			dr->dr_complete =
499 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
500 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
501 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
502 		}
503 	}
504 
505 	return dr;
506 }
507 
508 struct iscsi_datain_req *iscsit_get_datain_values(
509 	struct iscsi_cmd *cmd,
510 	struct iscsi_datain *datain)
511 {
512 	struct iscsi_conn *conn = cmd->conn;
513 
514 	if (conn->sess->sess_ops->DataSequenceInOrder &&
515 	    conn->sess->sess_ops->DataPDUInOrder)
516 		return iscsit_set_datain_values_yes_and_yes(cmd, datain);
517 	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
518 		  conn->sess->sess_ops->DataPDUInOrder)
519 		return iscsit_set_datain_values_no_and_yes(cmd, datain);
520 	else if (conn->sess->sess_ops->DataSequenceInOrder &&
521 		 !conn->sess->sess_ops->DataPDUInOrder)
522 		return iscsit_set_datain_values_yes_and_no(cmd, datain);
523 	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
524 		   !conn->sess->sess_ops->DataPDUInOrder)
525 		return iscsit_set_datain_values_no_and_no(cmd, datain);
526 
527 	return NULL;
528 }
529