1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /******************************************************************************* |
3 | * This file contains the iSCSI Target specific Task Management functions. |
4 | * |
5 | * (c) Copyright 2007-2013 Datera, Inc. |
6 | * |
7 | * Author: Nicholas A. Bellinger <nab@linux-iscsi.org> |
8 | * |
9 | ******************************************************************************/ |
10 | |
11 | #include <asm/unaligned.h> |
12 | #include <scsi/scsi_proto.h> |
13 | #include <scsi/iscsi_proto.h> |
14 | #include <target/target_core_base.h> |
15 | #include <target/target_core_fabric.h> |
16 | #include <target/iscsi/iscsi_transport.h> |
17 | |
18 | #include <target/iscsi/iscsi_target_core.h> |
19 | #include "iscsi_target_seq_pdu_list.h" |
20 | #include "iscsi_target_datain_values.h" |
21 | #include "iscsi_target_device.h" |
22 | #include "iscsi_target_erl0.h" |
23 | #include "iscsi_target_erl1.h" |
24 | #include "iscsi_target_erl2.h" |
25 | #include "iscsi_target_tmr.h" |
26 | #include "iscsi_target_tpg.h" |
27 | #include "iscsi_target_util.h" |
28 | #include "iscsi_target.h" |
29 | |
30 | u8 iscsit_tmr_abort_task( |
31 | struct iscsit_cmd *cmd, |
32 | unsigned char *buf) |
33 | { |
34 | struct iscsit_cmd *ref_cmd; |
35 | struct iscsit_conn *conn = cmd->conn; |
36 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; |
37 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; |
38 | struct iscsi_tm *hdr = (struct iscsi_tm *) buf; |
39 | |
40 | ref_cmd = iscsit_find_cmd_from_itt(conn, hdr->rtt); |
41 | if (!ref_cmd) { |
42 | pr_err("Unable to locate RefTaskTag: 0x%08x on CID:" |
43 | " %hu.\n" , hdr->rtt, conn->cid); |
44 | return (iscsi_sna_gte(be32_to_cpu(hdr->refcmdsn), n2: conn->sess->exp_cmd_sn) && |
45 | iscsi_sna_lte(be32_to_cpu(hdr->refcmdsn), n2: (u32) atomic_read(v: &conn->sess->max_cmd_sn))) ? |
46 | ISCSI_TMF_RSP_COMPLETE : ISCSI_TMF_RSP_NO_TASK; |
47 | } |
48 | if (ref_cmd->cmd_sn != be32_to_cpu(hdr->refcmdsn)) { |
49 | pr_err("RefCmdSN 0x%08x does not equal" |
50 | " task's CmdSN 0x%08x. Rejecting ABORT_TASK.\n" , |
51 | hdr->refcmdsn, ref_cmd->cmd_sn); |
52 | return ISCSI_TMF_RSP_REJECTED; |
53 | } |
54 | |
55 | se_tmr->ref_task_tag = (__force u32)hdr->rtt; |
56 | tmr_req->ref_cmd = ref_cmd; |
57 | tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); |
58 | |
59 | return ISCSI_TMF_RSP_COMPLETE; |
60 | } |
61 | |
62 | /* |
63 | * Called from iscsit_handle_task_mgt_cmd(). |
64 | */ |
65 | int iscsit_tmr_task_warm_reset( |
66 | struct iscsit_conn *conn, |
67 | struct iscsi_tmr_req *tmr_req, |
68 | unsigned char *buf) |
69 | { |
70 | struct iscsit_session *sess = conn->sess; |
71 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); |
72 | |
73 | if (!na->tmr_warm_reset) { |
74 | pr_err("TMR Opcode TARGET_WARM_RESET authorization" |
75 | " failed for Initiator Node: %s\n" , |
76 | sess->se_sess->se_node_acl->initiatorname); |
77 | return -1; |
78 | } |
79 | /* |
80 | * Do the real work in transport_generic_do_tmr(). |
81 | */ |
82 | return 0; |
83 | } |
84 | |
85 | int iscsit_tmr_task_cold_reset( |
86 | struct iscsit_conn *conn, |
87 | struct iscsi_tmr_req *tmr_req, |
88 | unsigned char *buf) |
89 | { |
90 | struct iscsit_session *sess = conn->sess; |
91 | struct iscsi_node_attrib *na = iscsit_tpg_get_node_attrib(sess); |
92 | |
93 | if (!na->tmr_cold_reset) { |
94 | pr_err("TMR Opcode TARGET_COLD_RESET authorization" |
95 | " failed for Initiator Node: %s\n" , |
96 | sess->se_sess->se_node_acl->initiatorname); |
97 | return -1; |
98 | } |
99 | /* |
100 | * Do the real work in transport_generic_do_tmr(). |
101 | */ |
102 | return 0; |
103 | } |
104 | |
105 | u8 iscsit_tmr_task_reassign( |
106 | struct iscsit_cmd *cmd, |
107 | unsigned char *buf) |
108 | { |
109 | struct iscsit_cmd *ref_cmd = NULL; |
110 | struct iscsit_conn *conn = cmd->conn; |
111 | struct iscsi_conn_recovery *cr = NULL; |
112 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; |
113 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; |
114 | struct iscsi_tm *hdr = (struct iscsi_tm *) buf; |
115 | u64 ret, ref_lun; |
116 | |
117 | pr_debug("Got TASK_REASSIGN TMR ITT: 0x%08x," |
118 | " RefTaskTag: 0x%08x, ExpDataSN: 0x%08x, CID: %hu\n" , |
119 | hdr->itt, hdr->rtt, hdr->exp_datasn, conn->cid); |
120 | |
121 | if (conn->sess->sess_ops->ErrorRecoveryLevel != 2) { |
122 | pr_err("TMR TASK_REASSIGN not supported in ERL<2," |
123 | " ignoring request.\n" ); |
124 | return ISCSI_TMF_RSP_NOT_SUPPORTED; |
125 | } |
126 | |
127 | ret = iscsit_find_cmd_for_recovery(conn->sess, &ref_cmd, &cr, hdr->rtt); |
128 | if (ret == -2) { |
129 | pr_err("Command ITT: 0x%08x is still alligent to CID:" |
130 | " %hu\n" , ref_cmd->init_task_tag, cr->cid); |
131 | return ISCSI_TMF_RSP_TASK_ALLEGIANT; |
132 | } else if (ret == -1) { |
133 | pr_err("Unable to locate RefTaskTag: 0x%08x in" |
134 | " connection recovery command list.\n" , hdr->rtt); |
135 | return ISCSI_TMF_RSP_NO_TASK; |
136 | } |
137 | /* |
138 | * Temporary check to prevent connection recovery for |
139 | * connections with a differing Max*DataSegmentLength. |
140 | */ |
141 | if (cr->maxrecvdatasegmentlength != |
142 | conn->conn_ops->MaxRecvDataSegmentLength) { |
143 | pr_err("Unable to perform connection recovery for" |
144 | " differing MaxRecvDataSegmentLength, rejecting" |
145 | " TMR TASK_REASSIGN.\n" ); |
146 | return ISCSI_TMF_RSP_REJECTED; |
147 | } |
148 | if (cr->maxxmitdatasegmentlength != |
149 | conn->conn_ops->MaxXmitDataSegmentLength) { |
150 | pr_err("Unable to perform connection recovery for" |
151 | " differing MaxXmitDataSegmentLength, rejecting" |
152 | " TMR TASK_REASSIGN.\n" ); |
153 | return ISCSI_TMF_RSP_REJECTED; |
154 | } |
155 | |
156 | ref_lun = scsilun_to_int(&hdr->lun); |
157 | if (ref_lun != ref_cmd->se_cmd.orig_fe_lun) { |
158 | pr_err("Unable to perform connection recovery for" |
159 | " differing ref_lun: %llu ref_cmd orig_fe_lun: %llu\n" , |
160 | ref_lun, ref_cmd->se_cmd.orig_fe_lun); |
161 | return ISCSI_TMF_RSP_REJECTED; |
162 | } |
163 | |
164 | se_tmr->ref_task_tag = (__force u32)hdr->rtt; |
165 | tmr_req->ref_cmd = ref_cmd; |
166 | tmr_req->exp_data_sn = be32_to_cpu(hdr->exp_datasn); |
167 | tmr_req->conn_recovery = cr; |
168 | tmr_req->task_reassign = 1; |
169 | /* |
170 | * Command can now be reassigned to a new connection. |
171 | * The task management response must be sent before the |
172 | * reassignment actually happens. See iscsi_tmr_post_handler(). |
173 | */ |
174 | return ISCSI_TMF_RSP_COMPLETE; |
175 | } |
176 | |
177 | static void iscsit_task_reassign_remove_cmd( |
178 | struct iscsit_cmd *cmd, |
179 | struct iscsi_conn_recovery *cr, |
180 | struct iscsit_session *sess) |
181 | { |
182 | int ret; |
183 | |
184 | spin_lock(lock: &cr->conn_recovery_cmd_lock); |
185 | ret = iscsit_remove_cmd_from_connection_recovery(cmd, sess); |
186 | spin_unlock(lock: &cr->conn_recovery_cmd_lock); |
187 | if (!ret) { |
188 | pr_debug("iSCSI connection recovery successful for CID:" |
189 | " %hu on SID: %u\n" , cr->cid, sess->sid); |
190 | iscsit_remove_active_connection_recovery_entry(cr, sess); |
191 | } |
192 | } |
193 | |
194 | static int iscsit_task_reassign_complete_nop_out( |
195 | struct iscsi_tmr_req *tmr_req, |
196 | struct iscsit_conn *conn) |
197 | { |
198 | struct iscsit_cmd *cmd = tmr_req->ref_cmd; |
199 | struct iscsi_conn_recovery *cr; |
200 | |
201 | if (!cmd->cr) { |
202 | pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" |
203 | " is NULL!\n" , cmd->init_task_tag); |
204 | return -1; |
205 | } |
206 | cr = cmd->cr; |
207 | |
208 | /* |
209 | * Reset the StatSN so a new one for this commands new connection |
210 | * will be assigned. |
211 | * Reset the ExpStatSN as well so we may receive Status SNACKs. |
212 | */ |
213 | cmd->stat_sn = cmd->exp_stat_sn = 0; |
214 | |
215 | iscsit_task_reassign_remove_cmd(cmd, cr, sess: conn->sess); |
216 | |
217 | spin_lock_bh(lock: &conn->cmd_lock); |
218 | list_add_tail(new: &cmd->i_conn_node, head: &conn->conn_cmd_list); |
219 | spin_unlock_bh(lock: &conn->cmd_lock); |
220 | |
221 | cmd->i_state = ISTATE_SEND_NOPIN; |
222 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); |
223 | return 0; |
224 | } |
225 | |
226 | static int iscsit_task_reassign_complete_write( |
227 | struct iscsit_cmd *cmd, |
228 | struct iscsi_tmr_req *tmr_req) |
229 | { |
230 | int no_build_r2ts = 0; |
231 | u32 length = 0, offset = 0; |
232 | struct iscsit_conn *conn = cmd->conn; |
233 | struct se_cmd *se_cmd = &cmd->se_cmd; |
234 | /* |
235 | * The Initiator must not send a R2T SNACK with a Begrun less than |
236 | * the TMR TASK_REASSIGN's ExpDataSN. |
237 | */ |
238 | if (!tmr_req->exp_data_sn) { |
239 | cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; |
240 | cmd->acked_data_sn = 0; |
241 | } else { |
242 | cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; |
243 | cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); |
244 | } |
245 | |
246 | /* |
247 | * The TMR TASK_REASSIGN's ExpDataSN contains the next R2TSN the |
248 | * Initiator is expecting. The Target controls all WRITE operations |
249 | * so if we have received all DataOUT we can safety ignore Initiator. |
250 | */ |
251 | if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) { |
252 | if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { |
253 | pr_debug("WRITE ITT: 0x%08x: t_state: %d" |
254 | " never sent to transport\n" , |
255 | cmd->init_task_tag, cmd->se_cmd.t_state); |
256 | target_execute_cmd(cmd: se_cmd); |
257 | return 0; |
258 | } |
259 | |
260 | cmd->i_state = ISTATE_SEND_STATUS; |
261 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); |
262 | return 0; |
263 | } |
264 | |
265 | /* |
266 | * Special case to deal with DataSequenceInOrder=No and Non-Immeidate |
267 | * Unsolicited DataOut. |
268 | */ |
269 | if (cmd->unsolicited_data) { |
270 | cmd->unsolicited_data = 0; |
271 | |
272 | offset = cmd->next_burst_len = cmd->write_data_done; |
273 | |
274 | if ((conn->sess->sess_ops->FirstBurstLength - offset) >= |
275 | cmd->se_cmd.data_length) { |
276 | no_build_r2ts = 1; |
277 | length = (cmd->se_cmd.data_length - offset); |
278 | } else |
279 | length = (conn->sess->sess_ops->FirstBurstLength - offset); |
280 | |
281 | spin_lock_bh(lock: &cmd->r2t_lock); |
282 | if (iscsit_add_r2t_to_list(cmd, offset, length, 0, 0) < 0) { |
283 | spin_unlock_bh(lock: &cmd->r2t_lock); |
284 | return -1; |
285 | } |
286 | cmd->outstanding_r2ts++; |
287 | spin_unlock_bh(lock: &cmd->r2t_lock); |
288 | |
289 | if (no_build_r2ts) |
290 | return 0; |
291 | } |
292 | /* |
293 | * iscsit_build_r2ts_for_cmd() can handle the rest from here. |
294 | */ |
295 | return conn->conn_transport->iscsit_get_dataout(conn, cmd, true); |
296 | } |
297 | |
298 | static int iscsit_task_reassign_complete_read( |
299 | struct iscsit_cmd *cmd, |
300 | struct iscsi_tmr_req *tmr_req) |
301 | { |
302 | struct iscsit_conn *conn = cmd->conn; |
303 | struct iscsi_datain_req *dr; |
304 | struct se_cmd *se_cmd = &cmd->se_cmd; |
305 | /* |
306 | * The Initiator must not send a Data SNACK with a BegRun less than |
307 | * the TMR TASK_REASSIGN's ExpDataSN. |
308 | */ |
309 | if (!tmr_req->exp_data_sn) { |
310 | cmd->cmd_flags &= ~ICF_GOT_DATACK_SNACK; |
311 | cmd->acked_data_sn = 0; |
312 | } else { |
313 | cmd->cmd_flags |= ICF_GOT_DATACK_SNACK; |
314 | cmd->acked_data_sn = (tmr_req->exp_data_sn - 1); |
315 | } |
316 | |
317 | if (!(cmd->se_cmd.transport_state & CMD_T_SENT)) { |
318 | pr_debug("READ ITT: 0x%08x: t_state: %d never sent to" |
319 | " transport\n" , cmd->init_task_tag, |
320 | cmd->se_cmd.t_state); |
321 | target_submit(se_cmd); |
322 | return 0; |
323 | } |
324 | |
325 | if (!(se_cmd->transport_state & CMD_T_COMPLETE)) { |
326 | pr_err("READ ITT: 0x%08x: t_state: %d, never returned" |
327 | " from transport\n" , cmd->init_task_tag, |
328 | cmd->se_cmd.t_state); |
329 | return -1; |
330 | } |
331 | |
332 | dr = iscsit_allocate_datain_req(); |
333 | if (!dr) |
334 | return -1; |
335 | /* |
336 | * The TMR TASK_REASSIGN's ExpDataSN contains the next DataSN the |
337 | * Initiator is expecting. |
338 | */ |
339 | dr->data_sn = dr->begrun = tmr_req->exp_data_sn; |
340 | dr->runlength = 0; |
341 | dr->generate_recovery_values = 1; |
342 | dr->recovery = DATAIN_CONNECTION_RECOVERY; |
343 | |
344 | iscsit_attach_datain_req(cmd, dr); |
345 | |
346 | cmd->i_state = ISTATE_SEND_DATAIN; |
347 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); |
348 | return 0; |
349 | } |
350 | |
351 | static int iscsit_task_reassign_complete_none( |
352 | struct iscsit_cmd *cmd, |
353 | struct iscsi_tmr_req *tmr_req) |
354 | { |
355 | struct iscsit_conn *conn = cmd->conn; |
356 | |
357 | cmd->i_state = ISTATE_SEND_STATUS; |
358 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); |
359 | return 0; |
360 | } |
361 | |
362 | static int iscsit_task_reassign_complete_scsi_cmnd( |
363 | struct iscsi_tmr_req *tmr_req, |
364 | struct iscsit_conn *conn) |
365 | { |
366 | struct iscsit_cmd *cmd = tmr_req->ref_cmd; |
367 | struct iscsi_conn_recovery *cr; |
368 | |
369 | if (!cmd->cr) { |
370 | pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x" |
371 | " is NULL!\n" , cmd->init_task_tag); |
372 | return -1; |
373 | } |
374 | cr = cmd->cr; |
375 | |
376 | /* |
377 | * Reset the StatSN so a new one for this commands new connection |
378 | * will be assigned. |
379 | * Reset the ExpStatSN as well so we may receive Status SNACKs. |
380 | */ |
381 | cmd->stat_sn = cmd->exp_stat_sn = 0; |
382 | |
383 | iscsit_task_reassign_remove_cmd(cmd, cr, sess: conn->sess); |
384 | |
385 | spin_lock_bh(lock: &conn->cmd_lock); |
386 | list_add_tail(new: &cmd->i_conn_node, head: &conn->conn_cmd_list); |
387 | spin_unlock_bh(lock: &conn->cmd_lock); |
388 | |
389 | if (cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) { |
390 | cmd->i_state = ISTATE_SEND_STATUS; |
391 | iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state); |
392 | return 0; |
393 | } |
394 | |
395 | switch (cmd->data_direction) { |
396 | case DMA_TO_DEVICE: |
397 | return iscsit_task_reassign_complete_write(cmd, tmr_req); |
398 | case DMA_FROM_DEVICE: |
399 | return iscsit_task_reassign_complete_read(cmd, tmr_req); |
400 | case DMA_NONE: |
401 | return iscsit_task_reassign_complete_none(cmd, tmr_req); |
402 | default: |
403 | pr_err("Unknown cmd->data_direction: 0x%02x\n" , |
404 | cmd->data_direction); |
405 | return -1; |
406 | } |
407 | |
408 | return 0; |
409 | } |
410 | |
411 | static int iscsit_task_reassign_complete( |
412 | struct iscsi_tmr_req *tmr_req, |
413 | struct iscsit_conn *conn) |
414 | { |
415 | struct iscsit_cmd *cmd; |
416 | int ret = 0; |
417 | |
418 | if (!tmr_req->ref_cmd) { |
419 | pr_err("TMR Request is missing a RefCmd struct iscsit_cmd.\n" ); |
420 | return -1; |
421 | } |
422 | cmd = tmr_req->ref_cmd; |
423 | |
424 | cmd->conn = conn; |
425 | |
426 | switch (cmd->iscsi_opcode) { |
427 | case ISCSI_OP_NOOP_OUT: |
428 | ret = iscsit_task_reassign_complete_nop_out(tmr_req, conn); |
429 | break; |
430 | case ISCSI_OP_SCSI_CMD: |
431 | ret = iscsit_task_reassign_complete_scsi_cmnd(tmr_req, conn); |
432 | break; |
433 | default: |
434 | pr_err("Illegal iSCSI Opcode 0x%02x during" |
435 | " command reallegiance\n" , cmd->iscsi_opcode); |
436 | return -1; |
437 | } |
438 | |
439 | if (ret != 0) |
440 | return ret; |
441 | |
442 | pr_debug("Completed connection reallegiance for Opcode: 0x%02x," |
443 | " ITT: 0x%08x to CID: %hu.\n" , cmd->iscsi_opcode, |
444 | cmd->init_task_tag, conn->cid); |
445 | |
446 | return 0; |
447 | } |
448 | |
449 | /* |
450 | * Handles special after-the-fact actions related to TMRs. |
451 | * Right now the only one that its really needed for is |
452 | * connection recovery releated TASK_REASSIGN. |
453 | */ |
454 | int iscsit_tmr_post_handler(struct iscsit_cmd *cmd, struct iscsit_conn *conn) |
455 | { |
456 | struct iscsi_tmr_req *tmr_req = cmd->tmr_req; |
457 | struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req; |
458 | |
459 | if (tmr_req->task_reassign && |
460 | (se_tmr->response == ISCSI_TMF_RSP_COMPLETE)) |
461 | return iscsit_task_reassign_complete(tmr_req, conn); |
462 | |
463 | return 0; |
464 | } |
465 | EXPORT_SYMBOL(iscsit_tmr_post_handler); |
466 | |
467 | /* |
468 | * Nothing to do here, but leave it for good measure. :-) |
469 | */ |
470 | static int iscsit_task_reassign_prepare_read( |
471 | struct iscsi_tmr_req *tmr_req, |
472 | struct iscsit_conn *conn) |
473 | { |
474 | return 0; |
475 | } |
476 | |
477 | static void iscsit_task_reassign_prepare_unsolicited_dataout( |
478 | struct iscsit_cmd *cmd, |
479 | struct iscsit_conn *conn) |
480 | { |
481 | int i, j; |
482 | struct iscsi_pdu *pdu = NULL; |
483 | struct iscsi_seq *seq = NULL; |
484 | |
485 | if (conn->sess->sess_ops->DataSequenceInOrder) { |
486 | cmd->data_sn = 0; |
487 | |
488 | if (cmd->immediate_data) |
489 | cmd->r2t_offset += (cmd->first_burst_len - |
490 | cmd->seq_start_offset); |
491 | |
492 | if (conn->sess->sess_ops->DataPDUInOrder) { |
493 | cmd->write_data_done -= (cmd->immediate_data) ? |
494 | (cmd->first_burst_len - |
495 | cmd->seq_start_offset) : |
496 | cmd->first_burst_len; |
497 | cmd->first_burst_len = 0; |
498 | return; |
499 | } |
500 | |
501 | for (i = 0; i < cmd->pdu_count; i++) { |
502 | pdu = &cmd->pdu_list[i]; |
503 | |
504 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) |
505 | continue; |
506 | |
507 | if ((pdu->offset >= cmd->seq_start_offset) && |
508 | ((pdu->offset + pdu->length) <= |
509 | cmd->seq_end_offset)) { |
510 | cmd->first_burst_len -= pdu->length; |
511 | cmd->write_data_done -= pdu->length; |
512 | pdu->status = ISCSI_PDU_NOT_RECEIVED; |
513 | } |
514 | } |
515 | } else { |
516 | for (i = 0; i < cmd->seq_count; i++) { |
517 | seq = &cmd->seq_list[i]; |
518 | |
519 | if (seq->type != SEQTYPE_UNSOLICITED) |
520 | continue; |
521 | |
522 | cmd->write_data_done -= |
523 | (seq->offset - seq->orig_offset); |
524 | cmd->first_burst_len = 0; |
525 | seq->data_sn = 0; |
526 | seq->offset = seq->orig_offset; |
527 | seq->next_burst_len = 0; |
528 | seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; |
529 | |
530 | if (conn->sess->sess_ops->DataPDUInOrder) |
531 | continue; |
532 | |
533 | for (j = 0; j < seq->pdu_count; j++) { |
534 | pdu = &cmd->pdu_list[j+seq->pdu_start]; |
535 | |
536 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) |
537 | continue; |
538 | |
539 | pdu->status = ISCSI_PDU_NOT_RECEIVED; |
540 | } |
541 | } |
542 | } |
543 | } |
544 | |
545 | static int iscsit_task_reassign_prepare_write( |
546 | struct iscsi_tmr_req *tmr_req, |
547 | struct iscsit_conn *conn) |
548 | { |
549 | struct iscsit_cmd *cmd = tmr_req->ref_cmd; |
550 | struct iscsi_pdu *pdu = NULL; |
551 | struct iscsi_r2t *r2t = NULL, *r2t_tmp; |
552 | int first_incomplete_r2t = 1, i = 0; |
553 | |
554 | /* |
555 | * The command was in the process of receiving Unsolicited DataOUT when |
556 | * the connection failed. |
557 | */ |
558 | if (cmd->unsolicited_data) |
559 | iscsit_task_reassign_prepare_unsolicited_dataout(cmd, conn); |
560 | |
561 | /* |
562 | * The Initiator is requesting R2Ts starting from zero, skip |
563 | * checking acknowledged R2Ts and start checking struct iscsi_r2ts |
564 | * greater than zero. |
565 | */ |
566 | if (!tmr_req->exp_data_sn) |
567 | goto drop_unacknowledged_r2ts; |
568 | |
569 | /* |
570 | * We now check that the PDUs in DataOUT sequences below |
571 | * the TMR TASK_REASSIGN ExpDataSN (R2TSN the Initiator is |
572 | * expecting next) have all the DataOUT they require to complete |
573 | * the DataOUT sequence. First scan from R2TSN 0 to TMR |
574 | * TASK_REASSIGN ExpDataSN-1. |
575 | * |
576 | * If we have not received all DataOUT in question, we must |
577 | * make sure to make the appropriate changes to values in |
578 | * struct iscsit_cmd (and elsewhere depending on session parameters) |
579 | * so iscsit_build_r2ts_for_cmd() in iscsit_task_reassign_complete_write() |
580 | * will resend a new R2T for the DataOUT sequences in question. |
581 | */ |
582 | spin_lock_bh(lock: &cmd->r2t_lock); |
583 | if (list_empty(head: &cmd->cmd_r2t_list)) { |
584 | spin_unlock_bh(lock: &cmd->r2t_lock); |
585 | return -1; |
586 | } |
587 | |
588 | list_for_each_entry(r2t, &cmd->cmd_r2t_list, r2t_list) { |
589 | |
590 | if (r2t->r2t_sn >= tmr_req->exp_data_sn) |
591 | continue; |
592 | /* |
593 | * Safely ignore Recovery R2Ts and R2Ts that have completed |
594 | * DataOUT sequences. |
595 | */ |
596 | if (r2t->seq_complete) |
597 | continue; |
598 | |
599 | if (r2t->recovery_r2t) |
600 | continue; |
601 | |
602 | /* |
603 | * DataSequenceInOrder=Yes: |
604 | * |
605 | * Taking into account the iSCSI implementation requirement of |
606 | * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and |
607 | * DataSequenceInOrder=Yes, we must take into consideration |
608 | * the following: |
609 | * |
610 | * DataSequenceInOrder=No: |
611 | * |
612 | * Taking into account that the Initiator controls the (possibly |
613 | * random) PDU Order in (possibly random) Sequence Order of |
614 | * DataOUT the target requests with R2Ts, we must take into |
615 | * consideration the following: |
616 | * |
617 | * DataPDUInOrder=Yes for DataSequenceInOrder=[Yes,No]: |
618 | * |
619 | * While processing non-complete R2T DataOUT sequence requests |
620 | * the Target will re-request only the total sequence length |
621 | * minus current received offset. This is because we must |
622 | * assume the initiator will continue sending DataOUT from the |
623 | * last PDU before the connection failed. |
624 | * |
625 | * DataPDUInOrder=No for DataSequenceInOrder=[Yes,No]: |
626 | * |
627 | * While processing non-complete R2T DataOUT sequence requests |
628 | * the Target will re-request the entire DataOUT sequence if |
629 | * any single PDU is missing from the sequence. This is because |
630 | * we have no logical method to determine the next PDU offset, |
631 | * and we must assume the Initiator will be sending any random |
632 | * PDU offset in the current sequence after TASK_REASSIGN |
633 | * has completed. |
634 | */ |
635 | if (conn->sess->sess_ops->DataSequenceInOrder) { |
636 | if (!first_incomplete_r2t) { |
637 | cmd->r2t_offset -= r2t->xfer_len; |
638 | goto next; |
639 | } |
640 | |
641 | if (conn->sess->sess_ops->DataPDUInOrder) { |
642 | cmd->data_sn = 0; |
643 | cmd->r2t_offset -= (r2t->xfer_len - |
644 | cmd->next_burst_len); |
645 | first_incomplete_r2t = 0; |
646 | goto next; |
647 | } |
648 | |
649 | cmd->data_sn = 0; |
650 | cmd->r2t_offset -= r2t->xfer_len; |
651 | |
652 | for (i = 0; i < cmd->pdu_count; i++) { |
653 | pdu = &cmd->pdu_list[i]; |
654 | |
655 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) |
656 | continue; |
657 | |
658 | if ((pdu->offset >= r2t->offset) && |
659 | (pdu->offset < (r2t->offset + |
660 | r2t->xfer_len))) { |
661 | cmd->next_burst_len -= pdu->length; |
662 | cmd->write_data_done -= pdu->length; |
663 | pdu->status = ISCSI_PDU_NOT_RECEIVED; |
664 | } |
665 | } |
666 | |
667 | first_incomplete_r2t = 0; |
668 | } else { |
669 | struct iscsi_seq *seq; |
670 | |
671 | seq = iscsit_get_seq_holder(cmd, r2t->offset, |
672 | r2t->xfer_len); |
673 | if (!seq) { |
674 | spin_unlock_bh(lock: &cmd->r2t_lock); |
675 | return -1; |
676 | } |
677 | |
678 | cmd->write_data_done -= |
679 | (seq->offset - seq->orig_offset); |
680 | seq->data_sn = 0; |
681 | seq->offset = seq->orig_offset; |
682 | seq->next_burst_len = 0; |
683 | seq->status = DATAOUT_SEQUENCE_WITHIN_COMMAND_RECOVERY; |
684 | |
685 | cmd->seq_send_order--; |
686 | |
687 | if (conn->sess->sess_ops->DataPDUInOrder) |
688 | goto next; |
689 | |
690 | for (i = 0; i < seq->pdu_count; i++) { |
691 | pdu = &cmd->pdu_list[i+seq->pdu_start]; |
692 | |
693 | if (pdu->status != ISCSI_PDU_RECEIVED_OK) |
694 | continue; |
695 | |
696 | pdu->status = ISCSI_PDU_NOT_RECEIVED; |
697 | } |
698 | } |
699 | |
700 | next: |
701 | cmd->outstanding_r2ts--; |
702 | } |
703 | spin_unlock_bh(lock: &cmd->r2t_lock); |
704 | |
705 | /* |
706 | * We now drop all unacknowledged R2Ts, ie: ExpDataSN from TMR |
707 | * TASK_REASSIGN to the last R2T in the list.. We are also careful |
708 | * to check that the Initiator is not requesting R2Ts for DataOUT |
709 | * sequences it has already completed. |
710 | * |
711 | * Free each R2T in question and adjust values in struct iscsit_cmd |
712 | * accordingly so iscsit_build_r2ts_for_cmd() do the rest of |
713 | * the work after the TMR TASK_REASSIGN Response is sent. |
714 | */ |
715 | drop_unacknowledged_r2ts: |
716 | |
717 | cmd->cmd_flags &= ~ICF_SENT_LAST_R2T; |
718 | cmd->r2t_sn = tmr_req->exp_data_sn; |
719 | |
720 | spin_lock_bh(lock: &cmd->r2t_lock); |
721 | list_for_each_entry_safe(r2t, r2t_tmp, &cmd->cmd_r2t_list, r2t_list) { |
722 | /* |
723 | * Skip up to the R2T Sequence number provided by the |
724 | * iSCSI TASK_REASSIGN TMR |
725 | */ |
726 | if (r2t->r2t_sn < tmr_req->exp_data_sn) |
727 | continue; |
728 | |
729 | if (r2t->seq_complete) { |
730 | pr_err("Initiator is requesting R2Ts from" |
731 | " R2TSN: 0x%08x, but R2TSN: 0x%08x, Offset: %u," |
732 | " Length: %u is already complete." |
733 | " BAD INITIATOR ERL=2 IMPLEMENTATION!\n" , |
734 | tmr_req->exp_data_sn, r2t->r2t_sn, |
735 | r2t->offset, r2t->xfer_len); |
736 | spin_unlock_bh(lock: &cmd->r2t_lock); |
737 | return -1; |
738 | } |
739 | |
740 | if (r2t->recovery_r2t) { |
741 | iscsit_free_r2t(r2t, cmd); |
742 | continue; |
743 | } |
744 | |
745 | /* DataSequenceInOrder=Yes: |
746 | * |
747 | * Taking into account the iSCSI implementation requirement of |
748 | * MaxOutstandingR2T=1 while ErrorRecoveryLevel>0 and |
749 | * DataSequenceInOrder=Yes, it's safe to subtract the R2Ts |
750 | * entire transfer length from the commands R2T offset marker. |
751 | * |
752 | * DataSequenceInOrder=No: |
753 | * |
754 | * We subtract the difference from struct iscsi_seq between the |
755 | * current offset and original offset from cmd->write_data_done |
756 | * for account for DataOUT PDUs already received. Then reset |
757 | * the current offset to the original and zero out the current |
758 | * burst length, to make sure we re-request the entire DataOUT |
759 | * sequence. |
760 | */ |
761 | if (conn->sess->sess_ops->DataSequenceInOrder) |
762 | cmd->r2t_offset -= r2t->xfer_len; |
763 | else |
764 | cmd->seq_send_order--; |
765 | |
766 | cmd->outstanding_r2ts--; |
767 | iscsit_free_r2t(r2t, cmd); |
768 | } |
769 | spin_unlock_bh(lock: &cmd->r2t_lock); |
770 | |
771 | return 0; |
772 | } |
773 | |
774 | /* |
775 | * Performs sanity checks TMR TASK_REASSIGN's ExpDataSN for |
776 | * a given struct iscsit_cmd. |
777 | */ |
778 | int iscsit_check_task_reassign_expdatasn( |
779 | struct iscsi_tmr_req *tmr_req, |
780 | struct iscsit_conn *conn) |
781 | { |
782 | struct iscsit_cmd *ref_cmd = tmr_req->ref_cmd; |
783 | |
784 | if (ref_cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) |
785 | return 0; |
786 | |
787 | if (ref_cmd->se_cmd.se_cmd_flags & SCF_SENT_CHECK_CONDITION) |
788 | return 0; |
789 | |
790 | if (ref_cmd->data_direction == DMA_NONE) |
791 | return 0; |
792 | |
793 | /* |
794 | * For READs the TMR TASK_REASSIGNs ExpDataSN contains the next DataSN |
795 | * of DataIN the Initiator is expecting. |
796 | * |
797 | * Also check that the Initiator is not re-requesting DataIN that has |
798 | * already been acknowledged with a DataAck SNACK. |
799 | */ |
800 | if (ref_cmd->data_direction == DMA_FROM_DEVICE) { |
801 | if (tmr_req->exp_data_sn > ref_cmd->data_sn) { |
802 | pr_err("Received ExpDataSN: 0x%08x for READ" |
803 | " in TMR TASK_REASSIGN greater than command's" |
804 | " DataSN: 0x%08x.\n" , tmr_req->exp_data_sn, |
805 | ref_cmd->data_sn); |
806 | return -1; |
807 | } |
808 | if ((ref_cmd->cmd_flags & ICF_GOT_DATACK_SNACK) && |
809 | (tmr_req->exp_data_sn <= ref_cmd->acked_data_sn)) { |
810 | pr_err("Received ExpDataSN: 0x%08x for READ" |
811 | " in TMR TASK_REASSIGN for previously" |
812 | " acknowledged DataIN: 0x%08x," |
813 | " protocol error\n" , tmr_req->exp_data_sn, |
814 | ref_cmd->acked_data_sn); |
815 | return -1; |
816 | } |
817 | return iscsit_task_reassign_prepare_read(tmr_req, conn); |
818 | } |
819 | |
820 | /* |
821 | * For WRITEs the TMR TASK_REASSIGNs ExpDataSN contains the next R2TSN |
822 | * for R2Ts the Initiator is expecting. |
823 | * |
824 | * Do the magic in iscsit_task_reassign_prepare_write(). |
825 | */ |
826 | if (ref_cmd->data_direction == DMA_TO_DEVICE) { |
827 | if (tmr_req->exp_data_sn > ref_cmd->r2t_sn) { |
828 | pr_err("Received ExpDataSN: 0x%08x for WRITE" |
829 | " in TMR TASK_REASSIGN greater than command's" |
830 | " R2TSN: 0x%08x.\n" , tmr_req->exp_data_sn, |
831 | ref_cmd->r2t_sn); |
832 | return -1; |
833 | } |
834 | return iscsit_task_reassign_prepare_write(tmr_req, conn); |
835 | } |
836 | |
837 | pr_err("Unknown iSCSI data_direction: 0x%02x\n" , |
838 | ref_cmd->data_direction); |
839 | |
840 | return -1; |
841 | } |
842 | |