File Index
Files and directories
Sign in
FILE

iscsi_target_erl2.c

File preview with syntax highlighting.

linux-6.19.14/drivers/target/iscsi/iscsi_target_erl2.c C
  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.
  5 *
  6 * (c) Copyright 2007-2013 Datera, Inc.
  7 *
  8 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
  9 *
 10 ******************************************************************************/
 11
 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>
 16
 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"
 24
 25/*
 26 *	FIXME: Does RData SNACK apply here as well?
 27 */
 28static int iscsit_attach_active_connection_recovery_entry(
 29	struct iscsit_session *sess,
 30	struct iscsi_conn_recovery *cr)
 31{
 32	spin_lock(&sess->cr_a_lock);
 33	list_add_tail(&cr->cr_list, &sess->cr_active_list);
 34	spin_unlock(&sess->cr_a_lock);
 35
 36	return 0;
 37}
 38
 39static int iscsit_attach_inactive_connection_recovery_entry(
 40	struct iscsit_session *sess,
 41	struct iscsi_conn_recovery *cr)
 42{
 43	spin_lock(&sess->cr_i_lock);
 44	list_add_tail(&cr->cr_list, &sess->cr_inactive_list);
 45
 46	sess->conn_recovery_count++;
 47	pr_debug("Incremented connection recovery count to %u for"
 48		" SID: %u\n", sess->conn_recovery_count, sess->sid);
 49	spin_unlock(&sess->cr_i_lock);
 50
 51	return 0;
 52}
 53
 54struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
 55	struct iscsit_session *sess,
 56	u16 cid)
 57{
 58	struct iscsi_conn_recovery *cr;
 59
 60	spin_lock(&sess->cr_i_lock);
 61	list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
 62		if (cr->cid == cid) {
 63			spin_unlock(&sess->cr_i_lock);
 64			return cr;
 65		}
 66	}
 67	spin_unlock(&sess->cr_i_lock);
 68
 69	return NULL;
 70}
 71
 72void iscsit_free_connection_recovery_entries(struct iscsit_session *sess)
 73{
 74	struct iscsit_cmd *cmd, *cmd_tmp;
 75	struct iscsi_conn_recovery *cr, *cr_tmp;
 76
 77	spin_lock(&sess->cr_a_lock);
 78	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) {
 79		list_del(&cr->cr_list);
 80		spin_unlock(&sess->cr_a_lock);
 81
 82		spin_lock(&cr->conn_recovery_cmd_lock);
 83		list_for_each_entry_safe(cmd, cmd_tmp,
 84				&cr->conn_recovery_cmd_list, i_conn_node) {
 85
 86			list_del_init(&cmd->i_conn_node);
 87			cmd->conn = NULL;
 88			spin_unlock(&cr->conn_recovery_cmd_lock);
 89			iscsit_free_cmd(cmd, true);
 90			spin_lock(&cr->conn_recovery_cmd_lock);
 91		}
 92		spin_unlock(&cr->conn_recovery_cmd_lock);
 93		spin_lock(&sess->cr_a_lock);
 94
 95		kfree(cr);
 96	}
 97	spin_unlock(&sess->cr_a_lock);
 98
 99	spin_lock(&sess->cr_i_lock);
100	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) {
101		list_del(&cr->cr_list);
102		spin_unlock(&sess->cr_i_lock);
103
104		spin_lock(&cr->conn_recovery_cmd_lock);
105		list_for_each_entry_safe(cmd, cmd_tmp,
106				&cr->conn_recovery_cmd_list, i_conn_node) {
107
108			list_del_init(&cmd->i_conn_node);
109			cmd->conn = NULL;
110			spin_unlock(&cr->conn_recovery_cmd_lock);
111			iscsit_free_cmd(cmd, true);
112			spin_lock(&cr->conn_recovery_cmd_lock);
113		}
114		spin_unlock(&cr->conn_recovery_cmd_lock);
115		spin_lock(&sess->cr_i_lock);
116
117		kfree(cr);
118	}
119	spin_unlock(&sess->cr_i_lock);
120}
121
122int iscsit_remove_active_connection_recovery_entry(
123	struct iscsi_conn_recovery *cr,
124	struct iscsit_session *sess)
125{
126	spin_lock(&sess->cr_a_lock);
127	list_del(&cr->cr_list);
128
129	sess->conn_recovery_count--;
130	pr_debug("Decremented connection recovery count to %u for"
131		" SID: %u\n", sess->conn_recovery_count, sess->sid);
132	spin_unlock(&sess->cr_a_lock);
133
134	kfree(cr);
135
136	return 0;
137}
138
139static void iscsit_remove_inactive_connection_recovery_entry(
140	struct iscsi_conn_recovery *cr,
141	struct iscsit_session *sess)
142{
143	spin_lock(&sess->cr_i_lock);
144	list_del(&cr->cr_list);
145	spin_unlock(&sess->cr_i_lock);
146}
147
148/*
149 *	Called with cr->conn_recovery_cmd_lock help.
150 */
151int iscsit_remove_cmd_from_connection_recovery(
152	struct iscsit_cmd *cmd,
153	struct iscsit_session *sess)
154{
155	struct iscsi_conn_recovery *cr;
156
157	if (!cmd->cr) {
158		pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
159			" is NULL!\n", cmd->init_task_tag);
160		BUG();
161	}
162	cr = cmd->cr;
163
164	list_del_init(&cmd->i_conn_node);
165	return --cr->cmd_count;
166}
167
168void iscsit_discard_cr_cmds_by_expstatsn(
169	struct iscsi_conn_recovery *cr,
170	u32 exp_statsn)
171{
172	u32 dropped_count = 0;
173	struct iscsit_cmd *cmd, *cmd_tmp;
174	struct iscsit_session *sess = cr->sess;
175
176	spin_lock(&cr->conn_recovery_cmd_lock);
177	list_for_each_entry_safe(cmd, cmd_tmp,
178			&cr->conn_recovery_cmd_list, i_conn_node) {
179
180		if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
181		     (cmd->deferred_i_state != ISTATE_REMOVE)) ||
182		     (cmd->stat_sn >= exp_statsn)) {
183			continue;
184		}
185
186		dropped_count++;
187		pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
188			" 0x%08x, CID: %hu.\n", cmd->init_task_tag,
189				cmd->stat_sn, cr->cid);
190
191		iscsit_remove_cmd_from_connection_recovery(cmd, sess);
192
193		spin_unlock(&cr->conn_recovery_cmd_lock);
194		iscsit_free_cmd(cmd, true);
195		spin_lock(&cr->conn_recovery_cmd_lock);
196	}
197	spin_unlock(&cr->conn_recovery_cmd_lock);
198
199	pr_debug("Dropped %u total acknowledged commands on"
200		" CID: %hu less than old ExpStatSN: 0x%08x\n",
201			dropped_count, cr->cid, exp_statsn);
202
203	if (!cr->cmd_count) {
204		pr_debug("No commands to be reassigned for failed"
205			" connection CID: %hu on SID: %u\n",
206			cr->cid, sess->sid);
207		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
208		iscsit_attach_active_connection_recovery_entry(sess, cr);
209		pr_debug("iSCSI connection recovery successful for CID:"
210			" %hu on SID: %u\n", cr->cid, sess->sid);
211		iscsit_remove_active_connection_recovery_entry(cr, sess);
212	} else {
213		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
214		iscsit_attach_active_connection_recovery_entry(sess, cr);
215	}
216}
217
218int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsit_conn *conn)
219{
220	u32 dropped_count = 0;
221	struct iscsit_cmd *cmd, *cmd_tmp;
222	struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
223	struct iscsit_session *sess = conn->sess;
224
225	mutex_lock(&sess->cmdsn_mutex);
226	list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
227			&sess->sess_ooo_cmdsn_list, ooo_list) {
228
229		if (ooo_cmdsn->cid != conn->cid)
230			continue;
231
232		dropped_count++;
233		pr_debug("Dropping unacknowledged CmdSN:"
234		" 0x%08x during connection recovery on CID: %hu\n",
235			ooo_cmdsn->cmdsn, conn->cid);
236		iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
237	}
238	mutex_unlock(&sess->cmdsn_mutex);
239
240	spin_lock_bh(&conn->cmd_lock);
241	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
242		if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
243			continue;
244
245		list_del_init(&cmd->i_conn_node);
246
247		spin_unlock_bh(&conn->cmd_lock);
248		iscsit_free_cmd(cmd, true);
249		spin_lock_bh(&conn->cmd_lock);
250	}
251	spin_unlock_bh(&conn->cmd_lock);
252
253	pr_debug("Dropped %u total unacknowledged commands on CID:"
254		" %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
255				sess->exp_cmd_sn);
256	return 0;
257}
258
259int iscsit_prepare_cmds_for_reallegiance(struct iscsit_conn *conn)
260{
261	u32 cmd_count = 0;
262	struct iscsit_cmd *cmd, *cmd_tmp;
263	struct iscsi_conn_recovery *cr;
264
265	/*
266	 * Allocate an struct iscsi_conn_recovery for this connection.
267	 * Each struct iscsit_cmd contains an struct iscsi_conn_recovery pointer
268	 * (struct iscsit_cmd->cr) so we need to allocate this before preparing the
269	 * connection's command list for connection recovery.
270	 */
271	cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
272	if (!cr) {
273		pr_err("Unable to allocate memory for"
274			" struct iscsi_conn_recovery.\n");
275		return -1;
276	}
277	INIT_LIST_HEAD(&cr->cr_list);
278	INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
279	spin_lock_init(&cr->conn_recovery_cmd_lock);
280	/*
281	 * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
282	 * ISCSI_OP_NOOP_OUT opcodes.  For all other opcodes call
283	 * list_del_init(&cmd->i_conn_node); to release the command to the
284	 * session pool and remove it from the connection's list.
285	 *
286	 * Also stop the DataOUT timer, which will be restarted after
287	 * sending the TMR response.
288	 */
289	spin_lock_bh(&conn->cmd_lock);
290	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
291
292		if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
293		    (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
294			pr_debug("Not performing reallegiance on"
295				" Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
296				" CID: %hu\n", cmd->iscsi_opcode,
297				cmd->init_task_tag, cmd->cmd_sn, conn->cid);
298
299			list_del_init(&cmd->i_conn_node);
300			spin_unlock_bh(&conn->cmd_lock);
301			iscsit_free_cmd(cmd, true);
302			spin_lock_bh(&conn->cmd_lock);
303			continue;
304		}
305
306		/*
307		 * Special case where commands greater than or equal to
308		 * the session's ExpCmdSN are attached to the connection
309		 * list but not to the out of order CmdSN list.  The one
310		 * obvious case is when a command with immediate data
311		 * attached must only check the CmdSN against ExpCmdSN
312		 * after the data is received.  The special case below
313		 * is when the connection fails before data is received,
314		 * but also may apply to other PDUs, so it has been
315		 * made generic here.
316		 */
317		if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
318		     iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) {
319			list_del_init(&cmd->i_conn_node);
320			spin_unlock_bh(&conn->cmd_lock);
321			iscsit_free_cmd(cmd, true);
322			spin_lock_bh(&conn->cmd_lock);
323			continue;
324		}
325
326		cmd_count++;
327		pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
328			" CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
329			" reallegiance.\n", cmd->iscsi_opcode,
330			cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
331			conn->cid);
332
333		cmd->deferred_i_state = cmd->i_state;
334		cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY;
335
336		if (cmd->data_direction == DMA_TO_DEVICE)
337			iscsit_stop_dataout_timer(cmd);
338
339		cmd->sess = conn->sess;
340
341		list_del_init(&cmd->i_conn_node);
342		spin_unlock_bh(&conn->cmd_lock);
343
344		iscsit_free_all_datain_reqs(cmd);
345
346		transport_wait_for_tasks(&cmd->se_cmd);
347		/*
348		 * Add the struct iscsit_cmd to the connection recovery cmd list
349		 */
350		spin_lock(&cr->conn_recovery_cmd_lock);
351		list_add_tail(&cmd->i_conn_node, &cr->conn_recovery_cmd_list);
352		spin_unlock(&cr->conn_recovery_cmd_lock);
353
354		spin_lock_bh(&conn->cmd_lock);
355		cmd->cr = cr;
356		cmd->conn = NULL;
357	}
358	spin_unlock_bh(&conn->cmd_lock);
359	/*
360	 * Fill in the various values in the preallocated struct iscsi_conn_recovery.
361	 */
362	cr->cid = conn->cid;
363	cr->cmd_count = cmd_count;
364	cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
365	cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength;
366	cr->sess = conn->sess;
367
368	iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
369
370	return 0;
371}
372
373int iscsit_connection_recovery_transport_reset(struct iscsit_conn *conn)
374{
375	atomic_set(&conn->connection_recovery, 1);
376
377	if (iscsit_close_connection(conn) < 0)
378		return -1;
379
380	return 0;
381}
~/ Read-only file preview.