18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*******************************************************************************
38c2ecf20Sopenharmony_ci * This file contains error recovery level two functions used by
48c2ecf20Sopenharmony_ci * the iSCSI Target driver.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci ******************************************************************************/
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/slab.h>
138c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h>
148c2ecf20Sopenharmony_ci#include <target/target_core_base.h>
158c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
188c2ecf20Sopenharmony_ci#include "iscsi_target_datain_values.h"
198c2ecf20Sopenharmony_ci#include "iscsi_target_util.h"
208c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h"
218c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h"
228c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h"
238c2ecf20Sopenharmony_ci#include "iscsi_target.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/*
268c2ecf20Sopenharmony_ci *	FIXME: Does RData SNACK apply here as well?
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_civoid iscsit_create_conn_recovery_datain_values(
298c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
308c2ecf20Sopenharmony_ci	__be32 exp_data_sn)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	u32 data_sn = 0;
338c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	cmd->next_burst_len = 0;
368c2ecf20Sopenharmony_ci	cmd->read_data_done = 0;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	while (be32_to_cpu(exp_data_sn) > data_sn) {
398c2ecf20Sopenharmony_ci		if ((cmd->next_burst_len +
408c2ecf20Sopenharmony_ci		     conn->conn_ops->MaxRecvDataSegmentLength) <
418c2ecf20Sopenharmony_ci		     conn->sess->sess_ops->MaxBurstLength) {
428c2ecf20Sopenharmony_ci			cmd->read_data_done +=
438c2ecf20Sopenharmony_ci			       conn->conn_ops->MaxRecvDataSegmentLength;
448c2ecf20Sopenharmony_ci			cmd->next_burst_len +=
458c2ecf20Sopenharmony_ci			       conn->conn_ops->MaxRecvDataSegmentLength;
468c2ecf20Sopenharmony_ci		} else {
478c2ecf20Sopenharmony_ci			cmd->read_data_done +=
488c2ecf20Sopenharmony_ci				(conn->sess->sess_ops->MaxBurstLength -
498c2ecf20Sopenharmony_ci				cmd->next_burst_len);
508c2ecf20Sopenharmony_ci			cmd->next_burst_len = 0;
518c2ecf20Sopenharmony_ci		}
528c2ecf20Sopenharmony_ci		data_sn++;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_civoid iscsit_create_conn_recovery_dataout_values(
578c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	u32 write_data_done = 0;
608c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	cmd->data_sn = 0;
638c2ecf20Sopenharmony_ci	cmd->next_burst_len = 0;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	while (cmd->write_data_done > write_data_done) {
668c2ecf20Sopenharmony_ci		if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <=
678c2ecf20Sopenharmony_ci		     cmd->write_data_done)
688c2ecf20Sopenharmony_ci			write_data_done += conn->sess->sess_ops->MaxBurstLength;
698c2ecf20Sopenharmony_ci		else
708c2ecf20Sopenharmony_ci			break;
718c2ecf20Sopenharmony_ci	}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	cmd->write_data_done = write_data_done;
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cistatic int iscsit_attach_active_connection_recovery_entry(
778c2ecf20Sopenharmony_ci	struct iscsi_session *sess,
788c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_a_lock);
818c2ecf20Sopenharmony_ci	list_add_tail(&cr->cr_list, &sess->cr_active_list);
828c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_a_lock);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return 0;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_cistatic int iscsit_attach_inactive_connection_recovery_entry(
888c2ecf20Sopenharmony_ci	struct iscsi_session *sess,
898c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_i_lock);
928c2ecf20Sopenharmony_ci	list_add_tail(&cr->cr_list, &sess->cr_inactive_list);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	sess->conn_recovery_count++;
958c2ecf20Sopenharmony_ci	pr_debug("Incremented connection recovery count to %u for"
968c2ecf20Sopenharmony_ci		" SID: %u\n", sess->conn_recovery_count, sess->sid);
978c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_i_lock);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	return 0;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistruct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
1038c2ecf20Sopenharmony_ci	struct iscsi_session *sess,
1048c2ecf20Sopenharmony_ci	u16 cid)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_i_lock);
1098c2ecf20Sopenharmony_ci	list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
1108c2ecf20Sopenharmony_ci		if (cr->cid == cid) {
1118c2ecf20Sopenharmony_ci			spin_unlock(&sess->cr_i_lock);
1128c2ecf20Sopenharmony_ci			return cr;
1138c2ecf20Sopenharmony_ci		}
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_i_lock);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	return NULL;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_civoid iscsit_free_connection_recovery_entries(struct iscsi_session *sess)
1218c2ecf20Sopenharmony_ci{
1228c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd, *cmd_tmp;
1238c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr, *cr_tmp;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_a_lock);
1268c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) {
1278c2ecf20Sopenharmony_ci		list_del(&cr->cr_list);
1288c2ecf20Sopenharmony_ci		spin_unlock(&sess->cr_a_lock);
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci		spin_lock(&cr->conn_recovery_cmd_lock);
1318c2ecf20Sopenharmony_ci		list_for_each_entry_safe(cmd, cmd_tmp,
1328c2ecf20Sopenharmony_ci				&cr->conn_recovery_cmd_list, i_conn_node) {
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci			list_del_init(&cmd->i_conn_node);
1358c2ecf20Sopenharmony_ci			cmd->conn = NULL;
1368c2ecf20Sopenharmony_ci			spin_unlock(&cr->conn_recovery_cmd_lock);
1378c2ecf20Sopenharmony_ci			iscsit_free_cmd(cmd, true);
1388c2ecf20Sopenharmony_ci			spin_lock(&cr->conn_recovery_cmd_lock);
1398c2ecf20Sopenharmony_ci		}
1408c2ecf20Sopenharmony_ci		spin_unlock(&cr->conn_recovery_cmd_lock);
1418c2ecf20Sopenharmony_ci		spin_lock(&sess->cr_a_lock);
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		kfree(cr);
1448c2ecf20Sopenharmony_ci	}
1458c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_a_lock);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_i_lock);
1488c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) {
1498c2ecf20Sopenharmony_ci		list_del(&cr->cr_list);
1508c2ecf20Sopenharmony_ci		spin_unlock(&sess->cr_i_lock);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		spin_lock(&cr->conn_recovery_cmd_lock);
1538c2ecf20Sopenharmony_ci		list_for_each_entry_safe(cmd, cmd_tmp,
1548c2ecf20Sopenharmony_ci				&cr->conn_recovery_cmd_list, i_conn_node) {
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci			list_del_init(&cmd->i_conn_node);
1578c2ecf20Sopenharmony_ci			cmd->conn = NULL;
1588c2ecf20Sopenharmony_ci			spin_unlock(&cr->conn_recovery_cmd_lock);
1598c2ecf20Sopenharmony_ci			iscsit_free_cmd(cmd, true);
1608c2ecf20Sopenharmony_ci			spin_lock(&cr->conn_recovery_cmd_lock);
1618c2ecf20Sopenharmony_ci		}
1628c2ecf20Sopenharmony_ci		spin_unlock(&cr->conn_recovery_cmd_lock);
1638c2ecf20Sopenharmony_ci		spin_lock(&sess->cr_i_lock);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		kfree(cr);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_i_lock);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciint iscsit_remove_active_connection_recovery_entry(
1718c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr,
1728c2ecf20Sopenharmony_ci	struct iscsi_session *sess)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_a_lock);
1758c2ecf20Sopenharmony_ci	list_del(&cr->cr_list);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	sess->conn_recovery_count--;
1788c2ecf20Sopenharmony_ci	pr_debug("Decremented connection recovery count to %u for"
1798c2ecf20Sopenharmony_ci		" SID: %u\n", sess->conn_recovery_count, sess->sid);
1808c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_a_lock);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	kfree(cr);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	return 0;
1858c2ecf20Sopenharmony_ci}
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_cistatic void iscsit_remove_inactive_connection_recovery_entry(
1888c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr,
1898c2ecf20Sopenharmony_ci	struct iscsi_session *sess)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	spin_lock(&sess->cr_i_lock);
1928c2ecf20Sopenharmony_ci	list_del(&cr->cr_list);
1938c2ecf20Sopenharmony_ci	spin_unlock(&sess->cr_i_lock);
1948c2ecf20Sopenharmony_ci}
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/*
1978c2ecf20Sopenharmony_ci *	Called with cr->conn_recovery_cmd_lock help.
1988c2ecf20Sopenharmony_ci */
1998c2ecf20Sopenharmony_ciint iscsit_remove_cmd_from_connection_recovery(
2008c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
2018c2ecf20Sopenharmony_ci	struct iscsi_session *sess)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	if (!cmd->cr) {
2068c2ecf20Sopenharmony_ci		pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
2078c2ecf20Sopenharmony_ci			" is NULL!\n", cmd->init_task_tag);
2088c2ecf20Sopenharmony_ci		BUG();
2098c2ecf20Sopenharmony_ci	}
2108c2ecf20Sopenharmony_ci	cr = cmd->cr;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	list_del_init(&cmd->i_conn_node);
2138c2ecf20Sopenharmony_ci	return --cr->cmd_count;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_civoid iscsit_discard_cr_cmds_by_expstatsn(
2178c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr,
2188c2ecf20Sopenharmony_ci	u32 exp_statsn)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	u32 dropped_count = 0;
2218c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd, *cmd_tmp;
2228c2ecf20Sopenharmony_ci	struct iscsi_session *sess = cr->sess;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	spin_lock(&cr->conn_recovery_cmd_lock);
2258c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp,
2268c2ecf20Sopenharmony_ci			&cr->conn_recovery_cmd_list, i_conn_node) {
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
2298c2ecf20Sopenharmony_ci		     (cmd->deferred_i_state != ISTATE_REMOVE)) ||
2308c2ecf20Sopenharmony_ci		     (cmd->stat_sn >= exp_statsn)) {
2318c2ecf20Sopenharmony_ci			continue;
2328c2ecf20Sopenharmony_ci		}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci		dropped_count++;
2358c2ecf20Sopenharmony_ci		pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
2368c2ecf20Sopenharmony_ci			" 0x%08x, CID: %hu.\n", cmd->init_task_tag,
2378c2ecf20Sopenharmony_ci				cmd->stat_sn, cr->cid);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		iscsit_remove_cmd_from_connection_recovery(cmd, sess);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci		spin_unlock(&cr->conn_recovery_cmd_lock);
2428c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, true);
2438c2ecf20Sopenharmony_ci		spin_lock(&cr->conn_recovery_cmd_lock);
2448c2ecf20Sopenharmony_ci	}
2458c2ecf20Sopenharmony_ci	spin_unlock(&cr->conn_recovery_cmd_lock);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	pr_debug("Dropped %u total acknowledged commands on"
2488c2ecf20Sopenharmony_ci		" CID: %hu less than old ExpStatSN: 0x%08x\n",
2498c2ecf20Sopenharmony_ci			dropped_count, cr->cid, exp_statsn);
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	if (!cr->cmd_count) {
2528c2ecf20Sopenharmony_ci		pr_debug("No commands to be reassigned for failed"
2538c2ecf20Sopenharmony_ci			" connection CID: %hu on SID: %u\n",
2548c2ecf20Sopenharmony_ci			cr->cid, sess->sid);
2558c2ecf20Sopenharmony_ci		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
2568c2ecf20Sopenharmony_ci		iscsit_attach_active_connection_recovery_entry(sess, cr);
2578c2ecf20Sopenharmony_ci		pr_debug("iSCSI connection recovery successful for CID:"
2588c2ecf20Sopenharmony_ci			" %hu on SID: %u\n", cr->cid, sess->sid);
2598c2ecf20Sopenharmony_ci		iscsit_remove_active_connection_recovery_entry(cr, sess);
2608c2ecf20Sopenharmony_ci	} else {
2618c2ecf20Sopenharmony_ci		iscsit_remove_inactive_connection_recovery_entry(cr, sess);
2628c2ecf20Sopenharmony_ci		iscsit_attach_active_connection_recovery_entry(sess, cr);
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ciint iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	u32 dropped_count = 0;
2698c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd, *cmd_tmp;
2708c2ecf20Sopenharmony_ci	struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
2718c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	mutex_lock(&sess->cmdsn_mutex);
2748c2ecf20Sopenharmony_ci	list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
2758c2ecf20Sopenharmony_ci			&sess->sess_ooo_cmdsn_list, ooo_list) {
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci		if (ooo_cmdsn->cid != conn->cid)
2788c2ecf20Sopenharmony_ci			continue;
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci		dropped_count++;
2818c2ecf20Sopenharmony_ci		pr_debug("Dropping unacknowledged CmdSN:"
2828c2ecf20Sopenharmony_ci		" 0x%08x during connection recovery on CID: %hu\n",
2838c2ecf20Sopenharmony_ci			ooo_cmdsn->cmdsn, conn->cid);
2848c2ecf20Sopenharmony_ci		iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
2858c2ecf20Sopenharmony_ci	}
2868c2ecf20Sopenharmony_ci	mutex_unlock(&sess->cmdsn_mutex);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
2898c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
2908c2ecf20Sopenharmony_ci		if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
2918c2ecf20Sopenharmony_ci			continue;
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
2968c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, true);
2978c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci	pr_debug("Dropped %u total unacknowledged commands on CID:"
3028c2ecf20Sopenharmony_ci		" %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
3038c2ecf20Sopenharmony_ci				sess->exp_cmd_sn);
3048c2ecf20Sopenharmony_ci	return 0;
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ciint iscsit_prepare_cmds_for_reallegiance(struct iscsi_conn *conn)
3088c2ecf20Sopenharmony_ci{
3098c2ecf20Sopenharmony_ci	u32 cmd_count = 0;
3108c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd, *cmd_tmp;
3118c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	/*
3148c2ecf20Sopenharmony_ci	 * Allocate an struct iscsi_conn_recovery for this connection.
3158c2ecf20Sopenharmony_ci	 * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
3168c2ecf20Sopenharmony_ci	 * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
3178c2ecf20Sopenharmony_ci	 * connection's command list for connection recovery.
3188c2ecf20Sopenharmony_ci	 */
3198c2ecf20Sopenharmony_ci	cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
3208c2ecf20Sopenharmony_ci	if (!cr) {
3218c2ecf20Sopenharmony_ci		pr_err("Unable to allocate memory for"
3228c2ecf20Sopenharmony_ci			" struct iscsi_conn_recovery.\n");
3238c2ecf20Sopenharmony_ci		return -1;
3248c2ecf20Sopenharmony_ci	}
3258c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cr->cr_list);
3268c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
3278c2ecf20Sopenharmony_ci	spin_lock_init(&cr->conn_recovery_cmd_lock);
3288c2ecf20Sopenharmony_ci	/*
3298c2ecf20Sopenharmony_ci	 * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
3308c2ecf20Sopenharmony_ci	 * ISCSI_OP_NOOP_OUT opcodes.  For all other opcodes call
3318c2ecf20Sopenharmony_ci	 * list_del_init(&cmd->i_conn_node); to release the command to the
3328c2ecf20Sopenharmony_ci	 * session pool and remove it from the connection's list.
3338c2ecf20Sopenharmony_ci	 *
3348c2ecf20Sopenharmony_ci	 * Also stop the DataOUT timer, which will be restarted after
3358c2ecf20Sopenharmony_ci	 * sending the TMR response.
3368c2ecf20Sopenharmony_ci	 */
3378c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
3388c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci		if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
3418c2ecf20Sopenharmony_ci		    (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
3428c2ecf20Sopenharmony_ci			pr_debug("Not performing reallegiance on"
3438c2ecf20Sopenharmony_ci				" Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
3448c2ecf20Sopenharmony_ci				" CID: %hu\n", cmd->iscsi_opcode,
3458c2ecf20Sopenharmony_ci				cmd->init_task_tag, cmd->cmd_sn, conn->cid);
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci			list_del_init(&cmd->i_conn_node);
3488c2ecf20Sopenharmony_ci			spin_unlock_bh(&conn->cmd_lock);
3498c2ecf20Sopenharmony_ci			iscsit_free_cmd(cmd, true);
3508c2ecf20Sopenharmony_ci			spin_lock_bh(&conn->cmd_lock);
3518c2ecf20Sopenharmony_ci			continue;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		/*
3558c2ecf20Sopenharmony_ci		 * Special case where commands greater than or equal to
3568c2ecf20Sopenharmony_ci		 * the session's ExpCmdSN are attached to the connection
3578c2ecf20Sopenharmony_ci		 * list but not to the out of order CmdSN list.  The one
3588c2ecf20Sopenharmony_ci		 * obvious case is when a command with immediate data
3598c2ecf20Sopenharmony_ci		 * attached must only check the CmdSN against ExpCmdSN
3608c2ecf20Sopenharmony_ci		 * after the data is received.  The special case below
3618c2ecf20Sopenharmony_ci		 * is when the connection fails before data is received,
3628c2ecf20Sopenharmony_ci		 * but also may apply to other PDUs, so it has been
3638c2ecf20Sopenharmony_ci		 * made generic here.
3648c2ecf20Sopenharmony_ci		 */
3658c2ecf20Sopenharmony_ci		if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
3668c2ecf20Sopenharmony_ci		     iscsi_sna_gte(cmd->cmd_sn, conn->sess->exp_cmd_sn)) {
3678c2ecf20Sopenharmony_ci			list_del_init(&cmd->i_conn_node);
3688c2ecf20Sopenharmony_ci			spin_unlock_bh(&conn->cmd_lock);
3698c2ecf20Sopenharmony_ci			iscsit_free_cmd(cmd, true);
3708c2ecf20Sopenharmony_ci			spin_lock_bh(&conn->cmd_lock);
3718c2ecf20Sopenharmony_ci			continue;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci		cmd_count++;
3758c2ecf20Sopenharmony_ci		pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
3768c2ecf20Sopenharmony_ci			" CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
3778c2ecf20Sopenharmony_ci			" reallegiance.\n", cmd->iscsi_opcode,
3788c2ecf20Sopenharmony_ci			cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
3798c2ecf20Sopenharmony_ci			conn->cid);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci		cmd->deferred_i_state = cmd->i_state;
3828c2ecf20Sopenharmony_ci		cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci		if (cmd->data_direction == DMA_TO_DEVICE)
3858c2ecf20Sopenharmony_ci			iscsit_stop_dataout_timer(cmd);
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci		cmd->sess = conn->sess;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
3908c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci		iscsit_free_all_datain_reqs(cmd);
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci		transport_wait_for_tasks(&cmd->se_cmd);
3958c2ecf20Sopenharmony_ci		/*
3968c2ecf20Sopenharmony_ci		 * Add the struct iscsi_cmd to the connection recovery cmd list
3978c2ecf20Sopenharmony_ci		 */
3988c2ecf20Sopenharmony_ci		spin_lock(&cr->conn_recovery_cmd_lock);
3998c2ecf20Sopenharmony_ci		list_add_tail(&cmd->i_conn_node, &cr->conn_recovery_cmd_list);
4008c2ecf20Sopenharmony_ci		spin_unlock(&cr->conn_recovery_cmd_lock);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
4038c2ecf20Sopenharmony_ci		cmd->cr = cr;
4048c2ecf20Sopenharmony_ci		cmd->conn = NULL;
4058c2ecf20Sopenharmony_ci	}
4068c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
4078c2ecf20Sopenharmony_ci	/*
4088c2ecf20Sopenharmony_ci	 * Fill in the various values in the preallocated struct iscsi_conn_recovery.
4098c2ecf20Sopenharmony_ci	 */
4108c2ecf20Sopenharmony_ci	cr->cid = conn->cid;
4118c2ecf20Sopenharmony_ci	cr->cmd_count = cmd_count;
4128c2ecf20Sopenharmony_ci	cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
4138c2ecf20Sopenharmony_ci	cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength;
4148c2ecf20Sopenharmony_ci	cr->sess = conn->sess;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return 0;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ciint iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn)
4228c2ecf20Sopenharmony_ci{
4238c2ecf20Sopenharmony_ci	atomic_set(&conn->connection_recovery, 1);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	if (iscsit_close_connection(conn) < 0)
4268c2ecf20Sopenharmony_ci		return -1;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return 0;
4298c2ecf20Sopenharmony_ci}
430