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->dr_list);
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->dr_list, &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->dr_list);
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, dr_list) {
67 		list_del(&dr->dr_list);
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 	struct iscsi_datain_req *dr;
76 
77 	if (list_empty(&cmd->datain_list)) {
78 		pr_err("cmd->datain_list is empty for ITT:"
79 			" 0x%08x\n", cmd->init_task_tag);
80 		return NULL;
81 	}
82 	list_for_each_entry(dr, &cmd->datain_list, dr_list)
83 		break;
84 
85 	return dr;
86 }
87 
88 /*
89  *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
90  */
91 static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
92 	struct iscsi_cmd *cmd,
93 	struct iscsi_datain *datain)
94 {
95 	u32 next_burst_len, read_data_done, read_data_left;
96 	struct iscsi_conn *conn = cmd->conn;
97 	struct iscsi_datain_req *dr;
98 
99 	dr = iscsit_get_datain_req(cmd);
100 	if (!dr)
101 		return NULL;
102 
103 	if (dr->recovery && dr->generate_recovery_values) {
104 		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
105 					cmd, dr) < 0)
106 			return NULL;
107 
108 		dr->generate_recovery_values = 0;
109 	}
110 
111 	next_burst_len = (!dr->recovery) ?
112 			cmd->next_burst_len : dr->next_burst_len;
113 	read_data_done = (!dr->recovery) ?
114 			cmd->read_data_done : dr->read_data_done;
115 
116 	read_data_left = (cmd->data_length - read_data_done);
117 	if (!read_data_left) {
118 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
119 				cmd->init_task_tag);
120 		return NULL;
121 	}
122 
123 	if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
124 	    (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
125 	     next_burst_len))) {
126 		datain->length = read_data_left;
127 
128 		datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
129 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
130 			datain->flags |= ISCSI_FLAG_DATA_ACK;
131 	} else {
132 		if ((next_burst_len +
133 		     conn->conn_ops->MaxRecvDataSegmentLength) <
134 		     conn->sess->sess_ops->MaxBurstLength) {
135 			datain->length =
136 				conn->conn_ops->MaxRecvDataSegmentLength;
137 			next_burst_len += datain->length;
138 		} else {
139 			datain->length = (conn->sess->sess_ops->MaxBurstLength -
140 					  next_burst_len);
141 			next_burst_len = 0;
142 
143 			datain->flags |= ISCSI_FLAG_CMD_FINAL;
144 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
145 				datain->flags |= ISCSI_FLAG_DATA_ACK;
146 		}
147 	}
148 
149 	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
150 	datain->offset = read_data_done;
151 
152 	if (!dr->recovery) {
153 		cmd->next_burst_len = next_burst_len;
154 		cmd->read_data_done += datain->length;
155 	} else {
156 		dr->next_burst_len = next_burst_len;
157 		dr->read_data_done += datain->length;
158 	}
159 
160 	if (!dr->recovery) {
161 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
162 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
163 
164 		return dr;
165 	}
166 
167 	if (!dr->runlength) {
168 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
169 			dr->dr_complete =
170 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
171 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
172 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
173 		}
174 	} else {
175 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
176 			dr->dr_complete =
177 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
178 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
179 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
180 		}
181 	}
182 
183 	return dr;
184 }
185 
186 /*
187  *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
188  */
189 static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
190 	struct iscsi_cmd *cmd,
191 	struct iscsi_datain *datain)
192 {
193 	u32 offset, read_data_done, read_data_left, seq_send_order;
194 	struct iscsi_conn *conn = cmd->conn;
195 	struct iscsi_datain_req *dr;
196 	struct iscsi_seq *seq;
197 
198 	dr = iscsit_get_datain_req(cmd);
199 	if (!dr)
200 		return NULL;
201 
202 	if (dr->recovery && dr->generate_recovery_values) {
203 		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
204 					cmd, dr) < 0)
205 			return NULL;
206 
207 		dr->generate_recovery_values = 0;
208 	}
209 
210 	read_data_done = (!dr->recovery) ?
211 			cmd->read_data_done : dr->read_data_done;
212 	seq_send_order = (!dr->recovery) ?
213 			cmd->seq_send_order : dr->seq_send_order;
214 
215 	read_data_left = (cmd->data_length - read_data_done);
216 	if (!read_data_left) {
217 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
218 				cmd->init_task_tag);
219 		return NULL;
220 	}
221 
222 	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
223 	if (!seq)
224 		return NULL;
225 
226 	seq->sent = 1;
227 
228 	if (!dr->recovery && !seq->next_burst_len)
229 		seq->first_datasn = cmd->data_sn;
230 
231 	offset = (seq->offset + seq->next_burst_len);
232 
233 	if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
234 	     cmd->data_length) {
235 		datain->length = (cmd->data_length - offset);
236 		datain->offset = offset;
237 
238 		datain->flags |= ISCSI_FLAG_CMD_FINAL;
239 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
240 			datain->flags |= ISCSI_FLAG_DATA_ACK;
241 
242 		seq->next_burst_len = 0;
243 		seq_send_order++;
244 	} else {
245 		if ((seq->next_burst_len +
246 		     conn->conn_ops->MaxRecvDataSegmentLength) <
247 		     conn->sess->sess_ops->MaxBurstLength) {
248 			datain->length =
249 				conn->conn_ops->MaxRecvDataSegmentLength;
250 			datain->offset = (seq->offset + seq->next_burst_len);
251 
252 			seq->next_burst_len += datain->length;
253 		} else {
254 			datain->length = (conn->sess->sess_ops->MaxBurstLength -
255 					  seq->next_burst_len);
256 			datain->offset = (seq->offset + seq->next_burst_len);
257 
258 			datain->flags |= ISCSI_FLAG_CMD_FINAL;
259 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
260 				datain->flags |= ISCSI_FLAG_DATA_ACK;
261 
262 			seq->next_burst_len = 0;
263 			seq_send_order++;
264 		}
265 	}
266 
267 	if ((read_data_done + datain->length) == cmd->data_length)
268 		datain->flags |= ISCSI_FLAG_DATA_STATUS;
269 
270 	datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
271 	if (!dr->recovery) {
272 		cmd->seq_send_order = seq_send_order;
273 		cmd->read_data_done += datain->length;
274 	} else {
275 		dr->seq_send_order = seq_send_order;
276 		dr->read_data_done += datain->length;
277 	}
278 
279 	if (!dr->recovery) {
280 		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
281 			seq->last_datasn = datain->data_sn;
282 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
283 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
284 
285 		return dr;
286 	}
287 
288 	if (!dr->runlength) {
289 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
290 			dr->dr_complete =
291 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
292 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
293 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
294 		}
295 	} else {
296 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
297 			dr->dr_complete =
298 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
299 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
300 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
301 		}
302 	}
303 
304 	return dr;
305 }
306 
307 /*
308  *	For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
309  */
310 static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
311 	struct iscsi_cmd *cmd,
312 	struct iscsi_datain *datain)
313 {
314 	u32 next_burst_len, read_data_done, read_data_left;
315 	struct iscsi_conn *conn = cmd->conn;
316 	struct iscsi_datain_req *dr;
317 	struct iscsi_pdu *pdu;
318 
319 	dr = iscsit_get_datain_req(cmd);
320 	if (!dr)
321 		return NULL;
322 
323 	if (dr->recovery && dr->generate_recovery_values) {
324 		if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
325 					cmd, dr) < 0)
326 			return NULL;
327 
328 		dr->generate_recovery_values = 0;
329 	}
330 
331 	next_burst_len = (!dr->recovery) ?
332 			cmd->next_burst_len : dr->next_burst_len;
333 	read_data_done = (!dr->recovery) ?
334 			cmd->read_data_done : dr->read_data_done;
335 
336 	read_data_left = (cmd->data_length - read_data_done);
337 	if (!read_data_left) {
338 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
339 				cmd->init_task_tag);
340 		return dr;
341 	}
342 
343 	pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
344 	if (!pdu)
345 		return dr;
346 
347 	if ((read_data_done + pdu->length) == cmd->data_length) {
348 		pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
349 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
350 			pdu->flags |= ISCSI_FLAG_DATA_ACK;
351 
352 		next_burst_len = 0;
353 	} else {
354 		if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
355 		     conn->sess->sess_ops->MaxBurstLength)
356 			next_burst_len += pdu->length;
357 		else {
358 			pdu->flags |= ISCSI_FLAG_CMD_FINAL;
359 			if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
360 				pdu->flags |= ISCSI_FLAG_DATA_ACK;
361 
362 			next_burst_len = 0;
363 		}
364 	}
365 
366 	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
367 	if (!dr->recovery) {
368 		cmd->next_burst_len = next_burst_len;
369 		cmd->read_data_done += pdu->length;
370 	} else {
371 		dr->next_burst_len = next_burst_len;
372 		dr->read_data_done += pdu->length;
373 	}
374 
375 	datain->flags = pdu->flags;
376 	datain->length = pdu->length;
377 	datain->offset = pdu->offset;
378 	datain->data_sn = pdu->data_sn;
379 
380 	if (!dr->recovery) {
381 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
382 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
383 
384 		return dr;
385 	}
386 
387 	if (!dr->runlength) {
388 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
389 			dr->dr_complete =
390 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
391 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
392 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
393 		}
394 	} else {
395 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
396 			dr->dr_complete =
397 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
398 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
399 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
400 		}
401 	}
402 
403 	return dr;
404 }
405 
406 /*
407  *	For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
408  */
409 static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
410 	struct iscsi_cmd *cmd,
411 	struct iscsi_datain *datain)
412 {
413 	u32 read_data_done, read_data_left, seq_send_order;
414 	struct iscsi_conn *conn = cmd->conn;
415 	struct iscsi_datain_req *dr;
416 	struct iscsi_pdu *pdu;
417 	struct iscsi_seq *seq = NULL;
418 
419 	dr = iscsit_get_datain_req(cmd);
420 	if (!dr)
421 		return NULL;
422 
423 	if (dr->recovery && dr->generate_recovery_values) {
424 		if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
425 					cmd, dr) < 0)
426 			return NULL;
427 
428 		dr->generate_recovery_values = 0;
429 	}
430 
431 	read_data_done = (!dr->recovery) ?
432 			cmd->read_data_done : dr->read_data_done;
433 	seq_send_order = (!dr->recovery) ?
434 			cmd->seq_send_order : dr->seq_send_order;
435 
436 	read_data_left = (cmd->data_length - read_data_done);
437 	if (!read_data_left) {
438 		pr_err("ITT: 0x%08x read_data_left is zero!\n",
439 				cmd->init_task_tag);
440 		return NULL;
441 	}
442 
443 	seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
444 	if (!seq)
445 		return NULL;
446 
447 	seq->sent = 1;
448 
449 	if (!dr->recovery && !seq->next_burst_len)
450 		seq->first_datasn = cmd->data_sn;
451 
452 	pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
453 	if (!pdu)
454 		return NULL;
455 
456 	if (seq->pdu_send_order == seq->pdu_count) {
457 		pdu->flags |= ISCSI_FLAG_CMD_FINAL;
458 		if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
459 			pdu->flags |= ISCSI_FLAG_DATA_ACK;
460 
461 		seq->next_burst_len = 0;
462 		seq_send_order++;
463 	} else
464 		seq->next_burst_len += pdu->length;
465 
466 	if ((read_data_done + pdu->length) == cmd->data_length)
467 		pdu->flags |= ISCSI_FLAG_DATA_STATUS;
468 
469 	pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
470 	if (!dr->recovery) {
471 		cmd->seq_send_order = seq_send_order;
472 		cmd->read_data_done += pdu->length;
473 	} else {
474 		dr->seq_send_order = seq_send_order;
475 		dr->read_data_done += pdu->length;
476 	}
477 
478 	datain->flags = pdu->flags;
479 	datain->length = pdu->length;
480 	datain->offset = pdu->offset;
481 	datain->data_sn = pdu->data_sn;
482 
483 	if (!dr->recovery) {
484 		if (datain->flags & ISCSI_FLAG_CMD_FINAL)
485 			seq->last_datasn = datain->data_sn;
486 		if (datain->flags & ISCSI_FLAG_DATA_STATUS)
487 			dr->dr_complete = DATAIN_COMPLETE_NORMAL;
488 
489 		return dr;
490 	}
491 
492 	if (!dr->runlength) {
493 		if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
494 			dr->dr_complete =
495 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
496 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
497 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
498 		}
499 	} else {
500 		if ((dr->begrun + dr->runlength) == dr->data_sn) {
501 			dr->dr_complete =
502 			    (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
503 				DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
504 				DATAIN_COMPLETE_CONNECTION_RECOVERY;
505 		}
506 	}
507 
508 	return dr;
509 }
510 
511 struct iscsi_datain_req *iscsit_get_datain_values(
512 	struct iscsi_cmd *cmd,
513 	struct iscsi_datain *datain)
514 {
515 	struct iscsi_conn *conn = cmd->conn;
516 
517 	if (conn->sess->sess_ops->DataSequenceInOrder &&
518 	    conn->sess->sess_ops->DataPDUInOrder)
519 		return iscsit_set_datain_values_yes_and_yes(cmd, datain);
520 	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
521 		  conn->sess->sess_ops->DataPDUInOrder)
522 		return iscsit_set_datain_values_no_and_yes(cmd, datain);
523 	else if (conn->sess->sess_ops->DataSequenceInOrder &&
524 		 !conn->sess->sess_ops->DataPDUInOrder)
525 		return iscsit_set_datain_values_yes_and_no(cmd, datain);
526 	else if (!conn->sess->sess_ops->DataSequenceInOrder &&
527 		   !conn->sess->sess_ops->DataPDUInOrder)
528 		return iscsit_set_datain_values_no_and_no(cmd, datain);
529 
530 	return NULL;
531 }
532