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