1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*******************************************************************************
3 * This file contains error recovery level two functions used by
4 * the iSCSI Target driver.
6 * (c) Copyright 2007-2013 Datera, Inc.
8 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
10 ******************************************************************************/
12 #include <linux/slab.h>
13 #include <scsi/iscsi_proto.h>
14 #include <target/target_core_base.h>
15 #include <target/target_core_fabric.h>
17 #include <target/iscsi/iscsi_target_core.h>
18 #include "iscsi_target_datain_values.h"
19 #include "iscsi_target_util.h"
20 #include "iscsi_target_erl0.h"
21 #include "iscsi_target_erl1.h"
22 #include "iscsi_target_erl2.h"
23 #include "iscsi_target.h"
26 * FIXME: Does RData SNACK apply here as well?
28 void iscsit_create_conn_recovery_datain_values(
29 struct iscsi_cmd
*cmd
,
33 struct iscsi_conn
*conn
= cmd
->conn
;
35 cmd
->next_burst_len
= 0;
36 cmd
->read_data_done
= 0;
38 while (be32_to_cpu(exp_data_sn
) > data_sn
) {
39 if ((cmd
->next_burst_len
+
40 conn
->conn_ops
->MaxRecvDataSegmentLength
) <
41 conn
->sess
->sess_ops
->MaxBurstLength
) {
42 cmd
->read_data_done
+=
43 conn
->conn_ops
->MaxRecvDataSegmentLength
;
44 cmd
->next_burst_len
+=
45 conn
->conn_ops
->MaxRecvDataSegmentLength
;
47 cmd
->read_data_done
+=
48 (conn
->sess
->sess_ops
->MaxBurstLength
-
50 cmd
->next_burst_len
= 0;
56 void iscsit_create_conn_recovery_dataout_values(
57 struct iscsi_cmd
*cmd
)
59 u32 write_data_done
= 0;
60 struct iscsi_conn
*conn
= cmd
->conn
;
63 cmd
->next_burst_len
= 0;
65 while (cmd
->write_data_done
> write_data_done
) {
66 if ((write_data_done
+ conn
->sess
->sess_ops
->MaxBurstLength
) <=
68 write_data_done
+= conn
->sess
->sess_ops
->MaxBurstLength
;
73 cmd
->write_data_done
= write_data_done
;
76 static int iscsit_attach_active_connection_recovery_entry(
77 struct iscsi_session
*sess
,
78 struct iscsi_conn_recovery
*cr
)
80 spin_lock(&sess
->cr_a_lock
);
81 list_add_tail(&cr
->cr_list
, &sess
->cr_active_list
);
82 spin_unlock(&sess
->cr_a_lock
);
87 static int iscsit_attach_inactive_connection_recovery_entry(
88 struct iscsi_session
*sess
,
89 struct iscsi_conn_recovery
*cr
)
91 spin_lock(&sess
->cr_i_lock
);
92 list_add_tail(&cr
->cr_list
, &sess
->cr_inactive_list
);
94 sess
->conn_recovery_count
++;
95 pr_debug("Incremented connection recovery count to %u for"
96 " SID: %u\n", sess
->conn_recovery_count
, sess
->sid
);
97 spin_unlock(&sess
->cr_i_lock
);
102 struct iscsi_conn_recovery
*iscsit_get_inactive_connection_recovery_entry(
103 struct iscsi_session
*sess
,
106 struct iscsi_conn_recovery
*cr
;
108 spin_lock(&sess
->cr_i_lock
);
109 list_for_each_entry(cr
, &sess
->cr_inactive_list
, cr_list
) {
110 if (cr
->cid
== cid
) {
111 spin_unlock(&sess
->cr_i_lock
);
115 spin_unlock(&sess
->cr_i_lock
);
120 void iscsit_free_connection_recovery_entries(struct iscsi_session
*sess
)
122 struct iscsi_cmd
*cmd
, *cmd_tmp
;
123 struct iscsi_conn_recovery
*cr
, *cr_tmp
;
125 spin_lock(&sess
->cr_a_lock
);
126 list_for_each_entry_safe(cr
, cr_tmp
, &sess
->cr_active_list
, cr_list
) {
127 list_del(&cr
->cr_list
);
128 spin_unlock(&sess
->cr_a_lock
);
130 spin_lock(&cr
->conn_recovery_cmd_lock
);
131 list_for_each_entry_safe(cmd
, cmd_tmp
,
132 &cr
->conn_recovery_cmd_list
, i_conn_node
) {
134 list_del_init(&cmd
->i_conn_node
);
136 spin_unlock(&cr
->conn_recovery_cmd_lock
);
137 iscsit_free_cmd(cmd
, true);
138 spin_lock(&cr
->conn_recovery_cmd_lock
);
140 spin_unlock(&cr
->conn_recovery_cmd_lock
);
141 spin_lock(&sess
->cr_a_lock
);
145 spin_unlock(&sess
->cr_a_lock
);
147 spin_lock(&sess
->cr_i_lock
);
148 list_for_each_entry_safe(cr
, cr_tmp
, &sess
->cr_inactive_list
, cr_list
) {
149 list_del(&cr
->cr_list
);
150 spin_unlock(&sess
->cr_i_lock
);
152 spin_lock(&cr
->conn_recovery_cmd_lock
);
153 list_for_each_entry_safe(cmd
, cmd_tmp
,
154 &cr
->conn_recovery_cmd_list
, i_conn_node
) {
156 list_del_init(&cmd
->i_conn_node
);
158 spin_unlock(&cr
->conn_recovery_cmd_lock
);
159 iscsit_free_cmd(cmd
, true);
160 spin_lock(&cr
->conn_recovery_cmd_lock
);
162 spin_unlock(&cr
->conn_recovery_cmd_lock
);
163 spin_lock(&sess
->cr_i_lock
);
167 spin_unlock(&sess
->cr_i_lock
);
170 int iscsit_remove_active_connection_recovery_entry(
171 struct iscsi_conn_recovery
*cr
,
172 struct iscsi_session
*sess
)
174 spin_lock(&sess
->cr_a_lock
);
175 list_del(&cr
->cr_list
);
177 sess
->conn_recovery_count
--;
178 pr_debug("Decremented connection recovery count to %u for"
179 " SID: %u\n", sess
->conn_recovery_count
, sess
->sid
);
180 spin_unlock(&sess
->cr_a_lock
);
187 static void iscsit_remove_inactive_connection_recovery_entry(
188 struct iscsi_conn_recovery
*cr
,
189 struct iscsi_session
*sess
)
191 spin_lock(&sess
->cr_i_lock
);
192 list_del(&cr
->cr_list
);
193 spin_unlock(&sess
->cr_i_lock
);
197 * Called with cr->conn_recovery_cmd_lock help.
199 int iscsit_remove_cmd_from_connection_recovery(
200 struct iscsi_cmd
*cmd
,
201 struct iscsi_session
*sess
)
203 struct iscsi_conn_recovery
*cr
;
206 pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
207 " is NULL!\n", cmd
->init_task_tag
);
212 list_del_init(&cmd
->i_conn_node
);
213 return --cr
->cmd_count
;
216 void iscsit_discard_cr_cmds_by_expstatsn(
217 struct iscsi_conn_recovery
*cr
,
220 u32 dropped_count
= 0;
221 struct iscsi_cmd
*cmd
, *cmd_tmp
;
222 struct iscsi_session
*sess
= cr
->sess
;
224 spin_lock(&cr
->conn_recovery_cmd_lock
);
225 list_for_each_entry_safe(cmd
, cmd_tmp
,
226 &cr
->conn_recovery_cmd_list
, i_conn_node
) {
228 if (((cmd
->deferred_i_state
!= ISTATE_SENT_STATUS
) &&
229 (cmd
->deferred_i_state
!= ISTATE_REMOVE
)) ||
230 (cmd
->stat_sn
>= exp_statsn
)) {
235 pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
236 " 0x%08x, CID: %hu.\n", cmd
->init_task_tag
,
237 cmd
->stat_sn
, cr
->cid
);
239 iscsit_remove_cmd_from_connection_recovery(cmd
, sess
);
241 spin_unlock(&cr
->conn_recovery_cmd_lock
);
242 iscsit_free_cmd(cmd
, true);
243 spin_lock(&cr
->conn_recovery_cmd_lock
);
245 spin_unlock(&cr
->conn_recovery_cmd_lock
);
247 pr_debug("Dropped %u total acknowledged commands on"
248 " CID: %hu less than old ExpStatSN: 0x%08x\n",
249 dropped_count
, cr
->cid
, exp_statsn
);
251 if (!cr
->cmd_count
) {
252 pr_debug("No commands to be reassigned for failed"
253 " connection CID: %hu on SID: %u\n",
255 iscsit_remove_inactive_connection_recovery_entry(cr
, sess
);
256 iscsit_attach_active_connection_recovery_entry(sess
, cr
);
257 pr_debug("iSCSI connection recovery successful for CID:"
258 " %hu on SID: %u\n", cr
->cid
, sess
->sid
);
259 iscsit_remove_active_connection_recovery_entry(cr
, sess
);
261 iscsit_remove_inactive_connection_recovery_entry(cr
, sess
);
262 iscsit_attach_active_connection_recovery_entry(sess
, cr
);
266 int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn
*conn
)
268 u32 dropped_count
= 0;
269 struct iscsi_cmd
*cmd
, *cmd_tmp
;
270 struct iscsi_ooo_cmdsn
*ooo_cmdsn
, *ooo_cmdsn_tmp
;
271 struct iscsi_session
*sess
= conn
->sess
;
273 mutex_lock(&sess
->cmdsn_mutex
);
274 list_for_each_entry_safe(ooo_cmdsn
, ooo_cmdsn_tmp
,
275 &sess
->sess_ooo_cmdsn_list
, ooo_list
) {
277 if (ooo_cmdsn
->cid
!= conn
->cid
)
281 pr_debug("Dropping unacknowledged CmdSN:"
282 " 0x%08x during connection recovery on CID: %hu\n",
283 ooo_cmdsn
->cmdsn
, conn
->cid
);
284 iscsit_remove_ooo_cmdsn(sess
, ooo_cmdsn
);
286 mutex_unlock(&sess
->cmdsn_mutex
);
288 spin_lock_bh(&conn
->cmd_lock
);
289 list_for_each_entry_safe(cmd
, cmd_tmp
, &conn
->conn_cmd_list
, i_conn_node
) {
290 if (!(cmd
->cmd_flags
& ICF_OOO_CMDSN
))
293 list_del_init(&cmd
->i_conn_node
);
295 spin_unlock_bh(&conn
->cmd_lock
);
296 iscsit_free_cmd(cmd
, true);
297 spin_lock_bh(&conn
->cmd_lock
);
299 spin_unlock_bh(&conn
->cmd_lock
);
301 pr_debug("Dropped %u total unacknowledged commands on CID:"
302 " %hu for ExpCmdSN: 0x%08x.\n", dropped_count
, conn
->cid
,
307 int iscsit_prepare_cmds_for_reallegiance(struct iscsi_conn
*conn
)
310 struct iscsi_cmd
*cmd
, *cmd_tmp
;
311 struct iscsi_conn_recovery
*cr
;
314 * Allocate an struct iscsi_conn_recovery for this connection.
315 * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
316 * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
317 * connection's command list for connection recovery.
319 cr
= kzalloc(sizeof(struct iscsi_conn_recovery
), GFP_KERNEL
);
321 pr_err("Unable to allocate memory for"
322 " struct iscsi_conn_recovery.\n");
325 INIT_LIST_HEAD(&cr
->cr_list
);
326 INIT_LIST_HEAD(&cr
->conn_recovery_cmd_list
);
327 spin_lock_init(&cr
->conn_recovery_cmd_lock
);
329 * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
330 * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call
331 * list_del_init(&cmd->i_conn_node); to release the command to the
332 * session pool and remove it from the connection's list.
334 * Also stop the DataOUT timer, which will be restarted after
335 * sending the TMR response.
337 spin_lock_bh(&conn
->cmd_lock
);
338 list_for_each_entry_safe(cmd
, cmd_tmp
, &conn
->conn_cmd_list
, i_conn_node
) {
340 if ((cmd
->iscsi_opcode
!= ISCSI_OP_SCSI_CMD
) &&
341 (cmd
->iscsi_opcode
!= ISCSI_OP_NOOP_OUT
)) {
342 pr_debug("Not performing reallegiance on"
343 " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
344 " CID: %hu\n", cmd
->iscsi_opcode
,
345 cmd
->init_task_tag
, cmd
->cmd_sn
, conn
->cid
);
347 list_del_init(&cmd
->i_conn_node
);
348 spin_unlock_bh(&conn
->cmd_lock
);
349 iscsit_free_cmd(cmd
, true);
350 spin_lock_bh(&conn
->cmd_lock
);
355 * Special case where commands greater than or equal to
356 * the session's ExpCmdSN are attached to the connection
357 * list but not to the out of order CmdSN list. The one
358 * obvious case is when a command with immediate data
359 * attached must only check the CmdSN against ExpCmdSN
360 * after the data is received. The special case below
361 * is when the connection fails before data is received,
362 * but also may apply to other PDUs, so it has been
365 if (!(cmd
->cmd_flags
& ICF_OOO_CMDSN
) && !cmd
->immediate_cmd
&&
366 iscsi_sna_gte(cmd
->cmd_sn
, conn
->sess
->exp_cmd_sn
)) {
367 list_del_init(&cmd
->i_conn_node
);
368 spin_unlock_bh(&conn
->cmd_lock
);
369 iscsit_free_cmd(cmd
, true);
370 spin_lock_bh(&conn
->cmd_lock
);
375 pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
376 " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
377 " reallegiance.\n", cmd
->iscsi_opcode
,
378 cmd
->init_task_tag
, cmd
->cmd_sn
, cmd
->stat_sn
,
381 cmd
->deferred_i_state
= cmd
->i_state
;
382 cmd
->i_state
= ISTATE_IN_CONNECTION_RECOVERY
;
384 if (cmd
->data_direction
== DMA_TO_DEVICE
)
385 iscsit_stop_dataout_timer(cmd
);
387 cmd
->sess
= conn
->sess
;
389 list_del_init(&cmd
->i_conn_node
);
390 spin_unlock_bh(&conn
->cmd_lock
);
392 iscsit_free_all_datain_reqs(cmd
);
394 transport_wait_for_tasks(&cmd
->se_cmd
);
396 * Add the struct iscsi_cmd to the connection recovery cmd list
398 spin_lock(&cr
->conn_recovery_cmd_lock
);
399 list_add_tail(&cmd
->i_conn_node
, &cr
->conn_recovery_cmd_list
);
400 spin_unlock(&cr
->conn_recovery_cmd_lock
);
402 spin_lock_bh(&conn
->cmd_lock
);
406 spin_unlock_bh(&conn
->cmd_lock
);
408 * Fill in the various values in the preallocated struct iscsi_conn_recovery.
411 cr
->cmd_count
= cmd_count
;
412 cr
->maxrecvdatasegmentlength
= conn
->conn_ops
->MaxRecvDataSegmentLength
;
413 cr
->maxxmitdatasegmentlength
= conn
->conn_ops
->MaxXmitDataSegmentLength
;
414 cr
->sess
= conn
->sess
;
416 iscsit_attach_inactive_connection_recovery_entry(conn
->sess
, cr
);
421 int iscsit_connection_recovery_transport_reset(struct iscsi_conn
*conn
)
423 atomic_set(&conn
->connection_recovery
, 1);
425 if (iscsit_close_connection(conn
) < 0)