18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*******************************************************************************
38c2ecf20Sopenharmony_ci * This file contains main functions related to the iSCSI Target Core Driver.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci ******************************************************************************/
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <crypto/hash.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/kthread.h>
148c2ecf20Sopenharmony_ci#include <linux/completion.h>
158c2ecf20Sopenharmony_ci#include <linux/module.h>
168c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
178c2ecf20Sopenharmony_ci#include <linux/idr.h>
188c2ecf20Sopenharmony_ci#include <linux/delay.h>
198c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
208c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
218c2ecf20Sopenharmony_ci#include <linux/inet.h>
228c2ecf20Sopenharmony_ci#include <net/ipv6.h>
238c2ecf20Sopenharmony_ci#include <scsi/scsi_proto.h>
248c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h>
258c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h>
268c2ecf20Sopenharmony_ci#include <target/target_core_base.h>
278c2ecf20Sopenharmony_ci#include <target/target_core_fabric.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
308c2ecf20Sopenharmony_ci#include "iscsi_target_parameters.h"
318c2ecf20Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h"
328c2ecf20Sopenharmony_ci#include "iscsi_target_datain_values.h"
338c2ecf20Sopenharmony_ci#include "iscsi_target_erl0.h"
348c2ecf20Sopenharmony_ci#include "iscsi_target_erl1.h"
358c2ecf20Sopenharmony_ci#include "iscsi_target_erl2.h"
368c2ecf20Sopenharmony_ci#include "iscsi_target_login.h"
378c2ecf20Sopenharmony_ci#include "iscsi_target_tmr.h"
388c2ecf20Sopenharmony_ci#include "iscsi_target_tpg.h"
398c2ecf20Sopenharmony_ci#include "iscsi_target_util.h"
408c2ecf20Sopenharmony_ci#include "iscsi_target.h"
418c2ecf20Sopenharmony_ci#include "iscsi_target_device.h"
428c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_target_stat.h>
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci#include <target/iscsi/iscsi_transport.h>
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_cistatic LIST_HEAD(g_tiqn_list);
478c2ecf20Sopenharmony_cistatic LIST_HEAD(g_np_list);
488c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(tiqn_lock);
498c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(np_lock);
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct idr tiqn_idr;
528c2ecf20Sopenharmony_ciDEFINE_IDA(sess_ida);
538c2ecf20Sopenharmony_cistruct mutex auth_id_lock;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct iscsit_global *iscsit_global;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_cistruct kmem_cache *lio_qr_cache;
588c2ecf20Sopenharmony_cistruct kmem_cache *lio_dr_cache;
598c2ecf20Sopenharmony_cistruct kmem_cache *lio_ooo_cache;
608c2ecf20Sopenharmony_cistruct kmem_cache *lio_r2t_cache;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic int iscsit_handle_immediate_data(struct iscsi_cmd *,
638c2ecf20Sopenharmony_ci			struct iscsi_scsi_req *, u32);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistruct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct iscsi_tiqn *tiqn = NULL;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	spin_lock(&tiqn_lock);
708c2ecf20Sopenharmony_ci	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
718c2ecf20Sopenharmony_ci		if (!strcmp(tiqn->tiqn, buf)) {
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci			spin_lock(&tiqn->tiqn_state_lock);
748c2ecf20Sopenharmony_ci			if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
758c2ecf20Sopenharmony_ci				tiqn->tiqn_access_count++;
768c2ecf20Sopenharmony_ci				spin_unlock(&tiqn->tiqn_state_lock);
778c2ecf20Sopenharmony_ci				spin_unlock(&tiqn_lock);
788c2ecf20Sopenharmony_ci				return tiqn;
798c2ecf20Sopenharmony_ci			}
808c2ecf20Sopenharmony_ci			spin_unlock(&tiqn->tiqn_state_lock);
818c2ecf20Sopenharmony_ci		}
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	spin_unlock(&tiqn_lock);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	return NULL;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int iscsit_set_tiqn_shutdown(struct iscsi_tiqn *tiqn)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
918c2ecf20Sopenharmony_ci	if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
928c2ecf20Sopenharmony_ci		tiqn->tiqn_state = TIQN_STATE_SHUTDOWN;
938c2ecf20Sopenharmony_ci		spin_unlock(&tiqn->tiqn_state_lock);
948c2ecf20Sopenharmony_ci		return 0;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return -1;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_civoid iscsit_put_tiqn_for_login(struct iscsi_tiqn *tiqn)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
1048c2ecf20Sopenharmony_ci	tiqn->tiqn_access_count--;
1058c2ecf20Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci/*
1098c2ecf20Sopenharmony_ci * Note that IQN formatting is expected to be done in userspace, and
1108c2ecf20Sopenharmony_ci * no explict IQN format checks are done here.
1118c2ecf20Sopenharmony_ci */
1128c2ecf20Sopenharmony_cistruct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct iscsi_tiqn *tiqn = NULL;
1158c2ecf20Sopenharmony_ci	int ret;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	if (strlen(buf) >= ISCSI_IQN_LEN) {
1188c2ecf20Sopenharmony_ci		pr_err("Target IQN exceeds %d bytes\n",
1198c2ecf20Sopenharmony_ci				ISCSI_IQN_LEN);
1208c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	tiqn = kzalloc(sizeof(*tiqn), GFP_KERNEL);
1248c2ecf20Sopenharmony_ci	if (!tiqn)
1258c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	sprintf(tiqn->tiqn, "%s", buf);
1288c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tiqn->tiqn_list);
1298c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tiqn->tiqn_tpg_list);
1308c2ecf20Sopenharmony_ci	spin_lock_init(&tiqn->tiqn_state_lock);
1318c2ecf20Sopenharmony_ci	spin_lock_init(&tiqn->tiqn_tpg_lock);
1328c2ecf20Sopenharmony_ci	spin_lock_init(&tiqn->sess_err_stats.lock);
1338c2ecf20Sopenharmony_ci	spin_lock_init(&tiqn->login_stats.lock);
1348c2ecf20Sopenharmony_ci	spin_lock_init(&tiqn->logout_stats.lock);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	tiqn->tiqn_state = TIQN_STATE_ACTIVE;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	idr_preload(GFP_KERNEL);
1398c2ecf20Sopenharmony_ci	spin_lock(&tiqn_lock);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	ret = idr_alloc(&tiqn_idr, NULL, 0, 0, GFP_NOWAIT);
1428c2ecf20Sopenharmony_ci	if (ret < 0) {
1438c2ecf20Sopenharmony_ci		pr_err("idr_alloc() failed for tiqn->tiqn_index\n");
1448c2ecf20Sopenharmony_ci		spin_unlock(&tiqn_lock);
1458c2ecf20Sopenharmony_ci		idr_preload_end();
1468c2ecf20Sopenharmony_ci		kfree(tiqn);
1478c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci	tiqn->tiqn_index = ret;
1508c2ecf20Sopenharmony_ci	list_add_tail(&tiqn->tiqn_list, &g_tiqn_list);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	spin_unlock(&tiqn_lock);
1538c2ecf20Sopenharmony_ci	idr_preload_end();
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return tiqn;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic void iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn)
1628c2ecf20Sopenharmony_ci{
1638c2ecf20Sopenharmony_ci	/*
1648c2ecf20Sopenharmony_ci	 * Wait for accesses to said struct iscsi_tiqn to end.
1658c2ecf20Sopenharmony_ci	 */
1668c2ecf20Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
1678c2ecf20Sopenharmony_ci	while (tiqn->tiqn_access_count != 0) {
1688c2ecf20Sopenharmony_ci		spin_unlock(&tiqn->tiqn_state_lock);
1698c2ecf20Sopenharmony_ci		msleep(10);
1708c2ecf20Sopenharmony_ci		spin_lock(&tiqn->tiqn_state_lock);
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
1738c2ecf20Sopenharmony_ci}
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_civoid iscsit_del_tiqn(struct iscsi_tiqn *tiqn)
1768c2ecf20Sopenharmony_ci{
1778c2ecf20Sopenharmony_ci	/*
1788c2ecf20Sopenharmony_ci	 * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN
1798c2ecf20Sopenharmony_ci	 * while holding tiqn->tiqn_state_lock.  This means that all subsequent
1808c2ecf20Sopenharmony_ci	 * attempts to access this struct iscsi_tiqn will fail from both transport
1818c2ecf20Sopenharmony_ci	 * fabric and control code paths.
1828c2ecf20Sopenharmony_ci	 */
1838c2ecf20Sopenharmony_ci	if (iscsit_set_tiqn_shutdown(tiqn) < 0) {
1848c2ecf20Sopenharmony_ci		pr_err("iscsit_set_tiqn_shutdown() failed\n");
1858c2ecf20Sopenharmony_ci		return;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	iscsit_wait_for_tiqn(tiqn);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	spin_lock(&tiqn_lock);
1918c2ecf20Sopenharmony_ci	list_del(&tiqn->tiqn_list);
1928c2ecf20Sopenharmony_ci	idr_remove(&tiqn_idr, tiqn->tiqn_index);
1938c2ecf20Sopenharmony_ci	spin_unlock(&tiqn_lock);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	pr_debug("CORE[0] - Deleted iSCSI Target IQN: %s\n",
1968c2ecf20Sopenharmony_ci			tiqn->tiqn);
1978c2ecf20Sopenharmony_ci	kfree(tiqn);
1988c2ecf20Sopenharmony_ci}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ciint iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	int ret;
2038c2ecf20Sopenharmony_ci	/*
2048c2ecf20Sopenharmony_ci	 * Determine if the network portal is accepting storage traffic.
2058c2ecf20Sopenharmony_ci	 */
2068c2ecf20Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
2078c2ecf20Sopenharmony_ci	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
2088c2ecf20Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
2098c2ecf20Sopenharmony_ci		return -1;
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
2128c2ecf20Sopenharmony_ci	/*
2138c2ecf20Sopenharmony_ci	 * Determine if the portal group is accepting storage traffic.
2148c2ecf20Sopenharmony_ci	 */
2158c2ecf20Sopenharmony_ci	spin_lock_bh(&tpg->tpg_state_lock);
2168c2ecf20Sopenharmony_ci	if (tpg->tpg_state != TPG_STATE_ACTIVE) {
2178c2ecf20Sopenharmony_ci		spin_unlock_bh(&tpg->tpg_state_lock);
2188c2ecf20Sopenharmony_ci		return -1;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci	spin_unlock_bh(&tpg->tpg_state_lock);
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/*
2238c2ecf20Sopenharmony_ci	 * Here we serialize access across the TIQN+TPG Tuple.
2248c2ecf20Sopenharmony_ci	 */
2258c2ecf20Sopenharmony_ci	ret = down_interruptible(&tpg->np_login_sem);
2268c2ecf20Sopenharmony_ci	if (ret != 0)
2278c2ecf20Sopenharmony_ci		return -1;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	spin_lock_bh(&tpg->tpg_state_lock);
2308c2ecf20Sopenharmony_ci	if (tpg->tpg_state != TPG_STATE_ACTIVE) {
2318c2ecf20Sopenharmony_ci		spin_unlock_bh(&tpg->tpg_state_lock);
2328c2ecf20Sopenharmony_ci		up(&tpg->np_login_sem);
2338c2ecf20Sopenharmony_ci		return -1;
2348c2ecf20Sopenharmony_ci	}
2358c2ecf20Sopenharmony_ci	spin_unlock_bh(&tpg->tpg_state_lock);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_civoid iscsit_login_kref_put(struct kref *kref)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	struct iscsi_tpg_np *tpg_np = container_of(kref,
2438c2ecf20Sopenharmony_ci				struct iscsi_tpg_np, tpg_np_kref);
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	complete(&tpg_np->tpg_np_comp);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ciint iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
2498c2ecf20Sopenharmony_ci		       struct iscsi_tpg_np *tpg_np)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	up(&tpg->np_login_sem);
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	if (tpg_np)
2568c2ecf20Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (tiqn)
2598c2ecf20Sopenharmony_ci		iscsit_put_tiqn_for_login(tiqn);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	return 0;
2628c2ecf20Sopenharmony_ci}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cibool iscsit_check_np_match(
2658c2ecf20Sopenharmony_ci	struct sockaddr_storage *sockaddr,
2668c2ecf20Sopenharmony_ci	struct iscsi_np *np,
2678c2ecf20Sopenharmony_ci	int network_transport)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct sockaddr_in *sock_in, *sock_in_e;
2708c2ecf20Sopenharmony_ci	struct sockaddr_in6 *sock_in6, *sock_in6_e;
2718c2ecf20Sopenharmony_ci	bool ip_match = false;
2728c2ecf20Sopenharmony_ci	u16 port, port_e;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (sockaddr->ss_family == AF_INET6) {
2758c2ecf20Sopenharmony_ci		sock_in6 = (struct sockaddr_in6 *)sockaddr;
2768c2ecf20Sopenharmony_ci		sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci		if (!memcmp(&sock_in6->sin6_addr.in6_u,
2798c2ecf20Sopenharmony_ci			    &sock_in6_e->sin6_addr.in6_u,
2808c2ecf20Sopenharmony_ci			    sizeof(struct in6_addr)))
2818c2ecf20Sopenharmony_ci			ip_match = true;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		port = ntohs(sock_in6->sin6_port);
2848c2ecf20Sopenharmony_ci		port_e = ntohs(sock_in6_e->sin6_port);
2858c2ecf20Sopenharmony_ci	} else {
2868c2ecf20Sopenharmony_ci		sock_in = (struct sockaddr_in *)sockaddr;
2878c2ecf20Sopenharmony_ci		sock_in_e = (struct sockaddr_in *)&np->np_sockaddr;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci		if (sock_in->sin_addr.s_addr == sock_in_e->sin_addr.s_addr)
2908c2ecf20Sopenharmony_ci			ip_match = true;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci		port = ntohs(sock_in->sin_port);
2938c2ecf20Sopenharmony_ci		port_e = ntohs(sock_in_e->sin_port);
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	if (ip_match && (port_e == port) &&
2978c2ecf20Sopenharmony_ci	    (np->np_network_transport == network_transport))
2988c2ecf20Sopenharmony_ci		return true;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	return false;
3018c2ecf20Sopenharmony_ci}
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_cistatic struct iscsi_np *iscsit_get_np(
3048c2ecf20Sopenharmony_ci	struct sockaddr_storage *sockaddr,
3058c2ecf20Sopenharmony_ci	int network_transport)
3068c2ecf20Sopenharmony_ci{
3078c2ecf20Sopenharmony_ci	struct iscsi_np *np;
3088c2ecf20Sopenharmony_ci	bool match;
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	lockdep_assert_held(&np_lock);
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	list_for_each_entry(np, &g_np_list, np_list) {
3138c2ecf20Sopenharmony_ci		spin_lock_bh(&np->np_thread_lock);
3148c2ecf20Sopenharmony_ci		if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
3158c2ecf20Sopenharmony_ci			spin_unlock_bh(&np->np_thread_lock);
3168c2ecf20Sopenharmony_ci			continue;
3178c2ecf20Sopenharmony_ci		}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		match = iscsit_check_np_match(sockaddr, np, network_transport);
3208c2ecf20Sopenharmony_ci		if (match) {
3218c2ecf20Sopenharmony_ci			/*
3228c2ecf20Sopenharmony_ci			 * Increment the np_exports reference count now to
3238c2ecf20Sopenharmony_ci			 * prevent iscsit_del_np() below from being called
3248c2ecf20Sopenharmony_ci			 * while iscsi_tpg_add_network_portal() is called.
3258c2ecf20Sopenharmony_ci			 */
3268c2ecf20Sopenharmony_ci			np->np_exports++;
3278c2ecf20Sopenharmony_ci			spin_unlock_bh(&np->np_thread_lock);
3288c2ecf20Sopenharmony_ci			return np;
3298c2ecf20Sopenharmony_ci		}
3308c2ecf20Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
3318c2ecf20Sopenharmony_ci	}
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	return NULL;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_cistruct iscsi_np *iscsit_add_np(
3378c2ecf20Sopenharmony_ci	struct sockaddr_storage *sockaddr,
3388c2ecf20Sopenharmony_ci	int network_transport)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct iscsi_np *np;
3418c2ecf20Sopenharmony_ci	int ret;
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	mutex_lock(&np_lock);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	/*
3468c2ecf20Sopenharmony_ci	 * Locate the existing struct iscsi_np if already active..
3478c2ecf20Sopenharmony_ci	 */
3488c2ecf20Sopenharmony_ci	np = iscsit_get_np(sockaddr, network_transport);
3498c2ecf20Sopenharmony_ci	if (np) {
3508c2ecf20Sopenharmony_ci		mutex_unlock(&np_lock);
3518c2ecf20Sopenharmony_ci		return np;
3528c2ecf20Sopenharmony_ci	}
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_KERNEL);
3558c2ecf20Sopenharmony_ci	if (!np) {
3568c2ecf20Sopenharmony_ci		mutex_unlock(&np_lock);
3578c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	np->np_flags |= NPF_IP_NETWORK;
3618c2ecf20Sopenharmony_ci	np->np_network_transport = network_transport;
3628c2ecf20Sopenharmony_ci	spin_lock_init(&np->np_thread_lock);
3638c2ecf20Sopenharmony_ci	init_completion(&np->np_restart_comp);
3648c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&np->np_list);
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	timer_setup(&np->np_login_timer, iscsi_handle_login_thread_timeout, 0);
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	ret = iscsi_target_setup_login_socket(np, sockaddr);
3698c2ecf20Sopenharmony_ci	if (ret != 0) {
3708c2ecf20Sopenharmony_ci		kfree(np);
3718c2ecf20Sopenharmony_ci		mutex_unlock(&np_lock);
3728c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci	np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np");
3768c2ecf20Sopenharmony_ci	if (IS_ERR(np->np_thread)) {
3778c2ecf20Sopenharmony_ci		pr_err("Unable to create kthread: iscsi_np\n");
3788c2ecf20Sopenharmony_ci		ret = PTR_ERR(np->np_thread);
3798c2ecf20Sopenharmony_ci		kfree(np);
3808c2ecf20Sopenharmony_ci		mutex_unlock(&np_lock);
3818c2ecf20Sopenharmony_ci		return ERR_PTR(ret);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci	/*
3848c2ecf20Sopenharmony_ci	 * Increment the np_exports reference count now to prevent
3858c2ecf20Sopenharmony_ci	 * iscsit_del_np() below from being run while a new call to
3868c2ecf20Sopenharmony_ci	 * iscsi_tpg_add_network_portal() for a matching iscsi_np is
3878c2ecf20Sopenharmony_ci	 * active.  We don't need to hold np->np_thread_lock at this
3888c2ecf20Sopenharmony_ci	 * point because iscsi_np has not been added to g_np_list yet.
3898c2ecf20Sopenharmony_ci	 */
3908c2ecf20Sopenharmony_ci	np->np_exports = 1;
3918c2ecf20Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	list_add_tail(&np->np_list, &g_np_list);
3948c2ecf20Sopenharmony_ci	mutex_unlock(&np_lock);
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	pr_debug("CORE[0] - Added Network Portal: %pISpc on %s\n",
3978c2ecf20Sopenharmony_ci		&np->np_sockaddr, np->np_transport->name);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci	return np;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ciint iscsit_reset_np_thread(
4038c2ecf20Sopenharmony_ci	struct iscsi_np *np,
4048c2ecf20Sopenharmony_ci	struct iscsi_tpg_np *tpg_np,
4058c2ecf20Sopenharmony_ci	struct iscsi_portal_group *tpg,
4068c2ecf20Sopenharmony_ci	bool shutdown)
4078c2ecf20Sopenharmony_ci{
4088c2ecf20Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
4098c2ecf20Sopenharmony_ci	if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) {
4108c2ecf20Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
4118c2ecf20Sopenharmony_ci		return 0;
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_RESET;
4148c2ecf20Sopenharmony_ci	atomic_inc(&np->np_reset_count);
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (np->np_thread) {
4178c2ecf20Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
4188c2ecf20Sopenharmony_ci		send_sig(SIGINT, np->np_thread, 1);
4198c2ecf20Sopenharmony_ci		wait_for_completion(&np->np_restart_comp);
4208c2ecf20Sopenharmony_ci		spin_lock_bh(&np->np_thread_lock);
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (tpg_np && shutdown) {
4258c2ecf20Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci		wait_for_completion(&tpg_np->tpg_np_comp);
4288c2ecf20Sopenharmony_ci	}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	return 0;
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_cistatic void iscsit_free_np(struct iscsi_np *np)
4348c2ecf20Sopenharmony_ci{
4358c2ecf20Sopenharmony_ci	if (np->np_socket)
4368c2ecf20Sopenharmony_ci		sock_release(np->np_socket);
4378c2ecf20Sopenharmony_ci}
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciint iscsit_del_np(struct iscsi_np *np)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
4428c2ecf20Sopenharmony_ci	np->np_exports--;
4438c2ecf20Sopenharmony_ci	if (np->np_exports) {
4448c2ecf20Sopenharmony_ci		np->enabled = true;
4458c2ecf20Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
4468c2ecf20Sopenharmony_ci		return 0;
4478c2ecf20Sopenharmony_ci	}
4488c2ecf20Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN;
4498c2ecf20Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if (np->np_thread) {
4528c2ecf20Sopenharmony_ci		/*
4538c2ecf20Sopenharmony_ci		 * We need to send the signal to wakeup Linux/Net
4548c2ecf20Sopenharmony_ci		 * which may be sleeping in sock_accept()..
4558c2ecf20Sopenharmony_ci		 */
4568c2ecf20Sopenharmony_ci		send_sig(SIGINT, np->np_thread, 1);
4578c2ecf20Sopenharmony_ci		kthread_stop(np->np_thread);
4588c2ecf20Sopenharmony_ci		np->np_thread = NULL;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	np->np_transport->iscsit_free_np(np);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	mutex_lock(&np_lock);
4648c2ecf20Sopenharmony_ci	list_del(&np->np_list);
4658c2ecf20Sopenharmony_ci	mutex_unlock(&np_lock);
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	pr_debug("CORE[0] - Removed Network Portal: %pISpc on %s\n",
4688c2ecf20Sopenharmony_ci		&np->np_sockaddr, np->np_transport->name);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	iscsit_put_transport(np->np_transport);
4718c2ecf20Sopenharmony_ci	kfree(np);
4728c2ecf20Sopenharmony_ci	return 0;
4738c2ecf20Sopenharmony_ci}
4748c2ecf20Sopenharmony_ci
4758c2ecf20Sopenharmony_cistatic void iscsit_get_rx_pdu(struct iscsi_conn *);
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ciint iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
4788c2ecf20Sopenharmony_ci{
4798c2ecf20Sopenharmony_ci	return iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
4808c2ecf20Sopenharmony_ci}
4818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_queue_rsp);
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_civoid iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
4868c2ecf20Sopenharmony_ci	if (!list_empty(&cmd->i_conn_node))
4878c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
4888c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	__iscsit_free_cmd(cmd, true);
4918c2ecf20Sopenharmony_ci}
4928c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_aborted_task);
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cistatic void iscsit_do_crypto_hash_buf(struct ahash_request *, const void *,
4958c2ecf20Sopenharmony_ci				      u32, u32, const void *, void *);
4968c2ecf20Sopenharmony_cistatic void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *);
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic int
4998c2ecf20Sopenharmony_ciiscsit_xmit_nondatain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
5008c2ecf20Sopenharmony_ci			  const void *data_buf, u32 data_buf_len)
5018c2ecf20Sopenharmony_ci{
5028c2ecf20Sopenharmony_ci	struct iscsi_hdr *hdr = (struct iscsi_hdr *)cmd->pdu;
5038c2ecf20Sopenharmony_ci	struct kvec *iov;
5048c2ecf20Sopenharmony_ci	u32 niov = 0, tx_size = ISCSI_HDR_LEN;
5058c2ecf20Sopenharmony_ci	int ret;
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci	iov = &cmd->iov_misc[0];
5088c2ecf20Sopenharmony_ci	iov[niov].iov_base	= cmd->pdu;
5098c2ecf20Sopenharmony_ci	iov[niov++].iov_len	= ISCSI_HDR_LEN;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (conn->conn_ops->HeaderDigest) {
5128c2ecf20Sopenharmony_ci		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		iscsit_do_crypto_hash_buf(conn->conn_tx_hash, hdr,
5158c2ecf20Sopenharmony_ci					  ISCSI_HDR_LEN, 0, NULL,
5168c2ecf20Sopenharmony_ci					  header_digest);
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		iov[0].iov_len += ISCSI_CRC_LEN;
5198c2ecf20Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
5208c2ecf20Sopenharmony_ci		pr_debug("Attaching CRC32C HeaderDigest"
5218c2ecf20Sopenharmony_ci			 " to opcode 0x%x 0x%08x\n",
5228c2ecf20Sopenharmony_ci			 hdr->opcode, *header_digest);
5238c2ecf20Sopenharmony_ci	}
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci	if (data_buf_len) {
5268c2ecf20Sopenharmony_ci		u32 padding = ((-data_buf_len) & 3);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		iov[niov].iov_base	= (void *)data_buf;
5298c2ecf20Sopenharmony_ci		iov[niov++].iov_len	= data_buf_len;
5308c2ecf20Sopenharmony_ci		tx_size += data_buf_len;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci		if (padding != 0) {
5338c2ecf20Sopenharmony_ci			iov[niov].iov_base = &cmd->pad_bytes;
5348c2ecf20Sopenharmony_ci			iov[niov++].iov_len = padding;
5358c2ecf20Sopenharmony_ci			tx_size += padding;
5368c2ecf20Sopenharmony_ci			pr_debug("Attaching %u additional"
5378c2ecf20Sopenharmony_ci				 " padding bytes.\n", padding);
5388c2ecf20Sopenharmony_ci		}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
5418c2ecf20Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_tx_hash,
5428c2ecf20Sopenharmony_ci						  data_buf, data_buf_len,
5438c2ecf20Sopenharmony_ci						  padding, &cmd->pad_bytes,
5448c2ecf20Sopenharmony_ci						  &cmd->data_crc);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci			iov[niov].iov_base = &cmd->data_crc;
5478c2ecf20Sopenharmony_ci			iov[niov++].iov_len = ISCSI_CRC_LEN;
5488c2ecf20Sopenharmony_ci			tx_size += ISCSI_CRC_LEN;
5498c2ecf20Sopenharmony_ci			pr_debug("Attached DataDigest for %u"
5508c2ecf20Sopenharmony_ci				 " bytes opcode 0x%x, CRC 0x%08x\n",
5518c2ecf20Sopenharmony_ci				 data_buf_len, hdr->opcode, cmd->data_crc);
5528c2ecf20Sopenharmony_ci		}
5538c2ecf20Sopenharmony_ci	}
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	cmd->iov_misc_count = niov;
5568c2ecf20Sopenharmony_ci	cmd->tx_size = tx_size;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	ret = iscsit_send_tx_data(cmd, conn, 1);
5598c2ecf20Sopenharmony_ci	if (ret < 0) {
5608c2ecf20Sopenharmony_ci		iscsit_tx_thread_wait_for_tcp(conn);
5618c2ecf20Sopenharmony_ci		return ret;
5628c2ecf20Sopenharmony_ci	}
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return 0;
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_cistatic int iscsit_map_iovec(struct iscsi_cmd *cmd, struct kvec *iov, int nvec,
5688c2ecf20Sopenharmony_ci			    u32 data_offset, u32 data_length);
5698c2ecf20Sopenharmony_cistatic void iscsit_unmap_iovec(struct iscsi_cmd *);
5708c2ecf20Sopenharmony_cistatic u32 iscsit_do_crypto_hash_sg(struct ahash_request *, struct iscsi_cmd *,
5718c2ecf20Sopenharmony_ci				    u32, u32, u32, u8 *);
5728c2ecf20Sopenharmony_cistatic int
5738c2ecf20Sopenharmony_ciiscsit_xmit_datain_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
5748c2ecf20Sopenharmony_ci		       const struct iscsi_datain *datain)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct kvec *iov;
5778c2ecf20Sopenharmony_ci	u32 iov_count = 0, tx_size = 0;
5788c2ecf20Sopenharmony_ci	int ret, iov_ret;
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	iov = &cmd->iov_data[0];
5818c2ecf20Sopenharmony_ci	iov[iov_count].iov_base	= cmd->pdu;
5828c2ecf20Sopenharmony_ci	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
5838c2ecf20Sopenharmony_ci	tx_size += ISCSI_HDR_LEN;
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci	if (conn->conn_ops->HeaderDigest) {
5868c2ecf20Sopenharmony_ci		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci		iscsit_do_crypto_hash_buf(conn->conn_tx_hash, cmd->pdu,
5898c2ecf20Sopenharmony_ci					  ISCSI_HDR_LEN, 0, NULL,
5908c2ecf20Sopenharmony_ci					  header_digest);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci		iov[0].iov_len += ISCSI_CRC_LEN;
5938c2ecf20Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci		pr_debug("Attaching CRC32 HeaderDigest for DataIN PDU 0x%08x\n",
5968c2ecf20Sopenharmony_ci			 *header_digest);
5978c2ecf20Sopenharmony_ci	}
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[iov_count],
6008c2ecf20Sopenharmony_ci				   cmd->orig_iov_data_count - (iov_count + 2),
6018c2ecf20Sopenharmony_ci				   datain->offset, datain->length);
6028c2ecf20Sopenharmony_ci	if (iov_ret < 0)
6038c2ecf20Sopenharmony_ci		return -1;
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	iov_count += iov_ret;
6068c2ecf20Sopenharmony_ci	tx_size += datain->length;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	cmd->padding = ((-datain->length) & 3);
6098c2ecf20Sopenharmony_ci	if (cmd->padding) {
6108c2ecf20Sopenharmony_ci		iov[iov_count].iov_base		= cmd->pad_bytes;
6118c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len	= cmd->padding;
6128c2ecf20Sopenharmony_ci		tx_size += cmd->padding;
6138c2ecf20Sopenharmony_ci
6148c2ecf20Sopenharmony_ci		pr_debug("Attaching %u padding bytes\n", cmd->padding);
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
6188c2ecf20Sopenharmony_ci		cmd->data_crc = iscsit_do_crypto_hash_sg(conn->conn_tx_hash,
6198c2ecf20Sopenharmony_ci							 cmd, datain->offset,
6208c2ecf20Sopenharmony_ci							 datain->length,
6218c2ecf20Sopenharmony_ci							 cmd->padding,
6228c2ecf20Sopenharmony_ci							 cmd->pad_bytes);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci		iov[iov_count].iov_base	= &cmd->data_crc;
6258c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
6268c2ecf20Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_ci		pr_debug("Attached CRC32C DataDigest %d bytes, crc 0x%08x\n",
6298c2ecf20Sopenharmony_ci			 datain->length + cmd->padding, cmd->data_crc);
6308c2ecf20Sopenharmony_ci	}
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	cmd->iov_data_count = iov_count;
6338c2ecf20Sopenharmony_ci	cmd->tx_size = tx_size;
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	ret = iscsit_fe_sendpage_sg(cmd, conn);
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	iscsit_unmap_iovec(cmd);
6388c2ecf20Sopenharmony_ci
6398c2ecf20Sopenharmony_ci	if (ret < 0) {
6408c2ecf20Sopenharmony_ci		iscsit_tx_thread_wait_for_tcp(conn);
6418c2ecf20Sopenharmony_ci		return ret;
6428c2ecf20Sopenharmony_ci	}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_ci	return 0;
6458c2ecf20Sopenharmony_ci}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_cistatic int iscsit_xmit_pdu(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
6488c2ecf20Sopenharmony_ci			   struct iscsi_datain_req *dr, const void *buf,
6498c2ecf20Sopenharmony_ci			   u32 buf_len)
6508c2ecf20Sopenharmony_ci{
6518c2ecf20Sopenharmony_ci	if (dr)
6528c2ecf20Sopenharmony_ci		return iscsit_xmit_datain_pdu(conn, cmd, buf);
6538c2ecf20Sopenharmony_ci	else
6548c2ecf20Sopenharmony_ci		return iscsit_xmit_nondatain_pdu(conn, cmd, buf, buf_len);
6558c2ecf20Sopenharmony_ci}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_cistatic enum target_prot_op iscsit_get_sup_prot_ops(struct iscsi_conn *conn)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	return TARGET_PROT_NORMAL;
6608c2ecf20Sopenharmony_ci}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_cistatic struct iscsit_transport iscsi_target_transport = {
6638c2ecf20Sopenharmony_ci	.name			= "iSCSI/TCP",
6648c2ecf20Sopenharmony_ci	.transport_type		= ISCSI_TCP,
6658c2ecf20Sopenharmony_ci	.rdma_shutdown		= false,
6668c2ecf20Sopenharmony_ci	.owner			= NULL,
6678c2ecf20Sopenharmony_ci	.iscsit_setup_np	= iscsit_setup_np,
6688c2ecf20Sopenharmony_ci	.iscsit_accept_np	= iscsit_accept_np,
6698c2ecf20Sopenharmony_ci	.iscsit_free_np		= iscsit_free_np,
6708c2ecf20Sopenharmony_ci	.iscsit_get_login_rx	= iscsit_get_login_rx,
6718c2ecf20Sopenharmony_ci	.iscsit_put_login_tx	= iscsit_put_login_tx,
6728c2ecf20Sopenharmony_ci	.iscsit_get_dataout	= iscsit_build_r2ts_for_cmd,
6738c2ecf20Sopenharmony_ci	.iscsit_immediate_queue	= iscsit_immediate_queue,
6748c2ecf20Sopenharmony_ci	.iscsit_response_queue	= iscsit_response_queue,
6758c2ecf20Sopenharmony_ci	.iscsit_queue_data_in	= iscsit_queue_rsp,
6768c2ecf20Sopenharmony_ci	.iscsit_queue_status	= iscsit_queue_rsp,
6778c2ecf20Sopenharmony_ci	.iscsit_aborted_task	= iscsit_aborted_task,
6788c2ecf20Sopenharmony_ci	.iscsit_xmit_pdu	= iscsit_xmit_pdu,
6798c2ecf20Sopenharmony_ci	.iscsit_get_rx_pdu	= iscsit_get_rx_pdu,
6808c2ecf20Sopenharmony_ci	.iscsit_get_sup_prot_ops = iscsit_get_sup_prot_ops,
6818c2ecf20Sopenharmony_ci};
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_cistatic int __init iscsi_target_init_module(void)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	int ret = 0, size;
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	pr_debug("iSCSI-Target "ISCSIT_VERSION"\n");
6888c2ecf20Sopenharmony_ci	iscsit_global = kzalloc(sizeof(*iscsit_global), GFP_KERNEL);
6898c2ecf20Sopenharmony_ci	if (!iscsit_global)
6908c2ecf20Sopenharmony_ci		return -1;
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_ci	spin_lock_init(&iscsit_global->ts_bitmap_lock);
6938c2ecf20Sopenharmony_ci	mutex_init(&auth_id_lock);
6948c2ecf20Sopenharmony_ci	idr_init(&tiqn_idr);
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	ret = target_register_template(&iscsi_ops);
6978c2ecf20Sopenharmony_ci	if (ret)
6988c2ecf20Sopenharmony_ci		goto out;
6998c2ecf20Sopenharmony_ci
7008c2ecf20Sopenharmony_ci	size = BITS_TO_LONGS(ISCSIT_BITMAP_BITS) * sizeof(long);
7018c2ecf20Sopenharmony_ci	iscsit_global->ts_bitmap = vzalloc(size);
7028c2ecf20Sopenharmony_ci	if (!iscsit_global->ts_bitmap)
7038c2ecf20Sopenharmony_ci		goto configfs_out;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	lio_qr_cache = kmem_cache_create("lio_qr_cache",
7068c2ecf20Sopenharmony_ci			sizeof(struct iscsi_queue_req),
7078c2ecf20Sopenharmony_ci			__alignof__(struct iscsi_queue_req), 0, NULL);
7088c2ecf20Sopenharmony_ci	if (!lio_qr_cache) {
7098c2ecf20Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
7108c2ecf20Sopenharmony_ci				" lio_qr_cache\n");
7118c2ecf20Sopenharmony_ci		goto bitmap_out;
7128c2ecf20Sopenharmony_ci	}
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci	lio_dr_cache = kmem_cache_create("lio_dr_cache",
7158c2ecf20Sopenharmony_ci			sizeof(struct iscsi_datain_req),
7168c2ecf20Sopenharmony_ci			__alignof__(struct iscsi_datain_req), 0, NULL);
7178c2ecf20Sopenharmony_ci	if (!lio_dr_cache) {
7188c2ecf20Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
7198c2ecf20Sopenharmony_ci				" lio_dr_cache\n");
7208c2ecf20Sopenharmony_ci		goto qr_out;
7218c2ecf20Sopenharmony_ci	}
7228c2ecf20Sopenharmony_ci
7238c2ecf20Sopenharmony_ci	lio_ooo_cache = kmem_cache_create("lio_ooo_cache",
7248c2ecf20Sopenharmony_ci			sizeof(struct iscsi_ooo_cmdsn),
7258c2ecf20Sopenharmony_ci			__alignof__(struct iscsi_ooo_cmdsn), 0, NULL);
7268c2ecf20Sopenharmony_ci	if (!lio_ooo_cache) {
7278c2ecf20Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
7288c2ecf20Sopenharmony_ci				" lio_ooo_cache\n");
7298c2ecf20Sopenharmony_ci		goto dr_out;
7308c2ecf20Sopenharmony_ci	}
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	lio_r2t_cache = kmem_cache_create("lio_r2t_cache",
7338c2ecf20Sopenharmony_ci			sizeof(struct iscsi_r2t), __alignof__(struct iscsi_r2t),
7348c2ecf20Sopenharmony_ci			0, NULL);
7358c2ecf20Sopenharmony_ci	if (!lio_r2t_cache) {
7368c2ecf20Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
7378c2ecf20Sopenharmony_ci				" lio_r2t_cache\n");
7388c2ecf20Sopenharmony_ci		goto ooo_out;
7398c2ecf20Sopenharmony_ci	}
7408c2ecf20Sopenharmony_ci
7418c2ecf20Sopenharmony_ci	iscsit_register_transport(&iscsi_target_transport);
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	if (iscsit_load_discovery_tpg() < 0)
7448c2ecf20Sopenharmony_ci		goto r2t_out;
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return ret;
7478c2ecf20Sopenharmony_cir2t_out:
7488c2ecf20Sopenharmony_ci	iscsit_unregister_transport(&iscsi_target_transport);
7498c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_r2t_cache);
7508c2ecf20Sopenharmony_ciooo_out:
7518c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_ooo_cache);
7528c2ecf20Sopenharmony_cidr_out:
7538c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_dr_cache);
7548c2ecf20Sopenharmony_ciqr_out:
7558c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_qr_cache);
7568c2ecf20Sopenharmony_cibitmap_out:
7578c2ecf20Sopenharmony_ci	vfree(iscsit_global->ts_bitmap);
7588c2ecf20Sopenharmony_ciconfigfs_out:
7598c2ecf20Sopenharmony_ci	/* XXX: this probably wants it to be it's own unwind step.. */
7608c2ecf20Sopenharmony_ci	if (iscsit_global->discovery_tpg)
7618c2ecf20Sopenharmony_ci		iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
7628c2ecf20Sopenharmony_ci	target_unregister_template(&iscsi_ops);
7638c2ecf20Sopenharmony_ciout:
7648c2ecf20Sopenharmony_ci	kfree(iscsit_global);
7658c2ecf20Sopenharmony_ci	return -ENOMEM;
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_cistatic void __exit iscsi_target_cleanup_module(void)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	iscsit_release_discovery_tpg();
7718c2ecf20Sopenharmony_ci	iscsit_unregister_transport(&iscsi_target_transport);
7728c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_qr_cache);
7738c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_dr_cache);
7748c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_ooo_cache);
7758c2ecf20Sopenharmony_ci	kmem_cache_destroy(lio_r2t_cache);
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	/*
7788c2ecf20Sopenharmony_ci	 * Shutdown discovery sessions and disable discovery TPG
7798c2ecf20Sopenharmony_ci	 */
7808c2ecf20Sopenharmony_ci	if (iscsit_global->discovery_tpg)
7818c2ecf20Sopenharmony_ci		iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	target_unregister_template(&iscsi_ops);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	vfree(iscsit_global->ts_bitmap);
7868c2ecf20Sopenharmony_ci	kfree(iscsit_global);
7878c2ecf20Sopenharmony_ci}
7888c2ecf20Sopenharmony_ci
7898c2ecf20Sopenharmony_ciint iscsit_add_reject(
7908c2ecf20Sopenharmony_ci	struct iscsi_conn *conn,
7918c2ecf20Sopenharmony_ci	u8 reason,
7928c2ecf20Sopenharmony_ci	unsigned char *buf)
7938c2ecf20Sopenharmony_ci{
7948c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
7978c2ecf20Sopenharmony_ci	if (!cmd)
7988c2ecf20Sopenharmony_ci		return -1;
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_REJECT;
8018c2ecf20Sopenharmony_ci	cmd->reject_reason = reason;
8028c2ecf20Sopenharmony_ci
8038c2ecf20Sopenharmony_ci	cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
8048c2ecf20Sopenharmony_ci	if (!cmd->buf_ptr) {
8058c2ecf20Sopenharmony_ci		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
8068c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
8078c2ecf20Sopenharmony_ci		return -1;
8088c2ecf20Sopenharmony_ci	}
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
8118c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
8128c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
8138c2ecf20Sopenharmony_ci
8148c2ecf20Sopenharmony_ci	cmd->i_state = ISTATE_SEND_REJECT;
8158c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci	return -1;
8188c2ecf20Sopenharmony_ci}
8198c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_add_reject);
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_cistatic int iscsit_add_reject_from_cmd(
8228c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
8238c2ecf20Sopenharmony_ci	u8 reason,
8248c2ecf20Sopenharmony_ci	bool add_to_conn,
8258c2ecf20Sopenharmony_ci	unsigned char *buf)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
8288c2ecf20Sopenharmony_ci	const bool do_put = cmd->se_cmd.se_tfo != NULL;
8298c2ecf20Sopenharmony_ci
8308c2ecf20Sopenharmony_ci	if (!cmd->conn) {
8318c2ecf20Sopenharmony_ci		pr_err("cmd->conn is NULL for ITT: 0x%08x\n",
8328c2ecf20Sopenharmony_ci				cmd->init_task_tag);
8338c2ecf20Sopenharmony_ci		return -1;
8348c2ecf20Sopenharmony_ci	}
8358c2ecf20Sopenharmony_ci	conn = cmd->conn;
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_REJECT;
8388c2ecf20Sopenharmony_ci	cmd->reject_reason = reason;
8398c2ecf20Sopenharmony_ci
8408c2ecf20Sopenharmony_ci	cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
8418c2ecf20Sopenharmony_ci	if (!cmd->buf_ptr) {
8428c2ecf20Sopenharmony_ci		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
8438c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
8448c2ecf20Sopenharmony_ci		return -1;
8458c2ecf20Sopenharmony_ci	}
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (add_to_conn) {
8488c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
8498c2ecf20Sopenharmony_ci		list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
8508c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
8518c2ecf20Sopenharmony_ci	}
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	cmd->i_state = ISTATE_SEND_REJECT;
8548c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
8558c2ecf20Sopenharmony_ci	/*
8568c2ecf20Sopenharmony_ci	 * Perform the kref_put now if se_cmd has already been setup by
8578c2ecf20Sopenharmony_ci	 * scsit_setup_scsi_cmd()
8588c2ecf20Sopenharmony_ci	 */
8598c2ecf20Sopenharmony_ci	if (do_put) {
8608c2ecf20Sopenharmony_ci		pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n");
8618c2ecf20Sopenharmony_ci		target_put_sess_cmd(&cmd->se_cmd);
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci	return -1;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int iscsit_add_reject_cmd(struct iscsi_cmd *cmd, u8 reason,
8678c2ecf20Sopenharmony_ci				 unsigned char *buf)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	return iscsit_add_reject_from_cmd(cmd, reason, true, buf);
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ciint iscsit_reject_cmd(struct iscsi_cmd *cmd, u8 reason, unsigned char *buf)
8738c2ecf20Sopenharmony_ci{
8748c2ecf20Sopenharmony_ci	return iscsit_add_reject_from_cmd(cmd, reason, false, buf);
8758c2ecf20Sopenharmony_ci}
8768c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_reject_cmd);
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_ci/*
8798c2ecf20Sopenharmony_ci * Map some portion of the allocated scatterlist to an iovec, suitable for
8808c2ecf20Sopenharmony_ci * kernel sockets to copy data in/out.
8818c2ecf20Sopenharmony_ci */
8828c2ecf20Sopenharmony_cistatic int iscsit_map_iovec(struct iscsi_cmd *cmd, struct kvec *iov, int nvec,
8838c2ecf20Sopenharmony_ci			    u32 data_offset, u32 data_length)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	u32 i = 0, orig_data_length = data_length;
8868c2ecf20Sopenharmony_ci	struct scatterlist *sg;
8878c2ecf20Sopenharmony_ci	unsigned int page_off;
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/*
8908c2ecf20Sopenharmony_ci	 * We know each entry in t_data_sg contains a page.
8918c2ecf20Sopenharmony_ci	 */
8928c2ecf20Sopenharmony_ci	u32 ent = data_offset / PAGE_SIZE;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	if (!data_length)
8958c2ecf20Sopenharmony_ci		return 0;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	if (ent >= cmd->se_cmd.t_data_nents) {
8988c2ecf20Sopenharmony_ci		pr_err("Initial page entry out-of-bounds\n");
8998c2ecf20Sopenharmony_ci		goto overflow;
9008c2ecf20Sopenharmony_ci	}
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	sg = &cmd->se_cmd.t_data_sg[ent];
9038c2ecf20Sopenharmony_ci	page_off = (data_offset % PAGE_SIZE);
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci	cmd->first_data_sg = sg;
9068c2ecf20Sopenharmony_ci	cmd->first_data_sg_off = page_off;
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci	while (data_length) {
9098c2ecf20Sopenharmony_ci		u32 cur_len;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(!sg || i >= nvec))
9128c2ecf20Sopenharmony_ci			goto overflow;
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_ci		cur_len = min_t(u32, data_length, sg->length - page_off);
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci		iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off;
9178c2ecf20Sopenharmony_ci		iov[i].iov_len = cur_len;
9188c2ecf20Sopenharmony_ci
9198c2ecf20Sopenharmony_ci		data_length -= cur_len;
9208c2ecf20Sopenharmony_ci		page_off = 0;
9218c2ecf20Sopenharmony_ci		sg = sg_next(sg);
9228c2ecf20Sopenharmony_ci		i++;
9238c2ecf20Sopenharmony_ci	}
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	cmd->kmapped_nents = i;
9268c2ecf20Sopenharmony_ci
9278c2ecf20Sopenharmony_ci	return i;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_cioverflow:
9308c2ecf20Sopenharmony_ci	pr_err("offset %d + length %d overflow; %d/%d; sg-list:\n",
9318c2ecf20Sopenharmony_ci	       data_offset, orig_data_length, i, nvec);
9328c2ecf20Sopenharmony_ci	for_each_sg(cmd->se_cmd.t_data_sg, sg,
9338c2ecf20Sopenharmony_ci		    cmd->se_cmd.t_data_nents, i) {
9348c2ecf20Sopenharmony_ci		pr_err("[%d] off %d len %d\n",
9358c2ecf20Sopenharmony_ci		       i, sg->offset, sg->length);
9368c2ecf20Sopenharmony_ci	}
9378c2ecf20Sopenharmony_ci	return -1;
9388c2ecf20Sopenharmony_ci}
9398c2ecf20Sopenharmony_ci
9408c2ecf20Sopenharmony_cistatic void iscsit_unmap_iovec(struct iscsi_cmd *cmd)
9418c2ecf20Sopenharmony_ci{
9428c2ecf20Sopenharmony_ci	u32 i;
9438c2ecf20Sopenharmony_ci	struct scatterlist *sg;
9448c2ecf20Sopenharmony_ci
9458c2ecf20Sopenharmony_ci	sg = cmd->first_data_sg;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	for (i = 0; i < cmd->kmapped_nents; i++)
9488c2ecf20Sopenharmony_ci		kunmap(sg_page(&sg[i]));
9498c2ecf20Sopenharmony_ci}
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_cistatic void iscsit_ack_from_expstatsn(struct iscsi_conn *conn, u32 exp_statsn)
9528c2ecf20Sopenharmony_ci{
9538c2ecf20Sopenharmony_ci	LIST_HEAD(ack_list);
9548c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd, *cmd_p;
9558c2ecf20Sopenharmony_ci
9568c2ecf20Sopenharmony_ci	conn->exp_statsn = exp_statsn;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->RDMAExtensions)
9598c2ecf20Sopenharmony_ci		return;
9608c2ecf20Sopenharmony_ci
9618c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
9628c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_p, &conn->conn_cmd_list, i_conn_node) {
9638c2ecf20Sopenharmony_ci		spin_lock(&cmd->istate_lock);
9648c2ecf20Sopenharmony_ci		if ((cmd->i_state == ISTATE_SENT_STATUS) &&
9658c2ecf20Sopenharmony_ci		    iscsi_sna_lt(cmd->stat_sn, exp_statsn)) {
9668c2ecf20Sopenharmony_ci			cmd->i_state = ISTATE_REMOVE;
9678c2ecf20Sopenharmony_ci			spin_unlock(&cmd->istate_lock);
9688c2ecf20Sopenharmony_ci			list_move_tail(&cmd->i_conn_node, &ack_list);
9698c2ecf20Sopenharmony_ci			continue;
9708c2ecf20Sopenharmony_ci		}
9718c2ecf20Sopenharmony_ci		spin_unlock(&cmd->istate_lock);
9728c2ecf20Sopenharmony_ci	}
9738c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) {
9768c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
9778c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
9788c2ecf20Sopenharmony_ci	}
9798c2ecf20Sopenharmony_ci}
9808c2ecf20Sopenharmony_ci
9818c2ecf20Sopenharmony_cistatic int iscsit_allocate_iovecs(struct iscsi_cmd *cmd)
9828c2ecf20Sopenharmony_ci{
9838c2ecf20Sopenharmony_ci	u32 iov_count = max(1UL, DIV_ROUND_UP(cmd->se_cmd.data_length, PAGE_SIZE));
9848c2ecf20Sopenharmony_ci
9858c2ecf20Sopenharmony_ci	iov_count += ISCSI_IOV_DATA_BUFFER;
9868c2ecf20Sopenharmony_ci	cmd->iov_data = kcalloc(iov_count, sizeof(*cmd->iov_data), GFP_KERNEL);
9878c2ecf20Sopenharmony_ci	if (!cmd->iov_data)
9888c2ecf20Sopenharmony_ci		return -ENOMEM;
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	cmd->orig_iov_data_count = iov_count;
9918c2ecf20Sopenharmony_ci	return 0;
9928c2ecf20Sopenharmony_ci}
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ciint iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
9958c2ecf20Sopenharmony_ci			  unsigned char *buf)
9968c2ecf20Sopenharmony_ci{
9978c2ecf20Sopenharmony_ci	int data_direction, payload_length;
9988c2ecf20Sopenharmony_ci	struct iscsi_scsi_req *hdr;
9998c2ecf20Sopenharmony_ci	int iscsi_task_attr;
10008c2ecf20Sopenharmony_ci	int sam_task_attr;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	atomic_long_inc(&conn->sess->cmd_pdus);
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_scsi_req *) buf;
10058c2ecf20Sopenharmony_ci	payload_length		= ntoh24(hdr->dlength);
10068c2ecf20Sopenharmony_ci
10078c2ecf20Sopenharmony_ci	/* FIXME; Add checks for AdditionalHeaderSegment */
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) &&
10108c2ecf20Sopenharmony_ci	    !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
10118c2ecf20Sopenharmony_ci		pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"
10128c2ecf20Sopenharmony_ci				" not set. Bad iSCSI Initiator.\n");
10138c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10148c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
10158c2ecf20Sopenharmony_ci	}
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
10188c2ecf20Sopenharmony_ci	     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) {
10198c2ecf20Sopenharmony_ci		/*
10208c2ecf20Sopenharmony_ci		 * From RFC-3720 Section 10.3.1:
10218c2ecf20Sopenharmony_ci		 *
10228c2ecf20Sopenharmony_ci		 * "Either or both of R and W MAY be 1 when either the
10238c2ecf20Sopenharmony_ci		 *  Expected Data Transfer Length and/or Bidirectional Read
10248c2ecf20Sopenharmony_ci		 *  Expected Data Transfer Length are 0"
10258c2ecf20Sopenharmony_ci		 *
10268c2ecf20Sopenharmony_ci		 * For this case, go ahead and clear the unnecssary bits
10278c2ecf20Sopenharmony_ci		 * to avoid any confusion with ->data_direction.
10288c2ecf20Sopenharmony_ci		 */
10298c2ecf20Sopenharmony_ci		hdr->flags &= ~ISCSI_FLAG_CMD_READ;
10308c2ecf20Sopenharmony_ci		hdr->flags &= ~ISCSI_FLAG_CMD_WRITE;
10318c2ecf20Sopenharmony_ci
10328c2ecf20Sopenharmony_ci		pr_warn("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"
10338c2ecf20Sopenharmony_ci			" set when Expected Data Transfer Length is 0 for"
10348c2ecf20Sopenharmony_ci			" CDB: 0x%02x, Fixing up flags\n", hdr->cdb[0]);
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_READ) &&
10388c2ecf20Sopenharmony_ci	    !(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) {
10398c2ecf20Sopenharmony_ci		pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"
10408c2ecf20Sopenharmony_ci			" MUST be set if Expected Data Transfer Length is not 0."
10418c2ecf20Sopenharmony_ci			" Bad iSCSI Initiator\n");
10428c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10438c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
10448c2ecf20Sopenharmony_ci	}
10458c2ecf20Sopenharmony_ci
10468c2ecf20Sopenharmony_ci	if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
10478c2ecf20Sopenharmony_ci	    (hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
10488c2ecf20Sopenharmony_ci		pr_err("Bidirectional operations not supported!\n");
10498c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10508c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
10518c2ecf20Sopenharmony_ci	}
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
10548c2ecf20Sopenharmony_ci		pr_err("Illegally set Immediate Bit in iSCSI Initiator"
10558c2ecf20Sopenharmony_ci				" Scsi Command PDU.\n");
10568c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10578c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci
10608c2ecf20Sopenharmony_ci	if (payload_length && !conn->sess->sess_ops->ImmediateData) {
10618c2ecf20Sopenharmony_ci		pr_err("ImmediateData=No but DataSegmentLength=%u,"
10628c2ecf20Sopenharmony_ci			" protocol error.\n", payload_length);
10638c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10648c2ecf20Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci	if ((be32_to_cpu(hdr->data_length) == payload_length) &&
10688c2ecf20Sopenharmony_ci	    (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
10698c2ecf20Sopenharmony_ci		pr_err("Expected Data Transfer Length and Length of"
10708c2ecf20Sopenharmony_ci			" Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"
10718c2ecf20Sopenharmony_ci			" bit is not set protocol error\n");
10728c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10738c2ecf20Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
10748c2ecf20Sopenharmony_ci	}
10758c2ecf20Sopenharmony_ci
10768c2ecf20Sopenharmony_ci	if (payload_length > be32_to_cpu(hdr->data_length)) {
10778c2ecf20Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
10788c2ecf20Sopenharmony_ci			" EDTL: %u, protocol error.\n", payload_length,
10798c2ecf20Sopenharmony_ci				hdr->data_length);
10808c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10818c2ecf20Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
10828c2ecf20Sopenharmony_ci	}
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
10858c2ecf20Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
10868c2ecf20Sopenharmony_ci			" MaxXmitDataSegmentLength: %u, protocol error.\n",
10878c2ecf20Sopenharmony_ci			payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
10888c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10898c2ecf20Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
10908c2ecf20Sopenharmony_ci	}
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	if (payload_length > conn->sess->sess_ops->FirstBurstLength) {
10938c2ecf20Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
10948c2ecf20Sopenharmony_ci			" FirstBurstLength: %u, protocol error.\n",
10958c2ecf20Sopenharmony_ci			payload_length, conn->sess->sess_ops->FirstBurstLength);
10968c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
10978c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
10988c2ecf20Sopenharmony_ci	}
10998c2ecf20Sopenharmony_ci
11008c2ecf20Sopenharmony_ci	data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
11018c2ecf20Sopenharmony_ci			 (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :
11028c2ecf20Sopenharmony_ci			  DMA_NONE;
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	cmd->data_direction = data_direction;
11058c2ecf20Sopenharmony_ci	iscsi_task_attr = hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK;
11068c2ecf20Sopenharmony_ci	/*
11078c2ecf20Sopenharmony_ci	 * Figure out the SAM Task Attribute for the incoming SCSI CDB
11088c2ecf20Sopenharmony_ci	 */
11098c2ecf20Sopenharmony_ci	if ((iscsi_task_attr == ISCSI_ATTR_UNTAGGED) ||
11108c2ecf20Sopenharmony_ci	    (iscsi_task_attr == ISCSI_ATTR_SIMPLE))
11118c2ecf20Sopenharmony_ci		sam_task_attr = TCM_SIMPLE_TAG;
11128c2ecf20Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_ORDERED)
11138c2ecf20Sopenharmony_ci		sam_task_attr = TCM_ORDERED_TAG;
11148c2ecf20Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_HEAD_OF_QUEUE)
11158c2ecf20Sopenharmony_ci		sam_task_attr = TCM_HEAD_TAG;
11168c2ecf20Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_ACA)
11178c2ecf20Sopenharmony_ci		sam_task_attr = TCM_ACA_TAG;
11188c2ecf20Sopenharmony_ci	else {
11198c2ecf20Sopenharmony_ci		pr_debug("Unknown iSCSI Task Attribute: 0x%02x, using"
11208c2ecf20Sopenharmony_ci			" TCM_SIMPLE_TAG\n", iscsi_task_attr);
11218c2ecf20Sopenharmony_ci		sam_task_attr = TCM_SIMPLE_TAG;
11228c2ecf20Sopenharmony_ci	}
11238c2ecf20Sopenharmony_ci
11248c2ecf20Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_SCSI_CMD;
11258c2ecf20Sopenharmony_ci	cmd->i_state		= ISTATE_NEW_CMD;
11268c2ecf20Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
11278c2ecf20Sopenharmony_ci	cmd->immediate_data	= (payload_length) ? 1 : 0;
11288c2ecf20Sopenharmony_ci	cmd->unsolicited_data	= ((!(hdr->flags & ISCSI_FLAG_CMD_FINAL) &&
11298c2ecf20Sopenharmony_ci				     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) ? 1 : 0);
11308c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data)
11318c2ecf20Sopenharmony_ci		cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA;
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
11348c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_READ)
11358c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag = session_get_next_ttt(conn->sess);
11368c2ecf20Sopenharmony_ci	else
11378c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag = 0xFFFFFFFF;
11388c2ecf20Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
11398c2ecf20Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
11408c2ecf20Sopenharmony_ci	cmd->first_burst_len	= payload_length;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci	if (!conn->sess->sess_ops->RDMAExtensions &&
11438c2ecf20Sopenharmony_ci	     cmd->data_direction == DMA_FROM_DEVICE) {
11448c2ecf20Sopenharmony_ci		struct iscsi_datain_req *dr;
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		dr = iscsit_allocate_datain_req();
11478c2ecf20Sopenharmony_ci		if (!dr)
11488c2ecf20Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
11498c2ecf20Sopenharmony_ci					ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
11508c2ecf20Sopenharmony_ci
11518c2ecf20Sopenharmony_ci		iscsit_attach_datain_req(cmd, dr);
11528c2ecf20Sopenharmony_ci	}
11538c2ecf20Sopenharmony_ci
11548c2ecf20Sopenharmony_ci	/*
11558c2ecf20Sopenharmony_ci	 * Initialize struct se_cmd descriptor from target_core_mod infrastructure
11568c2ecf20Sopenharmony_ci	 */
11578c2ecf20Sopenharmony_ci	transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
11588c2ecf20Sopenharmony_ci			conn->sess->se_sess, be32_to_cpu(hdr->data_length),
11598c2ecf20Sopenharmony_ci			cmd->data_direction, sam_task_attr,
11608c2ecf20Sopenharmony_ci			cmd->sense_buffer + 2, scsilun_to_int(&hdr->lun));
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x,"
11638c2ecf20Sopenharmony_ci		" ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt,
11648c2ecf20Sopenharmony_ci		hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length,
11658c2ecf20Sopenharmony_ci		conn->cid);
11668c2ecf20Sopenharmony_ci
11678c2ecf20Sopenharmony_ci	target_get_sess_cmd(&cmd->se_cmd, true);
11688c2ecf20Sopenharmony_ci
11698c2ecf20Sopenharmony_ci	cmd->se_cmd.tag = (__force u32)cmd->init_task_tag;
11708c2ecf20Sopenharmony_ci	cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, hdr->cdb);
11718c2ecf20Sopenharmony_ci	if (cmd->sense_reason) {
11728c2ecf20Sopenharmony_ci		if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) {
11738c2ecf20Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
11748c2ecf20Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
11758c2ecf20Sopenharmony_ci		}
11768c2ecf20Sopenharmony_ci
11778c2ecf20Sopenharmony_ci		goto attach_cmd;
11788c2ecf20Sopenharmony_ci	}
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd);
11818c2ecf20Sopenharmony_ci	if (cmd->sense_reason)
11828c2ecf20Sopenharmony_ci		goto attach_cmd;
11838c2ecf20Sopenharmony_ci
11848c2ecf20Sopenharmony_ci	cmd->sense_reason = target_cmd_parse_cdb(&cmd->se_cmd);
11858c2ecf20Sopenharmony_ci	if (cmd->sense_reason)
11868c2ecf20Sopenharmony_ci		goto attach_cmd;
11878c2ecf20Sopenharmony_ci
11888c2ecf20Sopenharmony_ci	if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) {
11898c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
11908c2ecf20Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ciattach_cmd:
11948c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
11958c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
11968c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
11978c2ecf20Sopenharmony_ci	/*
11988c2ecf20Sopenharmony_ci	 * Check if we need to delay processing because of ALUA
11998c2ecf20Sopenharmony_ci	 * Active/NonOptimized primary access state..
12008c2ecf20Sopenharmony_ci	 */
12018c2ecf20Sopenharmony_ci	core_alua_check_nonop_delay(&cmd->se_cmd);
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	return 0;
12048c2ecf20Sopenharmony_ci}
12058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_scsi_cmd);
12068c2ecf20Sopenharmony_ci
12078c2ecf20Sopenharmony_civoid iscsit_set_unsolicited_dataout(struct iscsi_cmd *cmd)
12088c2ecf20Sopenharmony_ci{
12098c2ecf20Sopenharmony_ci	iscsit_set_dataout_sequence_values(cmd);
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->dataout_timeout_lock);
12128c2ecf20Sopenharmony_ci	iscsit_start_dataout_timer(cmd, cmd->conn);
12138c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->dataout_timeout_lock);
12148c2ecf20Sopenharmony_ci}
12158c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_set_unsolicited_dataout);
12168c2ecf20Sopenharmony_ci
12178c2ecf20Sopenharmony_ciint iscsit_process_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
12188c2ecf20Sopenharmony_ci			    struct iscsi_scsi_req *hdr)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	int cmdsn_ret = 0;
12218c2ecf20Sopenharmony_ci	/*
12228c2ecf20Sopenharmony_ci	 * Check the CmdSN against ExpCmdSN/MaxCmdSN here if
12238c2ecf20Sopenharmony_ci	 * the Immediate Bit is not set, and no Immediate
12248c2ecf20Sopenharmony_ci	 * Data is attached.
12258c2ecf20Sopenharmony_ci	 *
12268c2ecf20Sopenharmony_ci	 * A PDU/CmdSN carrying Immediate Data can only
12278c2ecf20Sopenharmony_ci	 * be processed after the DataCRC has passed.
12288c2ecf20Sopenharmony_ci	 * If the DataCRC fails, the CmdSN MUST NOT
12298c2ecf20Sopenharmony_ci	 * be acknowledged. (See below)
12308c2ecf20Sopenharmony_ci	 */
12318c2ecf20Sopenharmony_ci	if (!cmd->immediate_data) {
12328c2ecf20Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
12338c2ecf20Sopenharmony_ci					(unsigned char *)hdr, hdr->cmdsn);
12348c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
12358c2ecf20Sopenharmony_ci			return -1;
12368c2ecf20Sopenharmony_ci		else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
12378c2ecf20Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
12388c2ecf20Sopenharmony_ci			return 0;
12398c2ecf20Sopenharmony_ci		}
12408c2ecf20Sopenharmony_ci	}
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	/*
12458c2ecf20Sopenharmony_ci	 * If no Immediate Data is attached, it's OK to return now.
12468c2ecf20Sopenharmony_ci	 */
12478c2ecf20Sopenharmony_ci	if (!cmd->immediate_data) {
12488c2ecf20Sopenharmony_ci		if (!cmd->sense_reason && cmd->unsolicited_data)
12498c2ecf20Sopenharmony_ci			iscsit_set_unsolicited_dataout(cmd);
12508c2ecf20Sopenharmony_ci		if (!cmd->sense_reason)
12518c2ecf20Sopenharmony_ci			return 0;
12528c2ecf20Sopenharmony_ci
12538c2ecf20Sopenharmony_ci		target_put_sess_cmd(&cmd->se_cmd);
12548c2ecf20Sopenharmony_ci		return 0;
12558c2ecf20Sopenharmony_ci	}
12568c2ecf20Sopenharmony_ci
12578c2ecf20Sopenharmony_ci	/*
12588c2ecf20Sopenharmony_ci	 * Early CHECK_CONDITIONs with ImmediateData never make it to command
12598c2ecf20Sopenharmony_ci	 * execution.  These exceptions are processed in CmdSN order using
12608c2ecf20Sopenharmony_ci	 * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.
12618c2ecf20Sopenharmony_ci	 */
12628c2ecf20Sopenharmony_ci	if (cmd->sense_reason)
12638c2ecf20Sopenharmony_ci		return 1;
12648c2ecf20Sopenharmony_ci	/*
12658c2ecf20Sopenharmony_ci	 * Call directly into transport_generic_new_cmd() to perform
12668c2ecf20Sopenharmony_ci	 * the backend memory allocation.
12678c2ecf20Sopenharmony_ci	 */
12688c2ecf20Sopenharmony_ci	cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd);
12698c2ecf20Sopenharmony_ci	if (cmd->sense_reason)
12708c2ecf20Sopenharmony_ci		return 1;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	return 0;
12738c2ecf20Sopenharmony_ci}
12748c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_scsi_cmd);
12758c2ecf20Sopenharmony_ci
12768c2ecf20Sopenharmony_cistatic int
12778c2ecf20Sopenharmony_ciiscsit_get_immediate_data(struct iscsi_cmd *cmd, struct iscsi_scsi_req *hdr,
12788c2ecf20Sopenharmony_ci			  bool dump_payload)
12798c2ecf20Sopenharmony_ci{
12808c2ecf20Sopenharmony_ci	int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
12818c2ecf20Sopenharmony_ci	int rc;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci	/*
12848c2ecf20Sopenharmony_ci	 * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
12858c2ecf20Sopenharmony_ci	 */
12868c2ecf20Sopenharmony_ci	if (dump_payload) {
12878c2ecf20Sopenharmony_ci		u32 length = min(cmd->se_cmd.data_length - cmd->write_data_done,
12888c2ecf20Sopenharmony_ci				 cmd->first_burst_len);
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci		pr_debug("Dumping min(%d - %d, %d) = %d bytes of immediate data\n",
12918c2ecf20Sopenharmony_ci			 cmd->se_cmd.data_length, cmd->write_data_done,
12928c2ecf20Sopenharmony_ci			 cmd->first_burst_len, length);
12938c2ecf20Sopenharmony_ci		rc = iscsit_dump_data_payload(cmd->conn, length, 1);
12948c2ecf20Sopenharmony_ci		pr_debug("Finished dumping immediate data\n");
12958c2ecf20Sopenharmony_ci		if (rc < 0)
12968c2ecf20Sopenharmony_ci			immed_ret = IMMEDIATE_DATA_CANNOT_RECOVER;
12978c2ecf20Sopenharmony_ci	} else {
12988c2ecf20Sopenharmony_ci		immed_ret = iscsit_handle_immediate_data(cmd, hdr,
12998c2ecf20Sopenharmony_ci							 cmd->first_burst_len);
13008c2ecf20Sopenharmony_ci	}
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci	if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) {
13038c2ecf20Sopenharmony_ci		/*
13048c2ecf20Sopenharmony_ci		 * A PDU/CmdSN carrying Immediate Data passed
13058c2ecf20Sopenharmony_ci		 * DataCRC, check against ExpCmdSN/MaxCmdSN if
13068c2ecf20Sopenharmony_ci		 * Immediate Bit is not set.
13078c2ecf20Sopenharmony_ci		 */
13088c2ecf20Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
13098c2ecf20Sopenharmony_ci					(unsigned char *)hdr, hdr->cmdsn);
13108c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
13118c2ecf20Sopenharmony_ci			return -1;
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ci		if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
13148c2ecf20Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci			return 0;
13178c2ecf20Sopenharmony_ci		} else if (cmd->unsolicited_data)
13188c2ecf20Sopenharmony_ci			iscsit_set_unsolicited_dataout(cmd);
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci	} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
13218c2ecf20Sopenharmony_ci		/*
13228c2ecf20Sopenharmony_ci		 * Immediate Data failed DataCRC and ERL>=1,
13238c2ecf20Sopenharmony_ci		 * silently drop this PDU and let the initiator
13248c2ecf20Sopenharmony_ci		 * plug the CmdSN gap.
13258c2ecf20Sopenharmony_ci		 *
13268c2ecf20Sopenharmony_ci		 * FIXME: Send Unsolicited NOPIN with reserved
13278c2ecf20Sopenharmony_ci		 * TTT here to help the initiator figure out
13288c2ecf20Sopenharmony_ci		 * the missing CmdSN, although they should be
13298c2ecf20Sopenharmony_ci		 * intelligent enough to determine the missing
13308c2ecf20Sopenharmony_ci		 * CmdSN and issue a retry to plug the sequence.
13318c2ecf20Sopenharmony_ci		 */
13328c2ecf20Sopenharmony_ci		cmd->i_state = ISTATE_REMOVE;
13338c2ecf20Sopenharmony_ci		iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, cmd->i_state);
13348c2ecf20Sopenharmony_ci	} else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */
13358c2ecf20Sopenharmony_ci		return -1;
13368c2ecf20Sopenharmony_ci
13378c2ecf20Sopenharmony_ci	return 0;
13388c2ecf20Sopenharmony_ci}
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_cistatic int
13418c2ecf20Sopenharmony_ciiscsit_handle_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
13428c2ecf20Sopenharmony_ci			   unsigned char *buf)
13438c2ecf20Sopenharmony_ci{
13448c2ecf20Sopenharmony_ci	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf;
13458c2ecf20Sopenharmony_ci	int rc, immed_data;
13468c2ecf20Sopenharmony_ci	bool dump_payload = false;
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	rc = iscsit_setup_scsi_cmd(conn, cmd, buf);
13498c2ecf20Sopenharmony_ci	if (rc < 0)
13508c2ecf20Sopenharmony_ci		return 0;
13518c2ecf20Sopenharmony_ci	/*
13528c2ecf20Sopenharmony_ci	 * Allocation iovecs needed for struct socket operations for
13538c2ecf20Sopenharmony_ci	 * traditional iSCSI block I/O.
13548c2ecf20Sopenharmony_ci	 */
13558c2ecf20Sopenharmony_ci	if (iscsit_allocate_iovecs(cmd) < 0) {
13568c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd,
13578c2ecf20Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
13588c2ecf20Sopenharmony_ci	}
13598c2ecf20Sopenharmony_ci	immed_data = cmd->immediate_data;
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci	rc = iscsit_process_scsi_cmd(conn, cmd, hdr);
13628c2ecf20Sopenharmony_ci	if (rc < 0)
13638c2ecf20Sopenharmony_ci		return rc;
13648c2ecf20Sopenharmony_ci	else if (rc > 0)
13658c2ecf20Sopenharmony_ci		dump_payload = true;
13668c2ecf20Sopenharmony_ci
13678c2ecf20Sopenharmony_ci	if (!immed_data)
13688c2ecf20Sopenharmony_ci		return 0;
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	return iscsit_get_immediate_data(cmd, hdr, dump_payload);
13718c2ecf20Sopenharmony_ci}
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_cistatic u32 iscsit_do_crypto_hash_sg(
13748c2ecf20Sopenharmony_ci	struct ahash_request *hash,
13758c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
13768c2ecf20Sopenharmony_ci	u32 data_offset,
13778c2ecf20Sopenharmony_ci	u32 data_length,
13788c2ecf20Sopenharmony_ci	u32 padding,
13798c2ecf20Sopenharmony_ci	u8 *pad_bytes)
13808c2ecf20Sopenharmony_ci{
13818c2ecf20Sopenharmony_ci	u32 data_crc;
13828c2ecf20Sopenharmony_ci	struct scatterlist *sg;
13838c2ecf20Sopenharmony_ci	unsigned int page_off;
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	crypto_ahash_init(hash);
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci	sg = cmd->first_data_sg;
13888c2ecf20Sopenharmony_ci	page_off = cmd->first_data_sg_off;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci	if (data_length && page_off) {
13918c2ecf20Sopenharmony_ci		struct scatterlist first_sg;
13928c2ecf20Sopenharmony_ci		u32 len = min_t(u32, data_length, sg->length - page_off);
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci		sg_init_table(&first_sg, 1);
13958c2ecf20Sopenharmony_ci		sg_set_page(&first_sg, sg_page(sg), len, sg->offset + page_off);
13968c2ecf20Sopenharmony_ci
13978c2ecf20Sopenharmony_ci		ahash_request_set_crypt(hash, &first_sg, NULL, len);
13988c2ecf20Sopenharmony_ci		crypto_ahash_update(hash);
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci		data_length -= len;
14018c2ecf20Sopenharmony_ci		sg = sg_next(sg);
14028c2ecf20Sopenharmony_ci	}
14038c2ecf20Sopenharmony_ci
14048c2ecf20Sopenharmony_ci	while (data_length) {
14058c2ecf20Sopenharmony_ci		u32 cur_len = min_t(u32, data_length, sg->length);
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci		ahash_request_set_crypt(hash, sg, NULL, cur_len);
14088c2ecf20Sopenharmony_ci		crypto_ahash_update(hash);
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci		data_length -= cur_len;
14118c2ecf20Sopenharmony_ci		/* iscsit_map_iovec has already checked for invalid sg pointers */
14128c2ecf20Sopenharmony_ci		sg = sg_next(sg);
14138c2ecf20Sopenharmony_ci	}
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci	if (padding) {
14168c2ecf20Sopenharmony_ci		struct scatterlist pad_sg;
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci		sg_init_one(&pad_sg, pad_bytes, padding);
14198c2ecf20Sopenharmony_ci		ahash_request_set_crypt(hash, &pad_sg, (u8 *)&data_crc,
14208c2ecf20Sopenharmony_ci					padding);
14218c2ecf20Sopenharmony_ci		crypto_ahash_finup(hash);
14228c2ecf20Sopenharmony_ci	} else {
14238c2ecf20Sopenharmony_ci		ahash_request_set_crypt(hash, NULL, (u8 *)&data_crc, 0);
14248c2ecf20Sopenharmony_ci		crypto_ahash_final(hash);
14258c2ecf20Sopenharmony_ci	}
14268c2ecf20Sopenharmony_ci
14278c2ecf20Sopenharmony_ci	return data_crc;
14288c2ecf20Sopenharmony_ci}
14298c2ecf20Sopenharmony_ci
14308c2ecf20Sopenharmony_cistatic void iscsit_do_crypto_hash_buf(struct ahash_request *hash,
14318c2ecf20Sopenharmony_ci	const void *buf, u32 payload_length, u32 padding,
14328c2ecf20Sopenharmony_ci	const void *pad_bytes, void *data_crc)
14338c2ecf20Sopenharmony_ci{
14348c2ecf20Sopenharmony_ci	struct scatterlist sg[2];
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci	sg_init_table(sg, ARRAY_SIZE(sg));
14378c2ecf20Sopenharmony_ci	sg_set_buf(sg, buf, payload_length);
14388c2ecf20Sopenharmony_ci	if (padding)
14398c2ecf20Sopenharmony_ci		sg_set_buf(sg + 1, pad_bytes, padding);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	ahash_request_set_crypt(hash, sg, data_crc, payload_length + padding);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	crypto_ahash_digest(hash);
14448c2ecf20Sopenharmony_ci}
14458c2ecf20Sopenharmony_ci
14468c2ecf20Sopenharmony_ciint
14478c2ecf20Sopenharmony_ci__iscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf,
14488c2ecf20Sopenharmony_ci			   struct iscsi_cmd *cmd, u32 payload_length,
14498c2ecf20Sopenharmony_ci			   bool *success)
14508c2ecf20Sopenharmony_ci{
14518c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = buf;
14528c2ecf20Sopenharmony_ci	struct se_cmd *se_cmd;
14538c2ecf20Sopenharmony_ci	int rc;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	/* iSCSI write */
14568c2ecf20Sopenharmony_ci	atomic_long_add(payload_length, &conn->sess->rx_data_octets);
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x,"
14598c2ecf20Sopenharmony_ci		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
14608c2ecf20Sopenharmony_ci		hdr->itt, hdr->ttt, hdr->datasn, ntohl(hdr->offset),
14618c2ecf20Sopenharmony_ci		payload_length, conn->cid);
14628c2ecf20Sopenharmony_ci
14638c2ecf20Sopenharmony_ci	if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
14648c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x received DataOUT after"
14658c2ecf20Sopenharmony_ci			" last DataOUT received, dumping payload\n",
14668c2ecf20Sopenharmony_ci			cmd->init_task_tag);
14678c2ecf20Sopenharmony_ci		return iscsit_dump_data_payload(conn, payload_length, 1);
14688c2ecf20Sopenharmony_ci	}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci	if (cmd->data_direction != DMA_TO_DEVICE) {
14718c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x received DataOUT for a"
14728c2ecf20Sopenharmony_ci			" NON-WRITE command.\n", cmd->init_task_tag);
14738c2ecf20Sopenharmony_ci		return iscsit_dump_data_payload(conn, payload_length, 1);
14748c2ecf20Sopenharmony_ci	}
14758c2ecf20Sopenharmony_ci	se_cmd = &cmd->se_cmd;
14768c2ecf20Sopenharmony_ci	iscsit_mod_dataout_timer(cmd);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if ((be32_to_cpu(hdr->offset) + payload_length) > cmd->se_cmd.data_length) {
14798c2ecf20Sopenharmony_ci		pr_err("DataOut Offset: %u, Length %u greater than iSCSI Command EDTL %u, protocol error.\n",
14808c2ecf20Sopenharmony_ci		       be32_to_cpu(hdr->offset), payload_length,
14818c2ecf20Sopenharmony_ci		       cmd->se_cmd.data_length);
14828c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf);
14838c2ecf20Sopenharmony_ci	}
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci	if (cmd->unsolicited_data) {
14868c2ecf20Sopenharmony_ci		int dump_unsolicited_data = 0;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->InitialR2T) {
14898c2ecf20Sopenharmony_ci			pr_err("Received unexpected unsolicited data"
14908c2ecf20Sopenharmony_ci				" while InitialR2T=Yes, protocol error.\n");
14918c2ecf20Sopenharmony_ci			transport_send_check_condition_and_sense(&cmd->se_cmd,
14928c2ecf20Sopenharmony_ci					TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
14938c2ecf20Sopenharmony_ci			return -1;
14948c2ecf20Sopenharmony_ci		}
14958c2ecf20Sopenharmony_ci		/*
14968c2ecf20Sopenharmony_ci		 * Special case for dealing with Unsolicited DataOUT
14978c2ecf20Sopenharmony_ci		 * and Unsupported SAM WRITE Opcodes and SE resource allocation
14988c2ecf20Sopenharmony_ci		 * failures;
14998c2ecf20Sopenharmony_ci		 */
15008c2ecf20Sopenharmony_ci
15018c2ecf20Sopenharmony_ci		/* Something's amiss if we're not in WRITE_PENDING state... */
15028c2ecf20Sopenharmony_ci		WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING);
15038c2ecf20Sopenharmony_ci		if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE))
15048c2ecf20Sopenharmony_ci			dump_unsolicited_data = 1;
15058c2ecf20Sopenharmony_ci
15068c2ecf20Sopenharmony_ci		if (dump_unsolicited_data) {
15078c2ecf20Sopenharmony_ci			/*
15088c2ecf20Sopenharmony_ci			 * Check if a delayed TASK_ABORTED status needs to
15098c2ecf20Sopenharmony_ci			 * be sent now if the ISCSI_FLAG_CMD_FINAL has been
15108c2ecf20Sopenharmony_ci			 * received with the unsolicited data out.
15118c2ecf20Sopenharmony_ci			 */
15128c2ecf20Sopenharmony_ci			if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
15138c2ecf20Sopenharmony_ci				iscsit_stop_dataout_timer(cmd);
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci			return iscsit_dump_data_payload(conn, payload_length, 1);
15168c2ecf20Sopenharmony_ci		}
15178c2ecf20Sopenharmony_ci	} else {
15188c2ecf20Sopenharmony_ci		/*
15198c2ecf20Sopenharmony_ci		 * For the normal solicited data path:
15208c2ecf20Sopenharmony_ci		 *
15218c2ecf20Sopenharmony_ci		 * Check for a delayed TASK_ABORTED status and dump any
15228c2ecf20Sopenharmony_ci		 * incoming data out payload if one exists.  Also, when the
15238c2ecf20Sopenharmony_ci		 * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current
15248c2ecf20Sopenharmony_ci		 * data out sequence, we decrement outstanding_r2ts.  Once
15258c2ecf20Sopenharmony_ci		 * outstanding_r2ts reaches zero, go ahead and send the delayed
15268c2ecf20Sopenharmony_ci		 * TASK_ABORTED status.
15278c2ecf20Sopenharmony_ci		 */
15288c2ecf20Sopenharmony_ci		if (se_cmd->transport_state & CMD_T_ABORTED) {
15298c2ecf20Sopenharmony_ci			if (hdr->flags & ISCSI_FLAG_CMD_FINAL &&
15308c2ecf20Sopenharmony_ci			    --cmd->outstanding_r2ts < 1)
15318c2ecf20Sopenharmony_ci				iscsit_stop_dataout_timer(cmd);
15328c2ecf20Sopenharmony_ci
15338c2ecf20Sopenharmony_ci			return iscsit_dump_data_payload(conn, payload_length, 1);
15348c2ecf20Sopenharmony_ci		}
15358c2ecf20Sopenharmony_ci	}
15368c2ecf20Sopenharmony_ci	/*
15378c2ecf20Sopenharmony_ci	 * Perform DataSN, DataSequenceInOrder, DataPDUInOrder, and
15388c2ecf20Sopenharmony_ci	 * within-command recovery checks before receiving the payload.
15398c2ecf20Sopenharmony_ci	 */
15408c2ecf20Sopenharmony_ci	rc = iscsit_check_pre_dataout(cmd, buf);
15418c2ecf20Sopenharmony_ci	if (rc == DATAOUT_WITHIN_COMMAND_RECOVERY)
15428c2ecf20Sopenharmony_ci		return 0;
15438c2ecf20Sopenharmony_ci	else if (rc == DATAOUT_CANNOT_RECOVER)
15448c2ecf20Sopenharmony_ci		return -1;
15458c2ecf20Sopenharmony_ci	*success = true;
15468c2ecf20Sopenharmony_ci	return 0;
15478c2ecf20Sopenharmony_ci}
15488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__iscsit_check_dataout_hdr);
15498c2ecf20Sopenharmony_ci
15508c2ecf20Sopenharmony_ciint
15518c2ecf20Sopenharmony_ciiscsit_check_dataout_hdr(struct iscsi_conn *conn, void *buf,
15528c2ecf20Sopenharmony_ci			 struct iscsi_cmd **out_cmd)
15538c2ecf20Sopenharmony_ci{
15548c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = buf;
15558c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
15568c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
15578c2ecf20Sopenharmony_ci	int rc;
15588c2ecf20Sopenharmony_ci	bool success = false;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci	if (!payload_length) {
15618c2ecf20Sopenharmony_ci		pr_warn_ratelimited("DataOUT payload is ZERO, ignoring.\n");
15628c2ecf20Sopenharmony_ci		return 0;
15638c2ecf20Sopenharmony_ci	}
15648c2ecf20Sopenharmony_ci
15658c2ecf20Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
15668c2ecf20Sopenharmony_ci		pr_err_ratelimited("DataSegmentLength: %u is greater than"
15678c2ecf20Sopenharmony_ci			" MaxXmitDataSegmentLength: %u\n", payload_length,
15688c2ecf20Sopenharmony_ci			conn->conn_ops->MaxXmitDataSegmentLength);
15698c2ecf20Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, buf);
15708c2ecf20Sopenharmony_ci	}
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci	cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, payload_length);
15738c2ecf20Sopenharmony_ci	if (!cmd)
15748c2ecf20Sopenharmony_ci		return 0;
15758c2ecf20Sopenharmony_ci
15768c2ecf20Sopenharmony_ci	rc = __iscsit_check_dataout_hdr(conn, buf, cmd, payload_length, &success);
15778c2ecf20Sopenharmony_ci
15788c2ecf20Sopenharmony_ci	if (success)
15798c2ecf20Sopenharmony_ci		*out_cmd = cmd;
15808c2ecf20Sopenharmony_ci
15818c2ecf20Sopenharmony_ci	return rc;
15828c2ecf20Sopenharmony_ci}
15838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_check_dataout_hdr);
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_cistatic int
15868c2ecf20Sopenharmony_ciiscsit_get_dataout(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
15878c2ecf20Sopenharmony_ci		   struct iscsi_data *hdr)
15888c2ecf20Sopenharmony_ci{
15898c2ecf20Sopenharmony_ci	struct kvec *iov;
15908c2ecf20Sopenharmony_ci	u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0;
15918c2ecf20Sopenharmony_ci	u32 payload_length;
15928c2ecf20Sopenharmony_ci	int iov_ret, data_crc_failed = 0;
15938c2ecf20Sopenharmony_ci
15948c2ecf20Sopenharmony_ci	payload_length = min_t(u32, cmd->se_cmd.data_length,
15958c2ecf20Sopenharmony_ci			       ntoh24(hdr->dlength));
15968c2ecf20Sopenharmony_ci	rx_size += payload_length;
15978c2ecf20Sopenharmony_ci	iov = &cmd->iov_data[0];
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, iov, cmd->orig_iov_data_count - 2,
16008c2ecf20Sopenharmony_ci				   be32_to_cpu(hdr->offset), payload_length);
16018c2ecf20Sopenharmony_ci	if (iov_ret < 0)
16028c2ecf20Sopenharmony_ci		return -1;
16038c2ecf20Sopenharmony_ci
16048c2ecf20Sopenharmony_ci	iov_count += iov_ret;
16058c2ecf20Sopenharmony_ci
16068c2ecf20Sopenharmony_ci	padding = ((-payload_length) & 3);
16078c2ecf20Sopenharmony_ci	if (padding != 0) {
16088c2ecf20Sopenharmony_ci		iov[iov_count].iov_base	= cmd->pad_bytes;
16098c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len = padding;
16108c2ecf20Sopenharmony_ci		rx_size += padding;
16118c2ecf20Sopenharmony_ci		pr_debug("Receiving %u padding bytes.\n", padding);
16128c2ecf20Sopenharmony_ci	}
16138c2ecf20Sopenharmony_ci
16148c2ecf20Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
16158c2ecf20Sopenharmony_ci		iov[iov_count].iov_base = &checksum;
16168c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
16178c2ecf20Sopenharmony_ci		rx_size += ISCSI_CRC_LEN;
16188c2ecf20Sopenharmony_ci	}
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count);
16218c2ecf20Sopenharmony_ci	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
16228c2ecf20Sopenharmony_ci
16238c2ecf20Sopenharmony_ci	iscsit_unmap_iovec(cmd);
16248c2ecf20Sopenharmony_ci
16258c2ecf20Sopenharmony_ci	if (rx_got != rx_size)
16268c2ecf20Sopenharmony_ci		return -1;
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
16298c2ecf20Sopenharmony_ci		u32 data_crc;
16308c2ecf20Sopenharmony_ci
16318c2ecf20Sopenharmony_ci		data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd,
16328c2ecf20Sopenharmony_ci						    be32_to_cpu(hdr->offset),
16338c2ecf20Sopenharmony_ci						    payload_length, padding,
16348c2ecf20Sopenharmony_ci						    cmd->pad_bytes);
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci		if (checksum != data_crc) {
16378c2ecf20Sopenharmony_ci			pr_err("ITT: 0x%08x, Offset: %u, Length: %u,"
16388c2ecf20Sopenharmony_ci				" DataSN: 0x%08x, CRC32C DataDigest 0x%08x"
16398c2ecf20Sopenharmony_ci				" does not match computed 0x%08x\n",
16408c2ecf20Sopenharmony_ci				hdr->itt, hdr->offset, payload_length,
16418c2ecf20Sopenharmony_ci				hdr->datasn, checksum, data_crc);
16428c2ecf20Sopenharmony_ci			data_crc_failed = 1;
16438c2ecf20Sopenharmony_ci		} else {
16448c2ecf20Sopenharmony_ci			pr_debug("Got CRC32C DataDigest 0x%08x for"
16458c2ecf20Sopenharmony_ci				" %u bytes of Data Out\n", checksum,
16468c2ecf20Sopenharmony_ci				payload_length);
16478c2ecf20Sopenharmony_ci		}
16488c2ecf20Sopenharmony_ci	}
16498c2ecf20Sopenharmony_ci
16508c2ecf20Sopenharmony_ci	return data_crc_failed;
16518c2ecf20Sopenharmony_ci}
16528c2ecf20Sopenharmony_ci
16538c2ecf20Sopenharmony_ciint
16548c2ecf20Sopenharmony_ciiscsit_check_dataout_payload(struct iscsi_cmd *cmd, struct iscsi_data *hdr,
16558c2ecf20Sopenharmony_ci			     bool data_crc_failed)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
16588c2ecf20Sopenharmony_ci	int rc, ooo_cmdsn;
16598c2ecf20Sopenharmony_ci	/*
16608c2ecf20Sopenharmony_ci	 * Increment post receive data and CRC values or perform
16618c2ecf20Sopenharmony_ci	 * within-command recovery.
16628c2ecf20Sopenharmony_ci	 */
16638c2ecf20Sopenharmony_ci	rc = iscsit_check_post_dataout(cmd, (unsigned char *)hdr, data_crc_failed);
16648c2ecf20Sopenharmony_ci	if ((rc == DATAOUT_NORMAL) || (rc == DATAOUT_WITHIN_COMMAND_RECOVERY))
16658c2ecf20Sopenharmony_ci		return 0;
16668c2ecf20Sopenharmony_ci	else if (rc == DATAOUT_SEND_R2T) {
16678c2ecf20Sopenharmony_ci		iscsit_set_dataout_sequence_values(cmd);
16688c2ecf20Sopenharmony_ci		conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
16698c2ecf20Sopenharmony_ci	} else if (rc == DATAOUT_SEND_TO_TRANSPORT) {
16708c2ecf20Sopenharmony_ci		/*
16718c2ecf20Sopenharmony_ci		 * Handle extra special case for out of order
16728c2ecf20Sopenharmony_ci		 * Unsolicited Data Out.
16738c2ecf20Sopenharmony_ci		 */
16748c2ecf20Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
16758c2ecf20Sopenharmony_ci		ooo_cmdsn = (cmd->cmd_flags & ICF_OOO_CMDSN);
16768c2ecf20Sopenharmony_ci		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
16778c2ecf20Sopenharmony_ci		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
16788c2ecf20Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci		iscsit_stop_dataout_timer(cmd);
16818c2ecf20Sopenharmony_ci		if (ooo_cmdsn)
16828c2ecf20Sopenharmony_ci			return 0;
16838c2ecf20Sopenharmony_ci		target_execute_cmd(&cmd->se_cmd);
16848c2ecf20Sopenharmony_ci		return 0;
16858c2ecf20Sopenharmony_ci	} else /* DATAOUT_CANNOT_RECOVER */
16868c2ecf20Sopenharmony_ci		return -1;
16878c2ecf20Sopenharmony_ci
16888c2ecf20Sopenharmony_ci	return 0;
16898c2ecf20Sopenharmony_ci}
16908c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_check_dataout_payload);
16918c2ecf20Sopenharmony_ci
16928c2ecf20Sopenharmony_cistatic int iscsit_handle_data_out(struct iscsi_conn *conn, unsigned char *buf)
16938c2ecf20Sopenharmony_ci{
16948c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd = NULL;
16958c2ecf20Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *)buf;
16968c2ecf20Sopenharmony_ci	int rc;
16978c2ecf20Sopenharmony_ci	bool data_crc_failed = false;
16988c2ecf20Sopenharmony_ci
16998c2ecf20Sopenharmony_ci	rc = iscsit_check_dataout_hdr(conn, buf, &cmd);
17008c2ecf20Sopenharmony_ci	if (rc < 0)
17018c2ecf20Sopenharmony_ci		return 0;
17028c2ecf20Sopenharmony_ci	else if (!cmd)
17038c2ecf20Sopenharmony_ci		return 0;
17048c2ecf20Sopenharmony_ci
17058c2ecf20Sopenharmony_ci	rc = iscsit_get_dataout(conn, cmd, hdr);
17068c2ecf20Sopenharmony_ci	if (rc < 0)
17078c2ecf20Sopenharmony_ci		return rc;
17088c2ecf20Sopenharmony_ci	else if (rc > 0)
17098c2ecf20Sopenharmony_ci		data_crc_failed = true;
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed);
17128c2ecf20Sopenharmony_ci}
17138c2ecf20Sopenharmony_ci
17148c2ecf20Sopenharmony_ciint iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
17158c2ecf20Sopenharmony_ci			 struct iscsi_nopout *hdr)
17168c2ecf20Sopenharmony_ci{
17178c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
17188c2ecf20Sopenharmony_ci
17198c2ecf20Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
17208c2ecf20Sopenharmony_ci		pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n");
17218c2ecf20Sopenharmony_ci		if (!cmd)
17228c2ecf20Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
17238c2ecf20Sopenharmony_ci						 (unsigned char *)hdr);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
17268c2ecf20Sopenharmony_ci					 (unsigned char *)hdr);
17278c2ecf20Sopenharmony_ci	}
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
17308c2ecf20Sopenharmony_ci		pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
17318c2ecf20Sopenharmony_ci			" not set, protocol error.\n");
17328c2ecf20Sopenharmony_ci		if (!cmd)
17338c2ecf20Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
17348c2ecf20Sopenharmony_ci						 (unsigned char *)hdr);
17358c2ecf20Sopenharmony_ci
17368c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
17378c2ecf20Sopenharmony_ci					 (unsigned char *)hdr);
17388c2ecf20Sopenharmony_ci	}
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
17418c2ecf20Sopenharmony_ci		pr_err("NOPOUT Ping Data DataSegmentLength: %u is"
17428c2ecf20Sopenharmony_ci			" greater than MaxXmitDataSegmentLength: %u, protocol"
17438c2ecf20Sopenharmony_ci			" error.\n", payload_length,
17448c2ecf20Sopenharmony_ci			conn->conn_ops->MaxXmitDataSegmentLength);
17458c2ecf20Sopenharmony_ci		if (!cmd)
17468c2ecf20Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
17478c2ecf20Sopenharmony_ci						 (unsigned char *)hdr);
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
17508c2ecf20Sopenharmony_ci					 (unsigned char *)hdr);
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x,"
17548c2ecf20Sopenharmony_ci		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
17558c2ecf20Sopenharmony_ci		hdr->itt == RESERVED_ITT ? "Response" : "Request",
17568c2ecf20Sopenharmony_ci		hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn,
17578c2ecf20Sopenharmony_ci		payload_length);
17588c2ecf20Sopenharmony_ci	/*
17598c2ecf20Sopenharmony_ci	 * This is not a response to a Unsolicited NopIN, which means
17608c2ecf20Sopenharmony_ci	 * it can either be a NOPOUT ping request (with a valid ITT),
17618c2ecf20Sopenharmony_ci	 * or a NOPOUT not requesting a NOPIN (with a reserved ITT).
17628c2ecf20Sopenharmony_ci	 * Either way, make sure we allocate an struct iscsi_cmd, as both
17638c2ecf20Sopenharmony_ci	 * can contain ping data.
17648c2ecf20Sopenharmony_ci	 */
17658c2ecf20Sopenharmony_ci	if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
17668c2ecf20Sopenharmony_ci		cmd->iscsi_opcode	= ISCSI_OP_NOOP_OUT;
17678c2ecf20Sopenharmony_ci		cmd->i_state		= ISTATE_SEND_NOPIN;
17688c2ecf20Sopenharmony_ci		cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
17698c2ecf20Sopenharmony_ci						1 : 0);
17708c2ecf20Sopenharmony_ci		conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
17718c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag	= 0xFFFFFFFF;
17728c2ecf20Sopenharmony_ci		cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
17738c2ecf20Sopenharmony_ci		cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
17748c2ecf20Sopenharmony_ci		cmd->data_direction	= DMA_NONE;
17758c2ecf20Sopenharmony_ci	}
17768c2ecf20Sopenharmony_ci
17778c2ecf20Sopenharmony_ci	return 0;
17788c2ecf20Sopenharmony_ci}
17798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_nop_out);
17808c2ecf20Sopenharmony_ci
17818c2ecf20Sopenharmony_ciint iscsit_process_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
17828c2ecf20Sopenharmony_ci			   struct iscsi_nopout *hdr)
17838c2ecf20Sopenharmony_ci{
17848c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd_p = NULL;
17858c2ecf20Sopenharmony_ci	int cmdsn_ret = 0;
17868c2ecf20Sopenharmony_ci	/*
17878c2ecf20Sopenharmony_ci	 * Initiator is expecting a NopIN ping reply..
17888c2ecf20Sopenharmony_ci	 */
17898c2ecf20Sopenharmony_ci	if (hdr->itt != RESERVED_ITT) {
17908c2ecf20Sopenharmony_ci		if (!cmd)
17918c2ecf20Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
17928c2ecf20Sopenharmony_ci						(unsigned char *)hdr);
17938c2ecf20Sopenharmony_ci
17948c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
17958c2ecf20Sopenharmony_ci		list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
17968c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
17978c2ecf20Sopenharmony_ci
17988c2ecf20Sopenharmony_ci		iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
17998c2ecf20Sopenharmony_ci
18008c2ecf20Sopenharmony_ci		if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
18018c2ecf20Sopenharmony_ci			iscsit_add_cmd_to_response_queue(cmd, conn,
18028c2ecf20Sopenharmony_ci							 cmd->i_state);
18038c2ecf20Sopenharmony_ci			return 0;
18048c2ecf20Sopenharmony_ci		}
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
18078c2ecf20Sopenharmony_ci				(unsigned char *)hdr, hdr->cmdsn);
18088c2ecf20Sopenharmony_ci                if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
18098c2ecf20Sopenharmony_ci			return 0;
18108c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
18118c2ecf20Sopenharmony_ci			return -1;
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci		return 0;
18148c2ecf20Sopenharmony_ci	}
18158c2ecf20Sopenharmony_ci	/*
18168c2ecf20Sopenharmony_ci	 * This was a response to a unsolicited NOPIN ping.
18178c2ecf20Sopenharmony_ci	 */
18188c2ecf20Sopenharmony_ci	if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
18198c2ecf20Sopenharmony_ci		cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt));
18208c2ecf20Sopenharmony_ci		if (!cmd_p)
18218c2ecf20Sopenharmony_ci			return -EINVAL;
18228c2ecf20Sopenharmony_ci
18238c2ecf20Sopenharmony_ci		iscsit_stop_nopin_response_timer(conn);
18248c2ecf20Sopenharmony_ci
18258c2ecf20Sopenharmony_ci		cmd_p->i_state = ISTATE_REMOVE;
18268c2ecf20Sopenharmony_ci		iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);
18278c2ecf20Sopenharmony_ci
18288c2ecf20Sopenharmony_ci		iscsit_start_nopin_timer(conn);
18298c2ecf20Sopenharmony_ci		return 0;
18308c2ecf20Sopenharmony_ci	}
18318c2ecf20Sopenharmony_ci	/*
18328c2ecf20Sopenharmony_ci	 * Otherwise, initiator is not expecting a NOPIN is response.
18338c2ecf20Sopenharmony_ci	 * Just ignore for now.
18348c2ecf20Sopenharmony_ci	 */
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci	if (cmd)
18378c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
18388c2ecf20Sopenharmony_ci
18398c2ecf20Sopenharmony_ci        return 0;
18408c2ecf20Sopenharmony_ci}
18418c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_nop_out);
18428c2ecf20Sopenharmony_ci
18438c2ecf20Sopenharmony_cistatic int iscsit_handle_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
18448c2ecf20Sopenharmony_ci				 unsigned char *buf)
18458c2ecf20Sopenharmony_ci{
18468c2ecf20Sopenharmony_ci	unsigned char *ping_data = NULL;
18478c2ecf20Sopenharmony_ci	struct iscsi_nopout *hdr = (struct iscsi_nopout *)buf;
18488c2ecf20Sopenharmony_ci	struct kvec *iov = NULL;
18498c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
18508c2ecf20Sopenharmony_ci	int ret;
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci	ret = iscsit_setup_nop_out(conn, cmd, hdr);
18538c2ecf20Sopenharmony_ci	if (ret < 0)
18548c2ecf20Sopenharmony_ci		return 0;
18558c2ecf20Sopenharmony_ci	/*
18568c2ecf20Sopenharmony_ci	 * Handle NOP-OUT payload for traditional iSCSI sockets
18578c2ecf20Sopenharmony_ci	 */
18588c2ecf20Sopenharmony_ci	if (payload_length && hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
18598c2ecf20Sopenharmony_ci		u32 checksum, data_crc, padding = 0;
18608c2ecf20Sopenharmony_ci		int niov = 0, rx_got, rx_size = payload_length;
18618c2ecf20Sopenharmony_ci
18628c2ecf20Sopenharmony_ci		ping_data = kzalloc(payload_length + 1, GFP_KERNEL);
18638c2ecf20Sopenharmony_ci		if (!ping_data) {
18648c2ecf20Sopenharmony_ci			ret = -1;
18658c2ecf20Sopenharmony_ci			goto out;
18668c2ecf20Sopenharmony_ci		}
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci		iov = &cmd->iov_misc[0];
18698c2ecf20Sopenharmony_ci		iov[niov].iov_base	= ping_data;
18708c2ecf20Sopenharmony_ci		iov[niov++].iov_len	= payload_length;
18718c2ecf20Sopenharmony_ci
18728c2ecf20Sopenharmony_ci		padding = ((-payload_length) & 3);
18738c2ecf20Sopenharmony_ci		if (padding != 0) {
18748c2ecf20Sopenharmony_ci			pr_debug("Receiving %u additional bytes"
18758c2ecf20Sopenharmony_ci				" for padding.\n", padding);
18768c2ecf20Sopenharmony_ci			iov[niov].iov_base	= &cmd->pad_bytes;
18778c2ecf20Sopenharmony_ci			iov[niov++].iov_len	= padding;
18788c2ecf20Sopenharmony_ci			rx_size += padding;
18798c2ecf20Sopenharmony_ci		}
18808c2ecf20Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
18818c2ecf20Sopenharmony_ci			iov[niov].iov_base	= &checksum;
18828c2ecf20Sopenharmony_ci			iov[niov++].iov_len	= ISCSI_CRC_LEN;
18838c2ecf20Sopenharmony_ci			rx_size += ISCSI_CRC_LEN;
18848c2ecf20Sopenharmony_ci		}
18858c2ecf20Sopenharmony_ci
18868c2ecf20Sopenharmony_ci		WARN_ON_ONCE(niov > ARRAY_SIZE(cmd->iov_misc));
18878c2ecf20Sopenharmony_ci		rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size);
18888c2ecf20Sopenharmony_ci		if (rx_got != rx_size) {
18898c2ecf20Sopenharmony_ci			ret = -1;
18908c2ecf20Sopenharmony_ci			goto out;
18918c2ecf20Sopenharmony_ci		}
18928c2ecf20Sopenharmony_ci
18938c2ecf20Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
18948c2ecf20Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash, ping_data,
18958c2ecf20Sopenharmony_ci						  payload_length, padding,
18968c2ecf20Sopenharmony_ci						  cmd->pad_bytes, &data_crc);
18978c2ecf20Sopenharmony_ci
18988c2ecf20Sopenharmony_ci			if (checksum != data_crc) {
18998c2ecf20Sopenharmony_ci				pr_err("Ping data CRC32C DataDigest"
19008c2ecf20Sopenharmony_ci				" 0x%08x does not match computed 0x%08x\n",
19018c2ecf20Sopenharmony_ci					checksum, data_crc);
19028c2ecf20Sopenharmony_ci				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
19038c2ecf20Sopenharmony_ci					pr_err("Unable to recover from"
19048c2ecf20Sopenharmony_ci					" NOPOUT Ping DataCRC failure while in"
19058c2ecf20Sopenharmony_ci						" ERL=0.\n");
19068c2ecf20Sopenharmony_ci					ret = -1;
19078c2ecf20Sopenharmony_ci					goto out;
19088c2ecf20Sopenharmony_ci				} else {
19098c2ecf20Sopenharmony_ci					/*
19108c2ecf20Sopenharmony_ci					 * Silently drop this PDU and let the
19118c2ecf20Sopenharmony_ci					 * initiator plug the CmdSN gap.
19128c2ecf20Sopenharmony_ci					 */
19138c2ecf20Sopenharmony_ci					pr_debug("Dropping NOPOUT"
19148c2ecf20Sopenharmony_ci					" Command CmdSN: 0x%08x due to"
19158c2ecf20Sopenharmony_ci					" DataCRC error.\n", hdr->cmdsn);
19168c2ecf20Sopenharmony_ci					ret = 0;
19178c2ecf20Sopenharmony_ci					goto out;
19188c2ecf20Sopenharmony_ci				}
19198c2ecf20Sopenharmony_ci			} else {
19208c2ecf20Sopenharmony_ci				pr_debug("Got CRC32C DataDigest"
19218c2ecf20Sopenharmony_ci				" 0x%08x for %u bytes of ping data.\n",
19228c2ecf20Sopenharmony_ci					checksum, payload_length);
19238c2ecf20Sopenharmony_ci			}
19248c2ecf20Sopenharmony_ci		}
19258c2ecf20Sopenharmony_ci
19268c2ecf20Sopenharmony_ci		ping_data[payload_length] = '\0';
19278c2ecf20Sopenharmony_ci		/*
19288c2ecf20Sopenharmony_ci		 * Attach ping data to struct iscsi_cmd->buf_ptr.
19298c2ecf20Sopenharmony_ci		 */
19308c2ecf20Sopenharmony_ci		cmd->buf_ptr = ping_data;
19318c2ecf20Sopenharmony_ci		cmd->buf_ptr_size = payload_length;
19328c2ecf20Sopenharmony_ci
19338c2ecf20Sopenharmony_ci		pr_debug("Got %u bytes of NOPOUT ping"
19348c2ecf20Sopenharmony_ci			" data.\n", payload_length);
19358c2ecf20Sopenharmony_ci		pr_debug("Ping Data: \"%s\"\n", ping_data);
19368c2ecf20Sopenharmony_ci	}
19378c2ecf20Sopenharmony_ci
19388c2ecf20Sopenharmony_ci	return iscsit_process_nop_out(conn, cmd, hdr);
19398c2ecf20Sopenharmony_ciout:
19408c2ecf20Sopenharmony_ci	if (cmd)
19418c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
19428c2ecf20Sopenharmony_ci
19438c2ecf20Sopenharmony_ci	kfree(ping_data);
19448c2ecf20Sopenharmony_ci	return ret;
19458c2ecf20Sopenharmony_ci}
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_cistatic enum tcm_tmreq_table iscsit_convert_tmf(u8 iscsi_tmf)
19488c2ecf20Sopenharmony_ci{
19498c2ecf20Sopenharmony_ci	switch (iscsi_tmf) {
19508c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK:
19518c2ecf20Sopenharmony_ci		return TMR_ABORT_TASK;
19528c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK_SET:
19538c2ecf20Sopenharmony_ci		return TMR_ABORT_TASK_SET;
19548c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_ACA:
19558c2ecf20Sopenharmony_ci		return TMR_CLEAR_ACA;
19568c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
19578c2ecf20Sopenharmony_ci		return TMR_CLEAR_TASK_SET;
19588c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
19598c2ecf20Sopenharmony_ci		return TMR_LUN_RESET;
19608c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
19618c2ecf20Sopenharmony_ci		return TMR_TARGET_WARM_RESET;
19628c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
19638c2ecf20Sopenharmony_ci		return TMR_TARGET_COLD_RESET;
19648c2ecf20Sopenharmony_ci	default:
19658c2ecf20Sopenharmony_ci		return TMR_UNKNOWN;
19668c2ecf20Sopenharmony_ci	}
19678c2ecf20Sopenharmony_ci}
19688c2ecf20Sopenharmony_ci
19698c2ecf20Sopenharmony_ciint
19708c2ecf20Sopenharmony_ciiscsit_handle_task_mgt_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
19718c2ecf20Sopenharmony_ci			   unsigned char *buf)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci	struct se_tmr_req *se_tmr;
19748c2ecf20Sopenharmony_ci	struct iscsi_tmr_req *tmr_req;
19758c2ecf20Sopenharmony_ci	struct iscsi_tm *hdr;
19768c2ecf20Sopenharmony_ci	int out_of_order_cmdsn = 0, ret;
19778c2ecf20Sopenharmony_ci	u8 function, tcm_function = TMR_UNKNOWN;
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_tm *) buf;
19808c2ecf20Sopenharmony_ci	hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
19818c2ecf20Sopenharmony_ci	function = hdr->flags;
19828c2ecf20Sopenharmony_ci
19838c2ecf20Sopenharmony_ci	pr_debug("Got Task Management Request ITT: 0x%08x, CmdSN:"
19848c2ecf20Sopenharmony_ci		" 0x%08x, Function: 0x%02x, RefTaskTag: 0x%08x, RefCmdSN:"
19858c2ecf20Sopenharmony_ci		" 0x%08x, CID: %hu\n", hdr->itt, hdr->cmdsn, function,
19868c2ecf20Sopenharmony_ci		hdr->rtt, hdr->refcmdsn, conn->cid);
19878c2ecf20Sopenharmony_ci
19888c2ecf20Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
19898c2ecf20Sopenharmony_ci	    ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
19908c2ecf20Sopenharmony_ci	     hdr->rtt != RESERVED_ITT)) {
19918c2ecf20Sopenharmony_ci		pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n");
19928c2ecf20Sopenharmony_ci		hdr->rtt = RESERVED_ITT;
19938c2ecf20Sopenharmony_ci	}
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci	if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) &&
19968c2ecf20Sopenharmony_ci			!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
19978c2ecf20Sopenharmony_ci		pr_err("Task Management Request TASK_REASSIGN not"
19988c2ecf20Sopenharmony_ci			" issued as immediate command, bad iSCSI Initiator"
19998c2ecf20Sopenharmony_ci				"implementation\n");
20008c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
20018c2ecf20Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
20028c2ecf20Sopenharmony_ci	}
20038c2ecf20Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
20048c2ecf20Sopenharmony_ci	    be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)
20058c2ecf20Sopenharmony_ci		hdr->refcmdsn = cpu_to_be32(ISCSI_RESERVED_TAG);
20068c2ecf20Sopenharmony_ci
20078c2ecf20Sopenharmony_ci	cmd->data_direction = DMA_NONE;
20088c2ecf20Sopenharmony_ci	cmd->tmr_req = kzalloc(sizeof(*cmd->tmr_req), GFP_KERNEL);
20098c2ecf20Sopenharmony_ci	if (!cmd->tmr_req) {
20108c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
20118c2ecf20Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_NO_RESOURCES,
20128c2ecf20Sopenharmony_ci					     buf);
20138c2ecf20Sopenharmony_ci	}
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci	transport_init_se_cmd(&cmd->se_cmd, &iscsi_ops,
20168c2ecf20Sopenharmony_ci			      conn->sess->se_sess, 0, DMA_NONE,
20178c2ecf20Sopenharmony_ci			      TCM_SIMPLE_TAG, cmd->sense_buffer + 2,
20188c2ecf20Sopenharmony_ci			      scsilun_to_int(&hdr->lun));
20198c2ecf20Sopenharmony_ci
20208c2ecf20Sopenharmony_ci	target_get_sess_cmd(&cmd->se_cmd, true);
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci	/*
20238c2ecf20Sopenharmony_ci	 * TASK_REASSIGN for ERL=2 / connection stays inside of
20248c2ecf20Sopenharmony_ci	 * LIO-Target $FABRIC_MOD
20258c2ecf20Sopenharmony_ci	 */
20268c2ecf20Sopenharmony_ci	if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
20278c2ecf20Sopenharmony_ci		tcm_function = iscsit_convert_tmf(function);
20288c2ecf20Sopenharmony_ci		if (tcm_function == TMR_UNKNOWN) {
20298c2ecf20Sopenharmony_ci			pr_err("Unknown iSCSI TMR Function:"
20308c2ecf20Sopenharmony_ci			       " 0x%02x\n", function);
20318c2ecf20Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
20328c2ecf20Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
20338c2ecf20Sopenharmony_ci		}
20348c2ecf20Sopenharmony_ci	}
20358c2ecf20Sopenharmony_ci	ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, tcm_function,
20368c2ecf20Sopenharmony_ci				 GFP_KERNEL);
20378c2ecf20Sopenharmony_ci	if (ret < 0)
20388c2ecf20Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
20398c2ecf20Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci	cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req;
20428c2ecf20Sopenharmony_ci
20438c2ecf20Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_SCSI_TMFUNC;
20448c2ecf20Sopenharmony_ci	cmd->i_state		= ISTATE_SEND_TASKMGTRSP;
20458c2ecf20Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
20468c2ecf20Sopenharmony_ci	cmd->init_task_tag	= hdr->itt;
20478c2ecf20Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
20488c2ecf20Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
20498c2ecf20Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
20508c2ecf20Sopenharmony_ci	se_tmr			= cmd->se_cmd.se_tmr_req;
20518c2ecf20Sopenharmony_ci	tmr_req			= cmd->tmr_req;
20528c2ecf20Sopenharmony_ci	/*
20538c2ecf20Sopenharmony_ci	 * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
20548c2ecf20Sopenharmony_ci	 */
20558c2ecf20Sopenharmony_ci	if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
20568c2ecf20Sopenharmony_ci		ret = transport_lookup_tmr_lun(&cmd->se_cmd);
20578c2ecf20Sopenharmony_ci		if (ret < 0) {
20588c2ecf20Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_NO_LUN;
20598c2ecf20Sopenharmony_ci			goto attach;
20608c2ecf20Sopenharmony_ci		}
20618c2ecf20Sopenharmony_ci	}
20628c2ecf20Sopenharmony_ci
20638c2ecf20Sopenharmony_ci	switch (function) {
20648c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK:
20658c2ecf20Sopenharmony_ci		se_tmr->response = iscsit_tmr_abort_task(cmd, buf);
20668c2ecf20Sopenharmony_ci		if (se_tmr->response)
20678c2ecf20Sopenharmony_ci			goto attach;
20688c2ecf20Sopenharmony_ci		break;
20698c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK_SET:
20708c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_ACA:
20718c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
20728c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
20738c2ecf20Sopenharmony_ci		break;
20748c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
20758c2ecf20Sopenharmony_ci		if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) {
20768c2ecf20Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
20778c2ecf20Sopenharmony_ci			goto attach;
20788c2ecf20Sopenharmony_ci		}
20798c2ecf20Sopenharmony_ci		break;
20808c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
20818c2ecf20Sopenharmony_ci		if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) {
20828c2ecf20Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
20838c2ecf20Sopenharmony_ci			goto attach;
20848c2ecf20Sopenharmony_ci		}
20858c2ecf20Sopenharmony_ci		break;
20868c2ecf20Sopenharmony_ci	case ISCSI_TM_FUNC_TASK_REASSIGN:
20878c2ecf20Sopenharmony_ci		se_tmr->response = iscsit_tmr_task_reassign(cmd, buf);
20888c2ecf20Sopenharmony_ci		/*
20898c2ecf20Sopenharmony_ci		 * Perform sanity checks on the ExpDataSN only if the
20908c2ecf20Sopenharmony_ci		 * TASK_REASSIGN was successful.
20918c2ecf20Sopenharmony_ci		 */
20928c2ecf20Sopenharmony_ci		if (se_tmr->response)
20938c2ecf20Sopenharmony_ci			break;
20948c2ecf20Sopenharmony_ci
20958c2ecf20Sopenharmony_ci		if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0)
20968c2ecf20Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
20978c2ecf20Sopenharmony_ci					ISCSI_REASON_BOOKMARK_INVALID, buf);
20988c2ecf20Sopenharmony_ci		break;
20998c2ecf20Sopenharmony_ci	default:
21008c2ecf20Sopenharmony_ci		pr_err("Unknown TMR function: 0x%02x, protocol"
21018c2ecf20Sopenharmony_ci			" error.\n", function);
21028c2ecf20Sopenharmony_ci		se_tmr->response = ISCSI_TMF_RSP_NOT_SUPPORTED;
21038c2ecf20Sopenharmony_ci		goto attach;
21048c2ecf20Sopenharmony_ci	}
21058c2ecf20Sopenharmony_ci
21068c2ecf20Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
21078c2ecf20Sopenharmony_ci	    (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
21088c2ecf20Sopenharmony_ci		se_tmr->call_transport = 1;
21098c2ecf20Sopenharmony_ciattach:
21108c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
21118c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
21128c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
21138c2ecf20Sopenharmony_ci
21148c2ecf20Sopenharmony_ci	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
21158c2ecf20Sopenharmony_ci		int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
21168c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) {
21178c2ecf20Sopenharmony_ci			out_of_order_cmdsn = 1;
21188c2ecf20Sopenharmony_ci		} else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
21198c2ecf20Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
21208c2ecf20Sopenharmony_ci			return 0;
21218c2ecf20Sopenharmony_ci		} else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
21228c2ecf20Sopenharmony_ci			return -1;
21238c2ecf20Sopenharmony_ci		}
21248c2ecf20Sopenharmony_ci	}
21258c2ecf20Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
21268c2ecf20Sopenharmony_ci
21278c2ecf20Sopenharmony_ci	if (out_of_order_cmdsn || !(hdr->opcode & ISCSI_OP_IMMEDIATE))
21288c2ecf20Sopenharmony_ci		return 0;
21298c2ecf20Sopenharmony_ci	/*
21308c2ecf20Sopenharmony_ci	 * Found the referenced task, send to transport for processing.
21318c2ecf20Sopenharmony_ci	 */
21328c2ecf20Sopenharmony_ci	if (se_tmr->call_transport)
21338c2ecf20Sopenharmony_ci		return transport_generic_handle_tmr(&cmd->se_cmd);
21348c2ecf20Sopenharmony_ci
21358c2ecf20Sopenharmony_ci	/*
21368c2ecf20Sopenharmony_ci	 * Could not find the referenced LUN, task, or Task Management
21378c2ecf20Sopenharmony_ci	 * command not authorized or supported.  Change state and
21388c2ecf20Sopenharmony_ci	 * let the tx_thread send the response.
21398c2ecf20Sopenharmony_ci	 *
21408c2ecf20Sopenharmony_ci	 * For connection recovery, this is also the default action for
21418c2ecf20Sopenharmony_ci	 * TMR TASK_REASSIGN.
21428c2ecf20Sopenharmony_ci	 */
21438c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
21448c2ecf20Sopenharmony_ci	target_put_sess_cmd(&cmd->se_cmd);
21458c2ecf20Sopenharmony_ci	return 0;
21468c2ecf20Sopenharmony_ci}
21478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
21488c2ecf20Sopenharmony_ci
21498c2ecf20Sopenharmony_ci/* #warning FIXME: Support Text Command parameters besides SendTargets */
21508c2ecf20Sopenharmony_ciint
21518c2ecf20Sopenharmony_ciiscsit_setup_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
21528c2ecf20Sopenharmony_ci		      struct iscsi_text *hdr)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
21558c2ecf20Sopenharmony_ci
21568c2ecf20Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
21578c2ecf20Sopenharmony_ci		pr_err("Unable to accept text parameter length: %u"
21588c2ecf20Sopenharmony_ci			"greater than MaxXmitDataSegmentLength %u.\n",
21598c2ecf20Sopenharmony_ci		       payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
21608c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
21618c2ecf20Sopenharmony_ci					 (unsigned char *)hdr);
21628c2ecf20Sopenharmony_ci	}
21638c2ecf20Sopenharmony_ci
21648c2ecf20Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
21658c2ecf20Sopenharmony_ci	     (hdr->flags & ISCSI_FLAG_TEXT_CONTINUE)) {
21668c2ecf20Sopenharmony_ci		pr_err("Multi sequence text commands currently not supported\n");
21678c2ecf20Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_CMD_NOT_SUPPORTED,
21688c2ecf20Sopenharmony_ci					(unsigned char *)hdr);
21698c2ecf20Sopenharmony_ci	}
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
21728c2ecf20Sopenharmony_ci		" ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
21738c2ecf20Sopenharmony_ci		hdr->exp_statsn, payload_length);
21748c2ecf20Sopenharmony_ci
21758c2ecf20Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_TEXT;
21768c2ecf20Sopenharmony_ci	cmd->i_state		= ISTATE_SEND_TEXTRSP;
21778c2ecf20Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
21788c2ecf20Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag  = hdr->itt;
21798c2ecf20Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
21808c2ecf20Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
21818c2ecf20Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
21828c2ecf20Sopenharmony_ci	cmd->data_direction	= DMA_NONE;
21838c2ecf20Sopenharmony_ci	kfree(cmd->text_in_ptr);
21848c2ecf20Sopenharmony_ci	cmd->text_in_ptr	= NULL;
21858c2ecf20Sopenharmony_ci
21868c2ecf20Sopenharmony_ci	return 0;
21878c2ecf20Sopenharmony_ci}
21888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_text_cmd);
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ciint
21918c2ecf20Sopenharmony_ciiscsit_process_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
21928c2ecf20Sopenharmony_ci			struct iscsi_text *hdr)
21938c2ecf20Sopenharmony_ci{
21948c2ecf20Sopenharmony_ci	unsigned char *text_in = cmd->text_in_ptr, *text_ptr;
21958c2ecf20Sopenharmony_ci	int cmdsn_ret;
21968c2ecf20Sopenharmony_ci
21978c2ecf20Sopenharmony_ci	if (!text_in) {
21988c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag = be32_to_cpu(hdr->ttt);
21998c2ecf20Sopenharmony_ci		if (cmd->targ_xfer_tag == 0xFFFFFFFF) {
22008c2ecf20Sopenharmony_ci			pr_err("Unable to locate text_in buffer for sendtargets"
22018c2ecf20Sopenharmony_ci			       " discovery\n");
22028c2ecf20Sopenharmony_ci			goto reject;
22038c2ecf20Sopenharmony_ci		}
22048c2ecf20Sopenharmony_ci		goto empty_sendtargets;
22058c2ecf20Sopenharmony_ci	}
22068c2ecf20Sopenharmony_ci	if (strncmp("SendTargets=", text_in, 12) != 0) {
22078c2ecf20Sopenharmony_ci		pr_err("Received Text Data that is not"
22088c2ecf20Sopenharmony_ci			" SendTargets, cannot continue.\n");
22098c2ecf20Sopenharmony_ci		goto reject;
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci	/* '=' confirmed in strncmp */
22128c2ecf20Sopenharmony_ci	text_ptr = strchr(text_in, '=');
22138c2ecf20Sopenharmony_ci	BUG_ON(!text_ptr);
22148c2ecf20Sopenharmony_ci	if (!strncmp("=All", text_ptr, 5)) {
22158c2ecf20Sopenharmony_ci		cmd->cmd_flags |= ICF_SENDTARGETS_ALL;
22168c2ecf20Sopenharmony_ci	} else if (!strncmp("=iqn.", text_ptr, 5) ||
22178c2ecf20Sopenharmony_ci		   !strncmp("=eui.", text_ptr, 5)) {
22188c2ecf20Sopenharmony_ci		cmd->cmd_flags |= ICF_SENDTARGETS_SINGLE;
22198c2ecf20Sopenharmony_ci	} else {
22208c2ecf20Sopenharmony_ci		pr_err("Unable to locate valid SendTargets%s value\n",
22218c2ecf20Sopenharmony_ci		       text_ptr);
22228c2ecf20Sopenharmony_ci		goto reject;
22238c2ecf20Sopenharmony_ci	}
22248c2ecf20Sopenharmony_ci
22258c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
22268c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
22278c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ciempty_sendtargets:
22308c2ecf20Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
22318c2ecf20Sopenharmony_ci
22328c2ecf20Sopenharmony_ci	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
22338c2ecf20Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
22348c2ecf20Sopenharmony_ci				(unsigned char *)hdr, hdr->cmdsn);
22358c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
22368c2ecf20Sopenharmony_ci			return -1;
22378c2ecf20Sopenharmony_ci
22388c2ecf20Sopenharmony_ci		return 0;
22398c2ecf20Sopenharmony_ci	}
22408c2ecf20Sopenharmony_ci
22418c2ecf20Sopenharmony_ci	return iscsit_execute_cmd(cmd, 0);
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_cireject:
22448c2ecf20Sopenharmony_ci	return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
22458c2ecf20Sopenharmony_ci				 (unsigned char *)hdr);
22468c2ecf20Sopenharmony_ci}
22478c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_text_cmd);
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_cistatic int
22508c2ecf20Sopenharmony_ciiscsit_handle_text_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
22518c2ecf20Sopenharmony_ci		       unsigned char *buf)
22528c2ecf20Sopenharmony_ci{
22538c2ecf20Sopenharmony_ci	struct iscsi_text *hdr = (struct iscsi_text *)buf;
22548c2ecf20Sopenharmony_ci	char *text_in = NULL;
22558c2ecf20Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
22568c2ecf20Sopenharmony_ci	int rx_size, rc;
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	rc = iscsit_setup_text_cmd(conn, cmd, hdr);
22598c2ecf20Sopenharmony_ci	if (rc < 0)
22608c2ecf20Sopenharmony_ci		return 0;
22618c2ecf20Sopenharmony_ci
22628c2ecf20Sopenharmony_ci	rx_size = payload_length;
22638c2ecf20Sopenharmony_ci	if (payload_length) {
22648c2ecf20Sopenharmony_ci		u32 checksum = 0, data_crc = 0;
22658c2ecf20Sopenharmony_ci		u32 padding = 0;
22668c2ecf20Sopenharmony_ci		int niov = 0, rx_got;
22678c2ecf20Sopenharmony_ci		struct kvec iov[2];
22688c2ecf20Sopenharmony_ci
22698c2ecf20Sopenharmony_ci		rx_size = ALIGN(payload_length, 4);
22708c2ecf20Sopenharmony_ci		text_in = kzalloc(rx_size, GFP_KERNEL);
22718c2ecf20Sopenharmony_ci		if (!text_in)
22728c2ecf20Sopenharmony_ci			goto reject;
22738c2ecf20Sopenharmony_ci
22748c2ecf20Sopenharmony_ci		cmd->text_in_ptr = text_in;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci		memset(iov, 0, sizeof(iov));
22778c2ecf20Sopenharmony_ci		iov[niov].iov_base	= text_in;
22788c2ecf20Sopenharmony_ci		iov[niov++].iov_len	= rx_size;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci		padding = rx_size - payload_length;
22818c2ecf20Sopenharmony_ci		if (padding)
22828c2ecf20Sopenharmony_ci			pr_debug("Receiving %u additional bytes"
22838c2ecf20Sopenharmony_ci					" for padding.\n", padding);
22848c2ecf20Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
22858c2ecf20Sopenharmony_ci			iov[niov].iov_base	= &checksum;
22868c2ecf20Sopenharmony_ci			iov[niov++].iov_len	= ISCSI_CRC_LEN;
22878c2ecf20Sopenharmony_ci			rx_size += ISCSI_CRC_LEN;
22888c2ecf20Sopenharmony_ci		}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci		WARN_ON_ONCE(niov > ARRAY_SIZE(iov));
22918c2ecf20Sopenharmony_ci		rx_got = rx_data(conn, &iov[0], niov, rx_size);
22928c2ecf20Sopenharmony_ci		if (rx_got != rx_size)
22938c2ecf20Sopenharmony_ci			goto reject;
22948c2ecf20Sopenharmony_ci
22958c2ecf20Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
22968c2ecf20Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash,
22978c2ecf20Sopenharmony_ci						  text_in, rx_size, 0, NULL,
22988c2ecf20Sopenharmony_ci						  &data_crc);
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci			if (checksum != data_crc) {
23018c2ecf20Sopenharmony_ci				pr_err("Text data CRC32C DataDigest"
23028c2ecf20Sopenharmony_ci					" 0x%08x does not match computed"
23038c2ecf20Sopenharmony_ci					" 0x%08x\n", checksum, data_crc);
23048c2ecf20Sopenharmony_ci				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
23058c2ecf20Sopenharmony_ci					pr_err("Unable to recover from"
23068c2ecf20Sopenharmony_ci					" Text Data digest failure while in"
23078c2ecf20Sopenharmony_ci						" ERL=0.\n");
23088c2ecf20Sopenharmony_ci					goto reject;
23098c2ecf20Sopenharmony_ci				} else {
23108c2ecf20Sopenharmony_ci					/*
23118c2ecf20Sopenharmony_ci					 * Silently drop this PDU and let the
23128c2ecf20Sopenharmony_ci					 * initiator plug the CmdSN gap.
23138c2ecf20Sopenharmony_ci					 */
23148c2ecf20Sopenharmony_ci					pr_debug("Dropping Text"
23158c2ecf20Sopenharmony_ci					" Command CmdSN: 0x%08x due to"
23168c2ecf20Sopenharmony_ci					" DataCRC error.\n", hdr->cmdsn);
23178c2ecf20Sopenharmony_ci					kfree(text_in);
23188c2ecf20Sopenharmony_ci					return 0;
23198c2ecf20Sopenharmony_ci				}
23208c2ecf20Sopenharmony_ci			} else {
23218c2ecf20Sopenharmony_ci				pr_debug("Got CRC32C DataDigest"
23228c2ecf20Sopenharmony_ci					" 0x%08x for %u bytes of text data.\n",
23238c2ecf20Sopenharmony_ci						checksum, payload_length);
23248c2ecf20Sopenharmony_ci			}
23258c2ecf20Sopenharmony_ci		}
23268c2ecf20Sopenharmony_ci		text_in[payload_length - 1] = '\0';
23278c2ecf20Sopenharmony_ci		pr_debug("Successfully read %d bytes of text"
23288c2ecf20Sopenharmony_ci				" data.\n", payload_length);
23298c2ecf20Sopenharmony_ci	}
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci	return iscsit_process_text_cmd(conn, cmd, hdr);
23328c2ecf20Sopenharmony_ci
23338c2ecf20Sopenharmony_cireject:
23348c2ecf20Sopenharmony_ci	kfree(cmd->text_in_ptr);
23358c2ecf20Sopenharmony_ci	cmd->text_in_ptr = NULL;
23368c2ecf20Sopenharmony_ci	return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
23378c2ecf20Sopenharmony_ci}
23388c2ecf20Sopenharmony_ci
23398c2ecf20Sopenharmony_ciint iscsit_logout_closesession(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
23408c2ecf20Sopenharmony_ci{
23418c2ecf20Sopenharmony_ci	struct iscsi_conn *conn_p;
23428c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	pr_debug("Received logout request CLOSESESSION on CID: %hu"
23458c2ecf20Sopenharmony_ci		" for SID: %u.\n", conn->cid, conn->sess->sid);
23468c2ecf20Sopenharmony_ci
23478c2ecf20Sopenharmony_ci	atomic_set(&sess->session_logout, 1);
23488c2ecf20Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 1);
23498c2ecf20Sopenharmony_ci	conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_SESSION;
23508c2ecf20Sopenharmony_ci
23518c2ecf20Sopenharmony_ci	iscsit_inc_conn_usage_count(conn);
23528c2ecf20Sopenharmony_ci	iscsit_inc_session_usage_count(sess);
23538c2ecf20Sopenharmony_ci
23548c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
23558c2ecf20Sopenharmony_ci	list_for_each_entry(conn_p, &sess->sess_conn_list, conn_list) {
23568c2ecf20Sopenharmony_ci		if (conn_p->conn_state != TARG_CONN_STATE_LOGGED_IN)
23578c2ecf20Sopenharmony_ci			continue;
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
23608c2ecf20Sopenharmony_ci		conn_p->conn_state = TARG_CONN_STATE_IN_LOGOUT;
23618c2ecf20Sopenharmony_ci	}
23628c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
23638c2ecf20Sopenharmony_ci
23648c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
23658c2ecf20Sopenharmony_ci
23668c2ecf20Sopenharmony_ci	return 0;
23678c2ecf20Sopenharmony_ci}
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_ciint iscsit_logout_closeconnection(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
23708c2ecf20Sopenharmony_ci{
23718c2ecf20Sopenharmony_ci	struct iscsi_conn *l_conn;
23728c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci	pr_debug("Received logout request CLOSECONNECTION for CID:"
23758c2ecf20Sopenharmony_ci		" %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_ci	/*
23788c2ecf20Sopenharmony_ci	 * A Logout Request with a CLOSECONNECTION reason code for a CID
23798c2ecf20Sopenharmony_ci	 * can arrive on a connection with a differing CID.
23808c2ecf20Sopenharmony_ci	 */
23818c2ecf20Sopenharmony_ci	if (conn->cid == cmd->logout_cid) {
23828c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
23838c2ecf20Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
23848c2ecf20Sopenharmony_ci		conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci		atomic_set(&conn->conn_logout_remove, 1);
23878c2ecf20Sopenharmony_ci		conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_CONNECTION;
23888c2ecf20Sopenharmony_ci		iscsit_inc_conn_usage_count(conn);
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
23918c2ecf20Sopenharmony_ci	} else {
23928c2ecf20Sopenharmony_ci		/*
23938c2ecf20Sopenharmony_ci		 * Handle all different cid CLOSECONNECTION requests in
23948c2ecf20Sopenharmony_ci		 * iscsit_logout_post_handler_diffcid() as to give enough
23958c2ecf20Sopenharmony_ci		 * time for any non immediate command's CmdSN to be
23968c2ecf20Sopenharmony_ci		 * acknowledged on the connection in question.
23978c2ecf20Sopenharmony_ci		 *
23988c2ecf20Sopenharmony_ci		 * Here we simply make sure the CID is still around.
23998c2ecf20Sopenharmony_ci		 */
24008c2ecf20Sopenharmony_ci		l_conn = iscsit_get_conn_from_cid(sess,
24018c2ecf20Sopenharmony_ci				cmd->logout_cid);
24028c2ecf20Sopenharmony_ci		if (!l_conn) {
24038c2ecf20Sopenharmony_ci			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
24048c2ecf20Sopenharmony_ci			iscsit_add_cmd_to_response_queue(cmd, conn,
24058c2ecf20Sopenharmony_ci					cmd->i_state);
24068c2ecf20Sopenharmony_ci			return 0;
24078c2ecf20Sopenharmony_ci		}
24088c2ecf20Sopenharmony_ci
24098c2ecf20Sopenharmony_ci		iscsit_dec_conn_usage_count(l_conn);
24108c2ecf20Sopenharmony_ci	}
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
24138c2ecf20Sopenharmony_ci
24148c2ecf20Sopenharmony_ci	return 0;
24158c2ecf20Sopenharmony_ci}
24168c2ecf20Sopenharmony_ci
24178c2ecf20Sopenharmony_ciint iscsit_logout_removeconnforrecovery(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
24188c2ecf20Sopenharmony_ci{
24198c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
24208c2ecf20Sopenharmony_ci
24218c2ecf20Sopenharmony_ci	pr_debug("Received explicit REMOVECONNFORRECOVERY logout for"
24228c2ecf20Sopenharmony_ci		" CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
24238c2ecf20Sopenharmony_ci
24248c2ecf20Sopenharmony_ci	if (sess->sess_ops->ErrorRecoveryLevel != 2) {
24258c2ecf20Sopenharmony_ci		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
24268c2ecf20Sopenharmony_ci			" while ERL!=2.\n");
24278c2ecf20Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED;
24288c2ecf20Sopenharmony_ci		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
24298c2ecf20Sopenharmony_ci		return 0;
24308c2ecf20Sopenharmony_ci	}
24318c2ecf20Sopenharmony_ci
24328c2ecf20Sopenharmony_ci	if (conn->cid == cmd->logout_cid) {
24338c2ecf20Sopenharmony_ci		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
24348c2ecf20Sopenharmony_ci			" with CID: %hu on CID: %hu, implementation error.\n",
24358c2ecf20Sopenharmony_ci				cmd->logout_cid, conn->cid);
24368c2ecf20Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_CLEANUP_FAILED;
24378c2ecf20Sopenharmony_ci		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
24388c2ecf20Sopenharmony_ci		return 0;
24398c2ecf20Sopenharmony_ci	}
24408c2ecf20Sopenharmony_ci
24418c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	return 0;
24448c2ecf20Sopenharmony_ci}
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ciint
24478c2ecf20Sopenharmony_ciiscsit_handle_logout_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd,
24488c2ecf20Sopenharmony_ci			unsigned char *buf)
24498c2ecf20Sopenharmony_ci{
24508c2ecf20Sopenharmony_ci	int cmdsn_ret, logout_remove = 0;
24518c2ecf20Sopenharmony_ci	u8 reason_code = 0;
24528c2ecf20Sopenharmony_ci	struct iscsi_logout *hdr;
24538c2ecf20Sopenharmony_ci	struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn);
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_logout *) buf;
24568c2ecf20Sopenharmony_ci	reason_code		= (hdr->flags & 0x7f);
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	if (tiqn) {
24598c2ecf20Sopenharmony_ci		spin_lock(&tiqn->logout_stats.lock);
24608c2ecf20Sopenharmony_ci		if (reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION)
24618c2ecf20Sopenharmony_ci			tiqn->logout_stats.normal_logouts++;
24628c2ecf20Sopenharmony_ci		else
24638c2ecf20Sopenharmony_ci			tiqn->logout_stats.abnormal_logouts++;
24648c2ecf20Sopenharmony_ci		spin_unlock(&tiqn->logout_stats.lock);
24658c2ecf20Sopenharmony_ci	}
24668c2ecf20Sopenharmony_ci
24678c2ecf20Sopenharmony_ci	pr_debug("Got Logout Request ITT: 0x%08x CmdSN: 0x%08x"
24688c2ecf20Sopenharmony_ci		" ExpStatSN: 0x%08x Reason: 0x%02x CID: %hu on CID: %hu\n",
24698c2ecf20Sopenharmony_ci		hdr->itt, hdr->cmdsn, hdr->exp_statsn, reason_code,
24708c2ecf20Sopenharmony_ci		hdr->cid, conn->cid);
24718c2ecf20Sopenharmony_ci
24728c2ecf20Sopenharmony_ci	if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) {
24738c2ecf20Sopenharmony_ci		pr_err("Received logout request on connection that"
24748c2ecf20Sopenharmony_ci			" is not in logged in state, ignoring request.\n");
24758c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
24768c2ecf20Sopenharmony_ci		return 0;
24778c2ecf20Sopenharmony_ci	}
24788c2ecf20Sopenharmony_ci
24798c2ecf20Sopenharmony_ci	cmd->iscsi_opcode       = ISCSI_OP_LOGOUT;
24808c2ecf20Sopenharmony_ci	cmd->i_state            = ISTATE_SEND_LOGOUTRSP;
24818c2ecf20Sopenharmony_ci	cmd->immediate_cmd      = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
24828c2ecf20Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag  = hdr->itt;
24838c2ecf20Sopenharmony_ci	cmd->targ_xfer_tag      = 0xFFFFFFFF;
24848c2ecf20Sopenharmony_ci	cmd->cmd_sn             = be32_to_cpu(hdr->cmdsn);
24858c2ecf20Sopenharmony_ci	cmd->exp_stat_sn        = be32_to_cpu(hdr->exp_statsn);
24868c2ecf20Sopenharmony_ci	cmd->logout_cid         = be16_to_cpu(hdr->cid);
24878c2ecf20Sopenharmony_ci	cmd->logout_reason      = reason_code;
24888c2ecf20Sopenharmony_ci	cmd->data_direction     = DMA_NONE;
24898c2ecf20Sopenharmony_ci
24908c2ecf20Sopenharmony_ci	/*
24918c2ecf20Sopenharmony_ci	 * We need to sleep in these cases (by returning 1) until the Logout
24928c2ecf20Sopenharmony_ci	 * Response gets sent in the tx thread.
24938c2ecf20Sopenharmony_ci	 */
24948c2ecf20Sopenharmony_ci	if ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) ||
24958c2ecf20Sopenharmony_ci	   ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) &&
24968c2ecf20Sopenharmony_ci	    be16_to_cpu(hdr->cid) == conn->cid))
24978c2ecf20Sopenharmony_ci		logout_remove = 1;
24988c2ecf20Sopenharmony_ci
24998c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
25008c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
25018c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
25028c2ecf20Sopenharmony_ci
25038c2ecf20Sopenharmony_ci	if (reason_code != ISCSI_LOGOUT_REASON_RECOVERY)
25048c2ecf20Sopenharmony_ci		iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
25058c2ecf20Sopenharmony_ci
25068c2ecf20Sopenharmony_ci	/*
25078c2ecf20Sopenharmony_ci	 * Immediate commands are executed, well, immediately.
25088c2ecf20Sopenharmony_ci	 * Non-Immediate Logout Commands are executed in CmdSN order.
25098c2ecf20Sopenharmony_ci	 */
25108c2ecf20Sopenharmony_ci	if (cmd->immediate_cmd) {
25118c2ecf20Sopenharmony_ci		int ret = iscsit_execute_cmd(cmd, 0);
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci		if (ret < 0)
25148c2ecf20Sopenharmony_ci			return ret;
25158c2ecf20Sopenharmony_ci	} else {
25168c2ecf20Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
25178c2ecf20Sopenharmony_ci		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
25188c2ecf20Sopenharmony_ci			logout_remove = 0;
25198c2ecf20Sopenharmony_ci		else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
25208c2ecf20Sopenharmony_ci			return -1;
25218c2ecf20Sopenharmony_ci	}
25228c2ecf20Sopenharmony_ci
25238c2ecf20Sopenharmony_ci	return logout_remove;
25248c2ecf20Sopenharmony_ci}
25258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_logout_cmd);
25268c2ecf20Sopenharmony_ci
25278c2ecf20Sopenharmony_ciint iscsit_handle_snack(
25288c2ecf20Sopenharmony_ci	struct iscsi_conn *conn,
25298c2ecf20Sopenharmony_ci	unsigned char *buf)
25308c2ecf20Sopenharmony_ci{
25318c2ecf20Sopenharmony_ci	struct iscsi_snack *hdr;
25328c2ecf20Sopenharmony_ci
25338c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_snack *) buf;
25348c2ecf20Sopenharmony_ci	hdr->flags		&= ~ISCSI_FLAG_CMD_FINAL;
25358c2ecf20Sopenharmony_ci
25368c2ecf20Sopenharmony_ci	pr_debug("Got ISCSI_INIT_SNACK, ITT: 0x%08x, ExpStatSN:"
25378c2ecf20Sopenharmony_ci		" 0x%08x, Type: 0x%02x, BegRun: 0x%08x, RunLength: 0x%08x,"
25388c2ecf20Sopenharmony_ci		" CID: %hu\n", hdr->itt, hdr->exp_statsn, hdr->flags,
25398c2ecf20Sopenharmony_ci			hdr->begrun, hdr->runlength, conn->cid);
25408c2ecf20Sopenharmony_ci
25418c2ecf20Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
25428c2ecf20Sopenharmony_ci		pr_err("Initiator sent SNACK request while in"
25438c2ecf20Sopenharmony_ci			" ErrorRecoveryLevel=0.\n");
25448c2ecf20Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
25458c2ecf20Sopenharmony_ci					 buf);
25468c2ecf20Sopenharmony_ci	}
25478c2ecf20Sopenharmony_ci	/*
25488c2ecf20Sopenharmony_ci	 * SNACK_DATA and SNACK_R2T are both 0,  so check which function to
25498c2ecf20Sopenharmony_ci	 * call from inside iscsi_send_recovery_datain_or_r2t().
25508c2ecf20Sopenharmony_ci	 */
25518c2ecf20Sopenharmony_ci	switch (hdr->flags & ISCSI_FLAG_SNACK_TYPE_MASK) {
25528c2ecf20Sopenharmony_ci	case 0:
25538c2ecf20Sopenharmony_ci		return iscsit_handle_recovery_datain_or_r2t(conn, buf,
25548c2ecf20Sopenharmony_ci			hdr->itt,
25558c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->ttt),
25568c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->begrun),
25578c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->runlength));
25588c2ecf20Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_STATUS:
25598c2ecf20Sopenharmony_ci		return iscsit_handle_status_snack(conn, hdr->itt,
25608c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->ttt),
25618c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->begrun), be32_to_cpu(hdr->runlength));
25628c2ecf20Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_DATA_ACK:
25638c2ecf20Sopenharmony_ci		return iscsit_handle_data_ack(conn, be32_to_cpu(hdr->ttt),
25648c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->begrun),
25658c2ecf20Sopenharmony_ci			be32_to_cpu(hdr->runlength));
25668c2ecf20Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_RDATA:
25678c2ecf20Sopenharmony_ci		/* FIXME: Support R-Data SNACK */
25688c2ecf20Sopenharmony_ci		pr_err("R-Data SNACK Not Supported.\n");
25698c2ecf20Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
25708c2ecf20Sopenharmony_ci					 buf);
25718c2ecf20Sopenharmony_ci	default:
25728c2ecf20Sopenharmony_ci		pr_err("Unknown SNACK type 0x%02x, protocol"
25738c2ecf20Sopenharmony_ci			" error.\n", hdr->flags & 0x0f);
25748c2ecf20Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
25758c2ecf20Sopenharmony_ci					 buf);
25768c2ecf20Sopenharmony_ci	}
25778c2ecf20Sopenharmony_ci
25788c2ecf20Sopenharmony_ci	return 0;
25798c2ecf20Sopenharmony_ci}
25808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_snack);
25818c2ecf20Sopenharmony_ci
25828c2ecf20Sopenharmony_cistatic void iscsit_rx_thread_wait_for_tcp(struct iscsi_conn *conn)
25838c2ecf20Sopenharmony_ci{
25848c2ecf20Sopenharmony_ci	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
25858c2ecf20Sopenharmony_ci	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
25868c2ecf20Sopenharmony_ci		wait_for_completion_interruptible_timeout(
25878c2ecf20Sopenharmony_ci					&conn->rx_half_close_comp,
25888c2ecf20Sopenharmony_ci					ISCSI_RX_THREAD_TCP_TIMEOUT * HZ);
25898c2ecf20Sopenharmony_ci	}
25908c2ecf20Sopenharmony_ci}
25918c2ecf20Sopenharmony_ci
25928c2ecf20Sopenharmony_cistatic int iscsit_handle_immediate_data(
25938c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
25948c2ecf20Sopenharmony_ci	struct iscsi_scsi_req *hdr,
25958c2ecf20Sopenharmony_ci	u32 length)
25968c2ecf20Sopenharmony_ci{
25978c2ecf20Sopenharmony_ci	int iov_ret, rx_got = 0, rx_size = 0;
25988c2ecf20Sopenharmony_ci	u32 checksum, iov_count = 0, padding = 0;
25998c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
26008c2ecf20Sopenharmony_ci	struct kvec *iov;
26018c2ecf20Sopenharmony_ci	void *overflow_buf = NULL;
26028c2ecf20Sopenharmony_ci
26038c2ecf20Sopenharmony_ci	BUG_ON(cmd->write_data_done > cmd->se_cmd.data_length);
26048c2ecf20Sopenharmony_ci	rx_size = min(cmd->se_cmd.data_length - cmd->write_data_done, length);
26058c2ecf20Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, cmd->iov_data,
26068c2ecf20Sopenharmony_ci				   cmd->orig_iov_data_count - 2,
26078c2ecf20Sopenharmony_ci				   cmd->write_data_done, rx_size);
26088c2ecf20Sopenharmony_ci	if (iov_ret < 0)
26098c2ecf20Sopenharmony_ci		return IMMEDIATE_DATA_CANNOT_RECOVER;
26108c2ecf20Sopenharmony_ci
26118c2ecf20Sopenharmony_ci	iov_count = iov_ret;
26128c2ecf20Sopenharmony_ci	iov = &cmd->iov_data[0];
26138c2ecf20Sopenharmony_ci	if (rx_size < length) {
26148c2ecf20Sopenharmony_ci		/*
26158c2ecf20Sopenharmony_ci		 * Special case: length of immediate data exceeds the data
26168c2ecf20Sopenharmony_ci		 * buffer size derived from the CDB.
26178c2ecf20Sopenharmony_ci		 */
26188c2ecf20Sopenharmony_ci		overflow_buf = kmalloc(length - rx_size, GFP_KERNEL);
26198c2ecf20Sopenharmony_ci		if (!overflow_buf) {
26208c2ecf20Sopenharmony_ci			iscsit_unmap_iovec(cmd);
26218c2ecf20Sopenharmony_ci			return IMMEDIATE_DATA_CANNOT_RECOVER;
26228c2ecf20Sopenharmony_ci		}
26238c2ecf20Sopenharmony_ci		cmd->overflow_buf = overflow_buf;
26248c2ecf20Sopenharmony_ci		iov[iov_count].iov_base = overflow_buf;
26258c2ecf20Sopenharmony_ci		iov[iov_count].iov_len = length - rx_size;
26268c2ecf20Sopenharmony_ci		iov_count++;
26278c2ecf20Sopenharmony_ci		rx_size = length;
26288c2ecf20Sopenharmony_ci	}
26298c2ecf20Sopenharmony_ci
26308c2ecf20Sopenharmony_ci	padding = ((-length) & 3);
26318c2ecf20Sopenharmony_ci	if (padding != 0) {
26328c2ecf20Sopenharmony_ci		iov[iov_count].iov_base	= cmd->pad_bytes;
26338c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len = padding;
26348c2ecf20Sopenharmony_ci		rx_size += padding;
26358c2ecf20Sopenharmony_ci	}
26368c2ecf20Sopenharmony_ci
26378c2ecf20Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
26388c2ecf20Sopenharmony_ci		iov[iov_count].iov_base		= &checksum;
26398c2ecf20Sopenharmony_ci		iov[iov_count++].iov_len	= ISCSI_CRC_LEN;
26408c2ecf20Sopenharmony_ci		rx_size += ISCSI_CRC_LEN;
26418c2ecf20Sopenharmony_ci	}
26428c2ecf20Sopenharmony_ci
26438c2ecf20Sopenharmony_ci	WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count);
26448c2ecf20Sopenharmony_ci	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
26458c2ecf20Sopenharmony_ci
26468c2ecf20Sopenharmony_ci	iscsit_unmap_iovec(cmd);
26478c2ecf20Sopenharmony_ci
26488c2ecf20Sopenharmony_ci	if (rx_got != rx_size) {
26498c2ecf20Sopenharmony_ci		iscsit_rx_thread_wait_for_tcp(conn);
26508c2ecf20Sopenharmony_ci		return IMMEDIATE_DATA_CANNOT_RECOVER;
26518c2ecf20Sopenharmony_ci	}
26528c2ecf20Sopenharmony_ci
26538c2ecf20Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
26548c2ecf20Sopenharmony_ci		u32 data_crc;
26558c2ecf20Sopenharmony_ci
26568c2ecf20Sopenharmony_ci		data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd,
26578c2ecf20Sopenharmony_ci						    cmd->write_data_done, length, padding,
26588c2ecf20Sopenharmony_ci						    cmd->pad_bytes);
26598c2ecf20Sopenharmony_ci
26608c2ecf20Sopenharmony_ci		if (checksum != data_crc) {
26618c2ecf20Sopenharmony_ci			pr_err("ImmediateData CRC32C DataDigest 0x%08x"
26628c2ecf20Sopenharmony_ci				" does not match computed 0x%08x\n", checksum,
26638c2ecf20Sopenharmony_ci				data_crc);
26648c2ecf20Sopenharmony_ci
26658c2ecf20Sopenharmony_ci			if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
26668c2ecf20Sopenharmony_ci				pr_err("Unable to recover from"
26678c2ecf20Sopenharmony_ci					" Immediate Data digest failure while"
26688c2ecf20Sopenharmony_ci					" in ERL=0.\n");
26698c2ecf20Sopenharmony_ci				iscsit_reject_cmd(cmd,
26708c2ecf20Sopenharmony_ci						ISCSI_REASON_DATA_DIGEST_ERROR,
26718c2ecf20Sopenharmony_ci						(unsigned char *)hdr);
26728c2ecf20Sopenharmony_ci				return IMMEDIATE_DATA_CANNOT_RECOVER;
26738c2ecf20Sopenharmony_ci			} else {
26748c2ecf20Sopenharmony_ci				iscsit_reject_cmd(cmd,
26758c2ecf20Sopenharmony_ci						ISCSI_REASON_DATA_DIGEST_ERROR,
26768c2ecf20Sopenharmony_ci						(unsigned char *)hdr);
26778c2ecf20Sopenharmony_ci				return IMMEDIATE_DATA_ERL1_CRC_FAILURE;
26788c2ecf20Sopenharmony_ci			}
26798c2ecf20Sopenharmony_ci		} else {
26808c2ecf20Sopenharmony_ci			pr_debug("Got CRC32C DataDigest 0x%08x for"
26818c2ecf20Sopenharmony_ci				" %u bytes of Immediate Data\n", checksum,
26828c2ecf20Sopenharmony_ci				length);
26838c2ecf20Sopenharmony_ci		}
26848c2ecf20Sopenharmony_ci	}
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci	cmd->write_data_done += length;
26878c2ecf20Sopenharmony_ci
26888c2ecf20Sopenharmony_ci	if (cmd->write_data_done == cmd->se_cmd.data_length) {
26898c2ecf20Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
26908c2ecf20Sopenharmony_ci		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
26918c2ecf20Sopenharmony_ci		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
26928c2ecf20Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
26938c2ecf20Sopenharmony_ci	}
26948c2ecf20Sopenharmony_ci
26958c2ecf20Sopenharmony_ci	return IMMEDIATE_DATA_NORMAL_OPERATION;
26968c2ecf20Sopenharmony_ci}
26978c2ecf20Sopenharmony_ci
26988c2ecf20Sopenharmony_ci/* #warning iscsi_build_conn_drop_async_message() only sends out on connections
26998c2ecf20Sopenharmony_ci	with active network interface */
27008c2ecf20Sopenharmony_cistatic void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn)
27018c2ecf20Sopenharmony_ci{
27028c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
27038c2ecf20Sopenharmony_ci	struct iscsi_conn *conn_p;
27048c2ecf20Sopenharmony_ci	bool found = false;
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci	lockdep_assert_held(&conn->sess->conn_lock);
27078c2ecf20Sopenharmony_ci
27088c2ecf20Sopenharmony_ci	/*
27098c2ecf20Sopenharmony_ci	 * Only send a Asynchronous Message on connections whos network
27108c2ecf20Sopenharmony_ci	 * interface is still functional.
27118c2ecf20Sopenharmony_ci	 */
27128c2ecf20Sopenharmony_ci	list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) {
27138c2ecf20Sopenharmony_ci		if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) {
27148c2ecf20Sopenharmony_ci			iscsit_inc_conn_usage_count(conn_p);
27158c2ecf20Sopenharmony_ci			found = true;
27168c2ecf20Sopenharmony_ci			break;
27178c2ecf20Sopenharmony_ci		}
27188c2ecf20Sopenharmony_ci	}
27198c2ecf20Sopenharmony_ci
27208c2ecf20Sopenharmony_ci	if (!found)
27218c2ecf20Sopenharmony_ci		return;
27228c2ecf20Sopenharmony_ci
27238c2ecf20Sopenharmony_ci	cmd = iscsit_allocate_cmd(conn_p, TASK_RUNNING);
27248c2ecf20Sopenharmony_ci	if (!cmd) {
27258c2ecf20Sopenharmony_ci		iscsit_dec_conn_usage_count(conn_p);
27268c2ecf20Sopenharmony_ci		return;
27278c2ecf20Sopenharmony_ci	}
27288c2ecf20Sopenharmony_ci
27298c2ecf20Sopenharmony_ci	cmd->logout_cid = conn->cid;
27308c2ecf20Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
27318c2ecf20Sopenharmony_ci	cmd->i_state = ISTATE_SEND_ASYNCMSG;
27328c2ecf20Sopenharmony_ci
27338c2ecf20Sopenharmony_ci	spin_lock_bh(&conn_p->cmd_lock);
27348c2ecf20Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn_p->conn_cmd_list);
27358c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn_p->cmd_lock);
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn_p, cmd->i_state);
27388c2ecf20Sopenharmony_ci	iscsit_dec_conn_usage_count(conn_p);
27398c2ecf20Sopenharmony_ci}
27408c2ecf20Sopenharmony_ci
27418c2ecf20Sopenharmony_cistatic int iscsit_send_conn_drop_async_message(
27428c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
27438c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
27448c2ecf20Sopenharmony_ci{
27458c2ecf20Sopenharmony_ci	struct iscsi_async *hdr;
27468c2ecf20Sopenharmony_ci
27478c2ecf20Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
27488c2ecf20Sopenharmony_ci
27498c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_async *) cmd->pdu;
27508c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_ASYNC_EVENT;
27518c2ecf20Sopenharmony_ci	hdr->flags		= ISCSI_FLAG_CMD_FINAL;
27528c2ecf20Sopenharmony_ci	cmd->init_task_tag	= RESERVED_ITT;
27538c2ecf20Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
27548c2ecf20Sopenharmony_ci	put_unaligned_be64(0xFFFFFFFFFFFFFFFFULL, &hdr->rsvd4[0]);
27558c2ecf20Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
27568c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
27578c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
27588c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
27598c2ecf20Sopenharmony_ci	hdr->async_event	= ISCSI_ASYNC_MSG_DROPPING_CONNECTION;
27608c2ecf20Sopenharmony_ci	hdr->param1		= cpu_to_be16(cmd->logout_cid);
27618c2ecf20Sopenharmony_ci	hdr->param2		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait);
27628c2ecf20Sopenharmony_ci	hdr->param3		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Retain);
27638c2ecf20Sopenharmony_ci
27648c2ecf20Sopenharmony_ci	pr_debug("Sending Connection Dropped Async Message StatSN:"
27658c2ecf20Sopenharmony_ci		" 0x%08x, for CID: %hu on CID: %hu\n", cmd->stat_sn,
27668c2ecf20Sopenharmony_ci			cmd->logout_cid, conn->cid);
27678c2ecf20Sopenharmony_ci
27688c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
27698c2ecf20Sopenharmony_ci}
27708c2ecf20Sopenharmony_ci
27718c2ecf20Sopenharmony_cistatic void iscsit_tx_thread_wait_for_tcp(struct iscsi_conn *conn)
27728c2ecf20Sopenharmony_ci{
27738c2ecf20Sopenharmony_ci	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
27748c2ecf20Sopenharmony_ci	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
27758c2ecf20Sopenharmony_ci		wait_for_completion_interruptible_timeout(
27768c2ecf20Sopenharmony_ci					&conn->tx_half_close_comp,
27778c2ecf20Sopenharmony_ci					ISCSI_TX_THREAD_TCP_TIMEOUT * HZ);
27788c2ecf20Sopenharmony_ci	}
27798c2ecf20Sopenharmony_ci}
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_civoid
27828c2ecf20Sopenharmony_ciiscsit_build_datain_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
27838c2ecf20Sopenharmony_ci			struct iscsi_datain *datain, struct iscsi_data_rsp *hdr,
27848c2ecf20Sopenharmony_ci			bool set_statsn)
27858c2ecf20Sopenharmony_ci{
27868c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_DATA_IN;
27878c2ecf20Sopenharmony_ci	hdr->flags		= datain->flags;
27888c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
27898c2ecf20Sopenharmony_ci		if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
27908c2ecf20Sopenharmony_ci			hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW;
27918c2ecf20Sopenharmony_ci			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
27928c2ecf20Sopenharmony_ci		} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
27938c2ecf20Sopenharmony_ci			hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW;
27948c2ecf20Sopenharmony_ci			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
27958c2ecf20Sopenharmony_ci		}
27968c2ecf20Sopenharmony_ci	}
27978c2ecf20Sopenharmony_ci	hton24(hdr->dlength, datain->length);
27988c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_ACK)
27998c2ecf20Sopenharmony_ci		int_to_scsilun(cmd->se_cmd.orig_fe_lun,
28008c2ecf20Sopenharmony_ci				(struct scsi_lun *)&hdr->lun);
28018c2ecf20Sopenharmony_ci	else
28028c2ecf20Sopenharmony_ci		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
28058c2ecf20Sopenharmony_ci
28068c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_ACK)
28078c2ecf20Sopenharmony_ci		hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
28088c2ecf20Sopenharmony_ci	else
28098c2ecf20Sopenharmony_ci		hdr->ttt		= cpu_to_be32(0xFFFFFFFF);
28108c2ecf20Sopenharmony_ci	if (set_statsn)
28118c2ecf20Sopenharmony_ci		hdr->statsn		= cpu_to_be32(cmd->stat_sn);
28128c2ecf20Sopenharmony_ci	else
28138c2ecf20Sopenharmony_ci		hdr->statsn		= cpu_to_be32(0xFFFFFFFF);
28148c2ecf20Sopenharmony_ci
28158c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
28168c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
28178c2ecf20Sopenharmony_ci	hdr->datasn		= cpu_to_be32(datain->data_sn);
28188c2ecf20Sopenharmony_ci	hdr->offset		= cpu_to_be32(datain->offset);
28198c2ecf20Sopenharmony_ci
28208c2ecf20Sopenharmony_ci	pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x,"
28218c2ecf20Sopenharmony_ci		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
28228c2ecf20Sopenharmony_ci		cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn),
28238c2ecf20Sopenharmony_ci		ntohl(hdr->offset), datain->length, conn->cid);
28248c2ecf20Sopenharmony_ci}
28258c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_datain_pdu);
28268c2ecf20Sopenharmony_ci
28278c2ecf20Sopenharmony_cistatic int iscsit_send_datain(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
28288c2ecf20Sopenharmony_ci{
28298c2ecf20Sopenharmony_ci	struct iscsi_data_rsp *hdr = (struct iscsi_data_rsp *)&cmd->pdu[0];
28308c2ecf20Sopenharmony_ci	struct iscsi_datain datain;
28318c2ecf20Sopenharmony_ci	struct iscsi_datain_req *dr;
28328c2ecf20Sopenharmony_ci	int eodr = 0, ret;
28338c2ecf20Sopenharmony_ci	bool set_statsn = false;
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	memset(&datain, 0, sizeof(struct iscsi_datain));
28368c2ecf20Sopenharmony_ci	dr = iscsit_get_datain_values(cmd, &datain);
28378c2ecf20Sopenharmony_ci	if (!dr) {
28388c2ecf20Sopenharmony_ci		pr_err("iscsit_get_datain_values failed for ITT: 0x%08x\n",
28398c2ecf20Sopenharmony_ci				cmd->init_task_tag);
28408c2ecf20Sopenharmony_ci		return -1;
28418c2ecf20Sopenharmony_ci	}
28428c2ecf20Sopenharmony_ci	/*
28438c2ecf20Sopenharmony_ci	 * Be paranoid and double check the logic for now.
28448c2ecf20Sopenharmony_ci	 */
28458c2ecf20Sopenharmony_ci	if ((datain.offset + datain.length) > cmd->se_cmd.data_length) {
28468c2ecf20Sopenharmony_ci		pr_err("Command ITT: 0x%08x, datain.offset: %u and"
28478c2ecf20Sopenharmony_ci			" datain.length: %u exceeds cmd->data_length: %u\n",
28488c2ecf20Sopenharmony_ci			cmd->init_task_tag, datain.offset, datain.length,
28498c2ecf20Sopenharmony_ci			cmd->se_cmd.data_length);
28508c2ecf20Sopenharmony_ci		return -1;
28518c2ecf20Sopenharmony_ci	}
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	atomic_long_add(datain.length, &conn->sess->tx_data_octets);
28548c2ecf20Sopenharmony_ci	/*
28558c2ecf20Sopenharmony_ci	 * Special case for successfully execution w/ both DATAIN
28568c2ecf20Sopenharmony_ci	 * and Sense Data.
28578c2ecf20Sopenharmony_ci	 */
28588c2ecf20Sopenharmony_ci	if ((datain.flags & ISCSI_FLAG_DATA_STATUS) &&
28598c2ecf20Sopenharmony_ci	    (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
28608c2ecf20Sopenharmony_ci		datain.flags &= ~ISCSI_FLAG_DATA_STATUS;
28618c2ecf20Sopenharmony_ci	else {
28628c2ecf20Sopenharmony_ci		if ((dr->dr_complete == DATAIN_COMPLETE_NORMAL) ||
28638c2ecf20Sopenharmony_ci		    (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) {
28648c2ecf20Sopenharmony_ci			iscsit_increment_maxcmdsn(cmd, conn->sess);
28658c2ecf20Sopenharmony_ci			cmd->stat_sn = conn->stat_sn++;
28668c2ecf20Sopenharmony_ci			set_statsn = true;
28678c2ecf20Sopenharmony_ci		} else if (dr->dr_complete ==
28688c2ecf20Sopenharmony_ci			   DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY)
28698c2ecf20Sopenharmony_ci			set_statsn = true;
28708c2ecf20Sopenharmony_ci	}
28718c2ecf20Sopenharmony_ci
28728c2ecf20Sopenharmony_ci	iscsit_build_datain_pdu(cmd, conn, &datain, hdr, set_statsn);
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, dr, &datain, 0);
28758c2ecf20Sopenharmony_ci	if (ret < 0)
28768c2ecf20Sopenharmony_ci		return ret;
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci	if (dr->dr_complete) {
28798c2ecf20Sopenharmony_ci		eodr = (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ?
28808c2ecf20Sopenharmony_ci				2 : 1;
28818c2ecf20Sopenharmony_ci		iscsit_free_datain_req(cmd, dr);
28828c2ecf20Sopenharmony_ci	}
28838c2ecf20Sopenharmony_ci
28848c2ecf20Sopenharmony_ci	return eodr;
28858c2ecf20Sopenharmony_ci}
28868c2ecf20Sopenharmony_ci
28878c2ecf20Sopenharmony_ciint
28888c2ecf20Sopenharmony_ciiscsit_build_logout_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
28898c2ecf20Sopenharmony_ci			struct iscsi_logout_rsp *hdr)
28908c2ecf20Sopenharmony_ci{
28918c2ecf20Sopenharmony_ci	struct iscsi_conn *logout_conn = NULL;
28928c2ecf20Sopenharmony_ci	struct iscsi_conn_recovery *cr = NULL;
28938c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
28948c2ecf20Sopenharmony_ci	/*
28958c2ecf20Sopenharmony_ci	 * The actual shutting down of Sessions and/or Connections
28968c2ecf20Sopenharmony_ci	 * for CLOSESESSION and CLOSECONNECTION Logout Requests
28978c2ecf20Sopenharmony_ci	 * is done in scsi_logout_post_handler().
28988c2ecf20Sopenharmony_ci	 */
28998c2ecf20Sopenharmony_ci	switch (cmd->logout_reason) {
29008c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
29018c2ecf20Sopenharmony_ci		pr_debug("iSCSI session logout successful, setting"
29028c2ecf20Sopenharmony_ci			" logout response to ISCSI_LOGOUT_SUCCESS.\n");
29038c2ecf20Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
29048c2ecf20Sopenharmony_ci		break;
29058c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
29068c2ecf20Sopenharmony_ci		if (cmd->logout_response == ISCSI_LOGOUT_CID_NOT_FOUND)
29078c2ecf20Sopenharmony_ci			break;
29088c2ecf20Sopenharmony_ci		/*
29098c2ecf20Sopenharmony_ci		 * For CLOSECONNECTION logout requests carrying
29108c2ecf20Sopenharmony_ci		 * a matching logout CID -> local CID, the reference
29118c2ecf20Sopenharmony_ci		 * for the local CID will have been incremented in
29128c2ecf20Sopenharmony_ci		 * iscsi_logout_closeconnection().
29138c2ecf20Sopenharmony_ci		 *
29148c2ecf20Sopenharmony_ci		 * For CLOSECONNECTION logout requests carrying
29158c2ecf20Sopenharmony_ci		 * a different CID than the connection it arrived
29168c2ecf20Sopenharmony_ci		 * on, the connection responding to cmd->logout_cid
29178c2ecf20Sopenharmony_ci		 * is stopped in iscsit_logout_post_handler_diffcid().
29188c2ecf20Sopenharmony_ci		 */
29198c2ecf20Sopenharmony_ci
29208c2ecf20Sopenharmony_ci		pr_debug("iSCSI CID: %hu logout on CID: %hu"
29218c2ecf20Sopenharmony_ci			" successful.\n", cmd->logout_cid, conn->cid);
29228c2ecf20Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
29238c2ecf20Sopenharmony_ci		break;
29248c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_RECOVERY:
29258c2ecf20Sopenharmony_ci		if ((cmd->logout_response == ISCSI_LOGOUT_RECOVERY_UNSUPPORTED) ||
29268c2ecf20Sopenharmony_ci		    (cmd->logout_response == ISCSI_LOGOUT_CLEANUP_FAILED))
29278c2ecf20Sopenharmony_ci			break;
29288c2ecf20Sopenharmony_ci		/*
29298c2ecf20Sopenharmony_ci		 * If the connection is still active from our point of view
29308c2ecf20Sopenharmony_ci		 * force connection recovery to occur.
29318c2ecf20Sopenharmony_ci		 */
29328c2ecf20Sopenharmony_ci		logout_conn = iscsit_get_conn_from_cid_rcfr(sess,
29338c2ecf20Sopenharmony_ci				cmd->logout_cid);
29348c2ecf20Sopenharmony_ci		if (logout_conn) {
29358c2ecf20Sopenharmony_ci			iscsit_connection_reinstatement_rcfr(logout_conn);
29368c2ecf20Sopenharmony_ci			iscsit_dec_conn_usage_count(logout_conn);
29378c2ecf20Sopenharmony_ci		}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci		cr = iscsit_get_inactive_connection_recovery_entry(
29408c2ecf20Sopenharmony_ci				conn->sess, cmd->logout_cid);
29418c2ecf20Sopenharmony_ci		if (!cr) {
29428c2ecf20Sopenharmony_ci			pr_err("Unable to locate CID: %hu for"
29438c2ecf20Sopenharmony_ci			" REMOVECONNFORRECOVERY Logout Request.\n",
29448c2ecf20Sopenharmony_ci				cmd->logout_cid);
29458c2ecf20Sopenharmony_ci			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
29468c2ecf20Sopenharmony_ci			break;
29478c2ecf20Sopenharmony_ci		}
29488c2ecf20Sopenharmony_ci
29498c2ecf20Sopenharmony_ci		iscsit_discard_cr_cmds_by_expstatsn(cr, cmd->exp_stat_sn);
29508c2ecf20Sopenharmony_ci
29518c2ecf20Sopenharmony_ci		pr_debug("iSCSI REMOVECONNFORRECOVERY logout"
29528c2ecf20Sopenharmony_ci			" for recovery for CID: %hu on CID: %hu successful.\n",
29538c2ecf20Sopenharmony_ci				cmd->logout_cid, conn->cid);
29548c2ecf20Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
29558c2ecf20Sopenharmony_ci		break;
29568c2ecf20Sopenharmony_ci	default:
29578c2ecf20Sopenharmony_ci		pr_err("Unknown cmd->logout_reason: 0x%02x\n",
29588c2ecf20Sopenharmony_ci				cmd->logout_reason);
29598c2ecf20Sopenharmony_ci		return -1;
29608c2ecf20Sopenharmony_ci	}
29618c2ecf20Sopenharmony_ci
29628c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_LOGOUT_RSP;
29638c2ecf20Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
29648c2ecf20Sopenharmony_ci	hdr->response		= cmd->logout_response;
29658c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
29668c2ecf20Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
29678c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
29688c2ecf20Sopenharmony_ci
29698c2ecf20Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
29708c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
29718c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
29728c2ecf20Sopenharmony_ci
29738c2ecf20Sopenharmony_ci	pr_debug("Built Logout Response ITT: 0x%08x StatSN:"
29748c2ecf20Sopenharmony_ci		" 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n",
29758c2ecf20Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, hdr->response,
29768c2ecf20Sopenharmony_ci		cmd->logout_cid, conn->cid);
29778c2ecf20Sopenharmony_ci
29788c2ecf20Sopenharmony_ci	return 0;
29798c2ecf20Sopenharmony_ci}
29808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_logout_rsp);
29818c2ecf20Sopenharmony_ci
29828c2ecf20Sopenharmony_cistatic int
29838c2ecf20Sopenharmony_ciiscsit_send_logout(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
29848c2ecf20Sopenharmony_ci{
29858c2ecf20Sopenharmony_ci	int rc;
29868c2ecf20Sopenharmony_ci
29878c2ecf20Sopenharmony_ci	rc = iscsit_build_logout_rsp(cmd, conn,
29888c2ecf20Sopenharmony_ci			(struct iscsi_logout_rsp *)&cmd->pdu[0]);
29898c2ecf20Sopenharmony_ci	if (rc < 0)
29908c2ecf20Sopenharmony_ci		return rc;
29918c2ecf20Sopenharmony_ci
29928c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
29938c2ecf20Sopenharmony_ci}
29948c2ecf20Sopenharmony_ci
29958c2ecf20Sopenharmony_civoid
29968c2ecf20Sopenharmony_ciiscsit_build_nopin_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
29978c2ecf20Sopenharmony_ci		       struct iscsi_nopin *hdr, bool nopout_response)
29988c2ecf20Sopenharmony_ci{
29998c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_NOOP_IN;
30008c2ecf20Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
30018c2ecf20Sopenharmony_ci        hton24(hdr->dlength, cmd->buf_ptr_size);
30028c2ecf20Sopenharmony_ci	if (nopout_response)
30038c2ecf20Sopenharmony_ci		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
30048c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
30058c2ecf20Sopenharmony_ci	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
30068c2ecf20Sopenharmony_ci	cmd->stat_sn		= (nopout_response) ? conn->stat_sn++ :
30078c2ecf20Sopenharmony_ci				  conn->stat_sn;
30088c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
30098c2ecf20Sopenharmony_ci
30108c2ecf20Sopenharmony_ci	if (nopout_response)
30118c2ecf20Sopenharmony_ci		iscsit_increment_maxcmdsn(cmd, conn->sess);
30128c2ecf20Sopenharmony_ci
30138c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
30148c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
30158c2ecf20Sopenharmony_ci
30168c2ecf20Sopenharmony_ci	pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x,"
30178c2ecf20Sopenharmony_ci		" StatSN: 0x%08x, Length %u\n", (nopout_response) ?
30188c2ecf20Sopenharmony_ci		"Solicited" : "Unsolicited", cmd->init_task_tag,
30198c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size);
30208c2ecf20Sopenharmony_ci}
30218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_nopin_rsp);
30228c2ecf20Sopenharmony_ci
30238c2ecf20Sopenharmony_ci/*
30248c2ecf20Sopenharmony_ci *	Unsolicited NOPIN, either requesting a response or not.
30258c2ecf20Sopenharmony_ci */
30268c2ecf20Sopenharmony_cistatic int iscsit_send_unsolicited_nopin(
30278c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
30288c2ecf20Sopenharmony_ci	struct iscsi_conn *conn,
30298c2ecf20Sopenharmony_ci	int want_response)
30308c2ecf20Sopenharmony_ci{
30318c2ecf20Sopenharmony_ci	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
30328c2ecf20Sopenharmony_ci	int ret;
30338c2ecf20Sopenharmony_ci
30348c2ecf20Sopenharmony_ci	iscsit_build_nopin_rsp(cmd, conn, hdr, false);
30358c2ecf20Sopenharmony_ci
30368c2ecf20Sopenharmony_ci	pr_debug("Sending Unsolicited NOPIN TTT: 0x%08x StatSN:"
30378c2ecf20Sopenharmony_ci		" 0x%08x CID: %hu\n", hdr->ttt, cmd->stat_sn, conn->cid);
30388c2ecf20Sopenharmony_ci
30398c2ecf20Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
30408c2ecf20Sopenharmony_ci	if (ret < 0)
30418c2ecf20Sopenharmony_ci		return ret;
30428c2ecf20Sopenharmony_ci
30438c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->istate_lock);
30448c2ecf20Sopenharmony_ci	cmd->i_state = want_response ?
30458c2ecf20Sopenharmony_ci		ISTATE_SENT_NOPIN_WANT_RESPONSE : ISTATE_SENT_STATUS;
30468c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->istate_lock);
30478c2ecf20Sopenharmony_ci
30488c2ecf20Sopenharmony_ci	return 0;
30498c2ecf20Sopenharmony_ci}
30508c2ecf20Sopenharmony_ci
30518c2ecf20Sopenharmony_cistatic int
30528c2ecf20Sopenharmony_ciiscsit_send_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
30538c2ecf20Sopenharmony_ci{
30548c2ecf20Sopenharmony_ci	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
30558c2ecf20Sopenharmony_ci
30568c2ecf20Sopenharmony_ci	iscsit_build_nopin_rsp(cmd, conn, hdr, true);
30578c2ecf20Sopenharmony_ci
30588c2ecf20Sopenharmony_ci	/*
30598c2ecf20Sopenharmony_ci	 * NOPOUT Ping Data is attached to struct iscsi_cmd->buf_ptr.
30608c2ecf20Sopenharmony_ci	 * NOPOUT DataSegmentLength is at struct iscsi_cmd->buf_ptr_size.
30618c2ecf20Sopenharmony_ci	 */
30628c2ecf20Sopenharmony_ci	pr_debug("Echoing back %u bytes of ping data.\n", cmd->buf_ptr_size);
30638c2ecf20Sopenharmony_ci
30648c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
30658c2ecf20Sopenharmony_ci						     cmd->buf_ptr,
30668c2ecf20Sopenharmony_ci						     cmd->buf_ptr_size);
30678c2ecf20Sopenharmony_ci}
30688c2ecf20Sopenharmony_ci
30698c2ecf20Sopenharmony_cistatic int iscsit_send_r2t(
30708c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
30718c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
30728c2ecf20Sopenharmony_ci{
30738c2ecf20Sopenharmony_ci	struct iscsi_r2t *r2t;
30748c2ecf20Sopenharmony_ci	struct iscsi_r2t_rsp *hdr;
30758c2ecf20Sopenharmony_ci	int ret;
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci	r2t = iscsit_get_r2t_from_list(cmd);
30788c2ecf20Sopenharmony_ci	if (!r2t)
30798c2ecf20Sopenharmony_ci		return -1;
30808c2ecf20Sopenharmony_ci
30818c2ecf20Sopenharmony_ci	hdr			= (struct iscsi_r2t_rsp *) cmd->pdu;
30828c2ecf20Sopenharmony_ci	memset(hdr, 0, ISCSI_HDR_LEN);
30838c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_R2T;
30848c2ecf20Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
30858c2ecf20Sopenharmony_ci	int_to_scsilun(cmd->se_cmd.orig_fe_lun,
30868c2ecf20Sopenharmony_ci			(struct scsi_lun *)&hdr->lun);
30878c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
30888c2ecf20Sopenharmony_ci	if (conn->conn_transport->iscsit_get_r2t_ttt)
30898c2ecf20Sopenharmony_ci		conn->conn_transport->iscsit_get_r2t_ttt(conn, cmd, r2t);
30908c2ecf20Sopenharmony_ci	else
30918c2ecf20Sopenharmony_ci		r2t->targ_xfer_tag = session_get_next_ttt(conn->sess);
30928c2ecf20Sopenharmony_ci	hdr->ttt		= cpu_to_be32(r2t->targ_xfer_tag);
30938c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(conn->stat_sn);
30948c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
30958c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
30968c2ecf20Sopenharmony_ci	hdr->r2tsn		= cpu_to_be32(r2t->r2t_sn);
30978c2ecf20Sopenharmony_ci	hdr->data_offset	= cpu_to_be32(r2t->offset);
30988c2ecf20Sopenharmony_ci	hdr->data_length	= cpu_to_be32(r2t->xfer_len);
30998c2ecf20Sopenharmony_ci
31008c2ecf20Sopenharmony_ci	pr_debug("Built %sR2T, ITT: 0x%08x, TTT: 0x%08x, StatSN:"
31018c2ecf20Sopenharmony_ci		" 0x%08x, R2TSN: 0x%08x, Offset: %u, DDTL: %u, CID: %hu\n",
31028c2ecf20Sopenharmony_ci		(!r2t->recovery_r2t) ? "" : "Recovery ", cmd->init_task_tag,
31038c2ecf20Sopenharmony_ci		r2t->targ_xfer_tag, ntohl(hdr->statsn), r2t->r2t_sn,
31048c2ecf20Sopenharmony_ci			r2t->offset, r2t->xfer_len, conn->cid);
31058c2ecf20Sopenharmony_ci
31068c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
31078c2ecf20Sopenharmony_ci	r2t->sent_r2t = 1;
31088c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
31098c2ecf20Sopenharmony_ci
31108c2ecf20Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
31118c2ecf20Sopenharmony_ci	if (ret < 0) {
31128c2ecf20Sopenharmony_ci		return ret;
31138c2ecf20Sopenharmony_ci	}
31148c2ecf20Sopenharmony_ci
31158c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->dataout_timeout_lock);
31168c2ecf20Sopenharmony_ci	iscsit_start_dataout_timer(cmd, conn);
31178c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->dataout_timeout_lock);
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci	return 0;
31208c2ecf20Sopenharmony_ci}
31218c2ecf20Sopenharmony_ci
31228c2ecf20Sopenharmony_ci/*
31238c2ecf20Sopenharmony_ci *	@recovery: If called from iscsi_task_reassign_complete_write() for
31248c2ecf20Sopenharmony_ci *		connection recovery.
31258c2ecf20Sopenharmony_ci */
31268c2ecf20Sopenharmony_ciint iscsit_build_r2ts_for_cmd(
31278c2ecf20Sopenharmony_ci	struct iscsi_conn *conn,
31288c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
31298c2ecf20Sopenharmony_ci	bool recovery)
31308c2ecf20Sopenharmony_ci{
31318c2ecf20Sopenharmony_ci	int first_r2t = 1;
31328c2ecf20Sopenharmony_ci	u32 offset = 0, xfer_len = 0;
31338c2ecf20Sopenharmony_ci
31348c2ecf20Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
31358c2ecf20Sopenharmony_ci	if (cmd->cmd_flags & ICF_SENT_LAST_R2T) {
31368c2ecf20Sopenharmony_ci		spin_unlock_bh(&cmd->r2t_lock);
31378c2ecf20Sopenharmony_ci		return 0;
31388c2ecf20Sopenharmony_ci	}
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder &&
31418c2ecf20Sopenharmony_ci	    !recovery)
31428c2ecf20Sopenharmony_ci		cmd->r2t_offset = max(cmd->r2t_offset, cmd->write_data_done);
31438c2ecf20Sopenharmony_ci
31448c2ecf20Sopenharmony_ci	while (cmd->outstanding_r2ts < conn->sess->sess_ops->MaxOutstandingR2T) {
31458c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
31468c2ecf20Sopenharmony_ci			offset = cmd->r2t_offset;
31478c2ecf20Sopenharmony_ci
31488c2ecf20Sopenharmony_ci			if (first_r2t && recovery) {
31498c2ecf20Sopenharmony_ci				int new_data_end = offset +
31508c2ecf20Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength -
31518c2ecf20Sopenharmony_ci					cmd->next_burst_len;
31528c2ecf20Sopenharmony_ci
31538c2ecf20Sopenharmony_ci				if (new_data_end > cmd->se_cmd.data_length)
31548c2ecf20Sopenharmony_ci					xfer_len = cmd->se_cmd.data_length - offset;
31558c2ecf20Sopenharmony_ci				else
31568c2ecf20Sopenharmony_ci					xfer_len =
31578c2ecf20Sopenharmony_ci						conn->sess->sess_ops->MaxBurstLength -
31588c2ecf20Sopenharmony_ci						cmd->next_burst_len;
31598c2ecf20Sopenharmony_ci			} else {
31608c2ecf20Sopenharmony_ci				int new_data_end = offset +
31618c2ecf20Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength;
31628c2ecf20Sopenharmony_ci
31638c2ecf20Sopenharmony_ci				if (new_data_end > cmd->se_cmd.data_length)
31648c2ecf20Sopenharmony_ci					xfer_len = cmd->se_cmd.data_length - offset;
31658c2ecf20Sopenharmony_ci				else
31668c2ecf20Sopenharmony_ci					xfer_len = conn->sess->sess_ops->MaxBurstLength;
31678c2ecf20Sopenharmony_ci			}
31688c2ecf20Sopenharmony_ci
31698c2ecf20Sopenharmony_ci			if ((s32)xfer_len < 0) {
31708c2ecf20Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
31718c2ecf20Sopenharmony_ci				break;
31728c2ecf20Sopenharmony_ci			}
31738c2ecf20Sopenharmony_ci
31748c2ecf20Sopenharmony_ci			cmd->r2t_offset += xfer_len;
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_ci			if (cmd->r2t_offset == cmd->se_cmd.data_length)
31778c2ecf20Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
31788c2ecf20Sopenharmony_ci		} else {
31798c2ecf20Sopenharmony_ci			struct iscsi_seq *seq;
31808c2ecf20Sopenharmony_ci
31818c2ecf20Sopenharmony_ci			seq = iscsit_get_seq_holder_for_r2t(cmd);
31828c2ecf20Sopenharmony_ci			if (!seq) {
31838c2ecf20Sopenharmony_ci				spin_unlock_bh(&cmd->r2t_lock);
31848c2ecf20Sopenharmony_ci				return -1;
31858c2ecf20Sopenharmony_ci			}
31868c2ecf20Sopenharmony_ci
31878c2ecf20Sopenharmony_ci			offset = seq->offset;
31888c2ecf20Sopenharmony_ci			xfer_len = seq->xfer_len;
31898c2ecf20Sopenharmony_ci
31908c2ecf20Sopenharmony_ci			if (cmd->seq_send_order == cmd->seq_count)
31918c2ecf20Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
31928c2ecf20Sopenharmony_ci		}
31938c2ecf20Sopenharmony_ci		cmd->outstanding_r2ts++;
31948c2ecf20Sopenharmony_ci		first_r2t = 0;
31958c2ecf20Sopenharmony_ci
31968c2ecf20Sopenharmony_ci		if (iscsit_add_r2t_to_list(cmd, offset, xfer_len, 0, 0) < 0) {
31978c2ecf20Sopenharmony_ci			spin_unlock_bh(&cmd->r2t_lock);
31988c2ecf20Sopenharmony_ci			return -1;
31998c2ecf20Sopenharmony_ci		}
32008c2ecf20Sopenharmony_ci
32018c2ecf20Sopenharmony_ci		if (cmd->cmd_flags & ICF_SENT_LAST_R2T)
32028c2ecf20Sopenharmony_ci			break;
32038c2ecf20Sopenharmony_ci	}
32048c2ecf20Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
32058c2ecf20Sopenharmony_ci
32068c2ecf20Sopenharmony_ci	return 0;
32078c2ecf20Sopenharmony_ci}
32088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_r2ts_for_cmd);
32098c2ecf20Sopenharmony_ci
32108c2ecf20Sopenharmony_civoid iscsit_build_rsp_pdu(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
32118c2ecf20Sopenharmony_ci			bool inc_stat_sn, struct iscsi_scsi_rsp *hdr)
32128c2ecf20Sopenharmony_ci{
32138c2ecf20Sopenharmony_ci	if (inc_stat_sn)
32148c2ecf20Sopenharmony_ci		cmd->stat_sn = conn->stat_sn++;
32158c2ecf20Sopenharmony_ci
32168c2ecf20Sopenharmony_ci	atomic_long_inc(&conn->sess->rsp_pdus);
32178c2ecf20Sopenharmony_ci
32188c2ecf20Sopenharmony_ci	memset(hdr, 0, ISCSI_HDR_LEN);
32198c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_CMD_RSP;
32208c2ecf20Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
32218c2ecf20Sopenharmony_ci	if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
32228c2ecf20Sopenharmony_ci		hdr->flags |= ISCSI_FLAG_CMD_OVERFLOW;
32238c2ecf20Sopenharmony_ci		hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
32248c2ecf20Sopenharmony_ci	} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
32258c2ecf20Sopenharmony_ci		hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
32268c2ecf20Sopenharmony_ci		hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
32278c2ecf20Sopenharmony_ci	}
32288c2ecf20Sopenharmony_ci	hdr->response		= cmd->iscsi_response;
32298c2ecf20Sopenharmony_ci	hdr->cmd_status		= cmd->se_cmd.scsi_status;
32308c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
32318c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
32348c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
32358c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
32368c2ecf20Sopenharmony_ci
32378c2ecf20Sopenharmony_ci	pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x,"
32388c2ecf20Sopenharmony_ci		" Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n",
32398c2ecf20Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, cmd->se_cmd.scsi_status,
32408c2ecf20Sopenharmony_ci		cmd->se_cmd.scsi_status, conn->cid);
32418c2ecf20Sopenharmony_ci}
32428c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_rsp_pdu);
32438c2ecf20Sopenharmony_ci
32448c2ecf20Sopenharmony_cistatic int iscsit_send_response(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
32458c2ecf20Sopenharmony_ci{
32468c2ecf20Sopenharmony_ci	struct iscsi_scsi_rsp *hdr = (struct iscsi_scsi_rsp *)&cmd->pdu[0];
32478c2ecf20Sopenharmony_ci	bool inc_stat_sn = (cmd->i_state == ISTATE_SEND_STATUS);
32488c2ecf20Sopenharmony_ci	void *data_buf = NULL;
32498c2ecf20Sopenharmony_ci	u32 padding = 0, data_buf_len = 0;
32508c2ecf20Sopenharmony_ci
32518c2ecf20Sopenharmony_ci	iscsit_build_rsp_pdu(cmd, conn, inc_stat_sn, hdr);
32528c2ecf20Sopenharmony_ci
32538c2ecf20Sopenharmony_ci	/*
32548c2ecf20Sopenharmony_ci	 * Attach SENSE DATA payload to iSCSI Response PDU
32558c2ecf20Sopenharmony_ci	 */
32568c2ecf20Sopenharmony_ci	if (cmd->se_cmd.sense_buffer &&
32578c2ecf20Sopenharmony_ci	   ((cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
32588c2ecf20Sopenharmony_ci	    (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
32598c2ecf20Sopenharmony_ci		put_unaligned_be16(cmd->se_cmd.scsi_sense_length, cmd->sense_buffer);
32608c2ecf20Sopenharmony_ci		cmd->se_cmd.scsi_sense_length += sizeof (__be16);
32618c2ecf20Sopenharmony_ci
32628c2ecf20Sopenharmony_ci		padding		= -(cmd->se_cmd.scsi_sense_length) & 3;
32638c2ecf20Sopenharmony_ci		hton24(hdr->dlength, (u32)cmd->se_cmd.scsi_sense_length);
32648c2ecf20Sopenharmony_ci		data_buf = cmd->sense_buffer;
32658c2ecf20Sopenharmony_ci		data_buf_len = cmd->se_cmd.scsi_sense_length + padding;
32668c2ecf20Sopenharmony_ci
32678c2ecf20Sopenharmony_ci		if (padding) {
32688c2ecf20Sopenharmony_ci			memset(cmd->sense_buffer +
32698c2ecf20Sopenharmony_ci				cmd->se_cmd.scsi_sense_length, 0, padding);
32708c2ecf20Sopenharmony_ci			pr_debug("Adding %u bytes of padding to"
32718c2ecf20Sopenharmony_ci				" SENSE.\n", padding);
32728c2ecf20Sopenharmony_ci		}
32738c2ecf20Sopenharmony_ci
32748c2ecf20Sopenharmony_ci		pr_debug("Attaching SENSE DATA: %u bytes to iSCSI"
32758c2ecf20Sopenharmony_ci				" Response PDU\n",
32768c2ecf20Sopenharmony_ci				cmd->se_cmd.scsi_sense_length);
32778c2ecf20Sopenharmony_ci	}
32788c2ecf20Sopenharmony_ci
32798c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, data_buf,
32808c2ecf20Sopenharmony_ci						     data_buf_len);
32818c2ecf20Sopenharmony_ci}
32828c2ecf20Sopenharmony_ci
32838c2ecf20Sopenharmony_cistatic u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr)
32848c2ecf20Sopenharmony_ci{
32858c2ecf20Sopenharmony_ci	switch (se_tmr->response) {
32868c2ecf20Sopenharmony_ci	case TMR_FUNCTION_COMPLETE:
32878c2ecf20Sopenharmony_ci		return ISCSI_TMF_RSP_COMPLETE;
32888c2ecf20Sopenharmony_ci	case TMR_TASK_DOES_NOT_EXIST:
32898c2ecf20Sopenharmony_ci		return ISCSI_TMF_RSP_NO_TASK;
32908c2ecf20Sopenharmony_ci	case TMR_LUN_DOES_NOT_EXIST:
32918c2ecf20Sopenharmony_ci		return ISCSI_TMF_RSP_NO_LUN;
32928c2ecf20Sopenharmony_ci	case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
32938c2ecf20Sopenharmony_ci		return ISCSI_TMF_RSP_NOT_SUPPORTED;
32948c2ecf20Sopenharmony_ci	case TMR_FUNCTION_REJECTED:
32958c2ecf20Sopenharmony_ci	default:
32968c2ecf20Sopenharmony_ci		return ISCSI_TMF_RSP_REJECTED;
32978c2ecf20Sopenharmony_ci	}
32988c2ecf20Sopenharmony_ci}
32998c2ecf20Sopenharmony_ci
33008c2ecf20Sopenharmony_civoid
33018c2ecf20Sopenharmony_ciiscsit_build_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
33028c2ecf20Sopenharmony_ci			  struct iscsi_tm_rsp *hdr)
33038c2ecf20Sopenharmony_ci{
33048c2ecf20Sopenharmony_ci	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
33058c2ecf20Sopenharmony_ci
33068c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_TMFUNC_RSP;
33078c2ecf20Sopenharmony_ci	hdr->flags		= ISCSI_FLAG_CMD_FINAL;
33088c2ecf20Sopenharmony_ci	hdr->response		= iscsit_convert_tcm_tmr_rsp(se_tmr);
33098c2ecf20Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
33108c2ecf20Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
33118c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
33128c2ecf20Sopenharmony_ci
33138c2ecf20Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
33148c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
33158c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
33168c2ecf20Sopenharmony_ci
33178c2ecf20Sopenharmony_ci	pr_debug("Built Task Management Response ITT: 0x%08x,"
33188c2ecf20Sopenharmony_ci		" StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n",
33198c2ecf20Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid);
33208c2ecf20Sopenharmony_ci}
33218c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_task_mgt_rsp);
33228c2ecf20Sopenharmony_ci
33238c2ecf20Sopenharmony_cistatic int
33248c2ecf20Sopenharmony_ciiscsit_send_task_mgt_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn)
33258c2ecf20Sopenharmony_ci{
33268c2ecf20Sopenharmony_ci	struct iscsi_tm_rsp *hdr = (struct iscsi_tm_rsp *)&cmd->pdu[0];
33278c2ecf20Sopenharmony_ci
33288c2ecf20Sopenharmony_ci	iscsit_build_task_mgt_rsp(cmd, conn, hdr);
33298c2ecf20Sopenharmony_ci
33308c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
33318c2ecf20Sopenharmony_ci}
33328c2ecf20Sopenharmony_ci
33338c2ecf20Sopenharmony_ci#define SENDTARGETS_BUF_LIMIT 32768U
33348c2ecf20Sopenharmony_ci
33358c2ecf20Sopenharmony_cistatic int
33368c2ecf20Sopenharmony_ciiscsit_build_sendtargets_response(struct iscsi_cmd *cmd,
33378c2ecf20Sopenharmony_ci				  enum iscsit_transport_type network_transport,
33388c2ecf20Sopenharmony_ci				  int skip_bytes, bool *completed)
33398c2ecf20Sopenharmony_ci{
33408c2ecf20Sopenharmony_ci	char *payload = NULL;
33418c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = cmd->conn;
33428c2ecf20Sopenharmony_ci	struct iscsi_portal_group *tpg;
33438c2ecf20Sopenharmony_ci	struct iscsi_tiqn *tiqn;
33448c2ecf20Sopenharmony_ci	struct iscsi_tpg_np *tpg_np;
33458c2ecf20Sopenharmony_ci	int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
33468c2ecf20Sopenharmony_ci	int target_name_printed;
33478c2ecf20Sopenharmony_ci	unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
33488c2ecf20Sopenharmony_ci	unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
33498c2ecf20Sopenharmony_ci	bool active;
33508c2ecf20Sopenharmony_ci
33518c2ecf20Sopenharmony_ci	buffer_len = min(conn->conn_ops->MaxRecvDataSegmentLength,
33528c2ecf20Sopenharmony_ci			 SENDTARGETS_BUF_LIMIT);
33538c2ecf20Sopenharmony_ci
33548c2ecf20Sopenharmony_ci	payload = kzalloc(buffer_len, GFP_KERNEL);
33558c2ecf20Sopenharmony_ci	if (!payload)
33568c2ecf20Sopenharmony_ci		return -ENOMEM;
33578c2ecf20Sopenharmony_ci
33588c2ecf20Sopenharmony_ci	/*
33598c2ecf20Sopenharmony_ci	 * Locate pointer to iqn./eui. string for ICF_SENDTARGETS_SINGLE
33608c2ecf20Sopenharmony_ci	 * explicit case..
33618c2ecf20Sopenharmony_ci	 */
33628c2ecf20Sopenharmony_ci	if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) {
33638c2ecf20Sopenharmony_ci		text_ptr = strchr(text_in, '=');
33648c2ecf20Sopenharmony_ci		if (!text_ptr) {
33658c2ecf20Sopenharmony_ci			pr_err("Unable to locate '=' string in text_in:"
33668c2ecf20Sopenharmony_ci			       " %s\n", text_in);
33678c2ecf20Sopenharmony_ci			kfree(payload);
33688c2ecf20Sopenharmony_ci			return -EINVAL;
33698c2ecf20Sopenharmony_ci		}
33708c2ecf20Sopenharmony_ci		/*
33718c2ecf20Sopenharmony_ci		 * Skip over '=' character..
33728c2ecf20Sopenharmony_ci		 */
33738c2ecf20Sopenharmony_ci		text_ptr += 1;
33748c2ecf20Sopenharmony_ci	}
33758c2ecf20Sopenharmony_ci
33768c2ecf20Sopenharmony_ci	spin_lock(&tiqn_lock);
33778c2ecf20Sopenharmony_ci	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
33788c2ecf20Sopenharmony_ci		if ((cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) &&
33798c2ecf20Sopenharmony_ci		     strcmp(tiqn->tiqn, text_ptr)) {
33808c2ecf20Sopenharmony_ci			continue;
33818c2ecf20Sopenharmony_ci		}
33828c2ecf20Sopenharmony_ci
33838c2ecf20Sopenharmony_ci		target_name_printed = 0;
33848c2ecf20Sopenharmony_ci
33858c2ecf20Sopenharmony_ci		spin_lock(&tiqn->tiqn_tpg_lock);
33868c2ecf20Sopenharmony_ci		list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
33878c2ecf20Sopenharmony_ci
33888c2ecf20Sopenharmony_ci			/* If demo_mode_discovery=0 and generate_node_acls=0
33898c2ecf20Sopenharmony_ci			 * (demo mode dislabed) do not return
33908c2ecf20Sopenharmony_ci			 * TargetName+TargetAddress unless a NodeACL exists.
33918c2ecf20Sopenharmony_ci			 */
33928c2ecf20Sopenharmony_ci
33938c2ecf20Sopenharmony_ci			if ((tpg->tpg_attrib.generate_node_acls == 0) &&
33948c2ecf20Sopenharmony_ci			    (tpg->tpg_attrib.demo_mode_discovery == 0) &&
33958c2ecf20Sopenharmony_ci			    (!target_tpg_has_node_acl(&tpg->tpg_se_tpg,
33968c2ecf20Sopenharmony_ci				cmd->conn->sess->sess_ops->InitiatorName))) {
33978c2ecf20Sopenharmony_ci				continue;
33988c2ecf20Sopenharmony_ci			}
33998c2ecf20Sopenharmony_ci
34008c2ecf20Sopenharmony_ci			spin_lock(&tpg->tpg_state_lock);
34018c2ecf20Sopenharmony_ci			active = (tpg->tpg_state == TPG_STATE_ACTIVE);
34028c2ecf20Sopenharmony_ci			spin_unlock(&tpg->tpg_state_lock);
34038c2ecf20Sopenharmony_ci
34048c2ecf20Sopenharmony_ci			if (!active && tpg->tpg_attrib.tpg_enabled_sendtargets)
34058c2ecf20Sopenharmony_ci				continue;
34068c2ecf20Sopenharmony_ci
34078c2ecf20Sopenharmony_ci			spin_lock(&tpg->tpg_np_lock);
34088c2ecf20Sopenharmony_ci			list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
34098c2ecf20Sopenharmony_ci						tpg_np_list) {
34108c2ecf20Sopenharmony_ci				struct iscsi_np *np = tpg_np->tpg_np;
34118c2ecf20Sopenharmony_ci				struct sockaddr_storage *sockaddr;
34128c2ecf20Sopenharmony_ci
34138c2ecf20Sopenharmony_ci				if (np->np_network_transport != network_transport)
34148c2ecf20Sopenharmony_ci					continue;
34158c2ecf20Sopenharmony_ci
34168c2ecf20Sopenharmony_ci				if (!target_name_printed) {
34178c2ecf20Sopenharmony_ci					len = sprintf(buf, "TargetName=%s",
34188c2ecf20Sopenharmony_ci						      tiqn->tiqn);
34198c2ecf20Sopenharmony_ci					len += 1;
34208c2ecf20Sopenharmony_ci
34218c2ecf20Sopenharmony_ci					if ((len + payload_len) > buffer_len) {
34228c2ecf20Sopenharmony_ci						spin_unlock(&tpg->tpg_np_lock);
34238c2ecf20Sopenharmony_ci						spin_unlock(&tiqn->tiqn_tpg_lock);
34248c2ecf20Sopenharmony_ci						end_of_buf = 1;
34258c2ecf20Sopenharmony_ci						goto eob;
34268c2ecf20Sopenharmony_ci					}
34278c2ecf20Sopenharmony_ci
34288c2ecf20Sopenharmony_ci					if (skip_bytes && len <= skip_bytes) {
34298c2ecf20Sopenharmony_ci						skip_bytes -= len;
34308c2ecf20Sopenharmony_ci					} else {
34318c2ecf20Sopenharmony_ci						memcpy(payload + payload_len, buf, len);
34328c2ecf20Sopenharmony_ci						payload_len += len;
34338c2ecf20Sopenharmony_ci						target_name_printed = 1;
34348c2ecf20Sopenharmony_ci						if (len > skip_bytes)
34358c2ecf20Sopenharmony_ci							skip_bytes = 0;
34368c2ecf20Sopenharmony_ci					}
34378c2ecf20Sopenharmony_ci				}
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci				if (inet_addr_is_any((struct sockaddr *)&np->np_sockaddr))
34408c2ecf20Sopenharmony_ci					sockaddr = &conn->local_sockaddr;
34418c2ecf20Sopenharmony_ci				else
34428c2ecf20Sopenharmony_ci					sockaddr = &np->np_sockaddr;
34438c2ecf20Sopenharmony_ci
34448c2ecf20Sopenharmony_ci				len = sprintf(buf, "TargetAddress="
34458c2ecf20Sopenharmony_ci					      "%pISpc,%hu",
34468c2ecf20Sopenharmony_ci					      sockaddr,
34478c2ecf20Sopenharmony_ci					      tpg->tpgt);
34488c2ecf20Sopenharmony_ci				len += 1;
34498c2ecf20Sopenharmony_ci
34508c2ecf20Sopenharmony_ci				if ((len + payload_len) > buffer_len) {
34518c2ecf20Sopenharmony_ci					spin_unlock(&tpg->tpg_np_lock);
34528c2ecf20Sopenharmony_ci					spin_unlock(&tiqn->tiqn_tpg_lock);
34538c2ecf20Sopenharmony_ci					end_of_buf = 1;
34548c2ecf20Sopenharmony_ci					goto eob;
34558c2ecf20Sopenharmony_ci				}
34568c2ecf20Sopenharmony_ci
34578c2ecf20Sopenharmony_ci				if (skip_bytes && len <= skip_bytes) {
34588c2ecf20Sopenharmony_ci					skip_bytes -= len;
34598c2ecf20Sopenharmony_ci				} else {
34608c2ecf20Sopenharmony_ci					memcpy(payload + payload_len, buf, len);
34618c2ecf20Sopenharmony_ci					payload_len += len;
34628c2ecf20Sopenharmony_ci					if (len > skip_bytes)
34638c2ecf20Sopenharmony_ci						skip_bytes = 0;
34648c2ecf20Sopenharmony_ci				}
34658c2ecf20Sopenharmony_ci			}
34668c2ecf20Sopenharmony_ci			spin_unlock(&tpg->tpg_np_lock);
34678c2ecf20Sopenharmony_ci		}
34688c2ecf20Sopenharmony_ci		spin_unlock(&tiqn->tiqn_tpg_lock);
34698c2ecf20Sopenharmony_cieob:
34708c2ecf20Sopenharmony_ci		if (end_of_buf) {
34718c2ecf20Sopenharmony_ci			*completed = false;
34728c2ecf20Sopenharmony_ci			break;
34738c2ecf20Sopenharmony_ci		}
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci		if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE)
34768c2ecf20Sopenharmony_ci			break;
34778c2ecf20Sopenharmony_ci	}
34788c2ecf20Sopenharmony_ci	spin_unlock(&tiqn_lock);
34798c2ecf20Sopenharmony_ci
34808c2ecf20Sopenharmony_ci	cmd->buf_ptr = payload;
34818c2ecf20Sopenharmony_ci
34828c2ecf20Sopenharmony_ci	return payload_len;
34838c2ecf20Sopenharmony_ci}
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ciint
34868c2ecf20Sopenharmony_ciiscsit_build_text_rsp(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
34878c2ecf20Sopenharmony_ci		      struct iscsi_text_rsp *hdr,
34888c2ecf20Sopenharmony_ci		      enum iscsit_transport_type network_transport)
34898c2ecf20Sopenharmony_ci{
34908c2ecf20Sopenharmony_ci	int text_length, padding;
34918c2ecf20Sopenharmony_ci	bool completed = true;
34928c2ecf20Sopenharmony_ci
34938c2ecf20Sopenharmony_ci	text_length = iscsit_build_sendtargets_response(cmd, network_transport,
34948c2ecf20Sopenharmony_ci							cmd->read_data_done,
34958c2ecf20Sopenharmony_ci							&completed);
34968c2ecf20Sopenharmony_ci	if (text_length < 0)
34978c2ecf20Sopenharmony_ci		return text_length;
34988c2ecf20Sopenharmony_ci
34998c2ecf20Sopenharmony_ci	if (completed) {
35008c2ecf20Sopenharmony_ci		hdr->flags = ISCSI_FLAG_CMD_FINAL;
35018c2ecf20Sopenharmony_ci	} else {
35028c2ecf20Sopenharmony_ci		hdr->flags = ISCSI_FLAG_TEXT_CONTINUE;
35038c2ecf20Sopenharmony_ci		cmd->read_data_done += text_length;
35048c2ecf20Sopenharmony_ci		if (cmd->targ_xfer_tag == 0xFFFFFFFF)
35058c2ecf20Sopenharmony_ci			cmd->targ_xfer_tag = session_get_next_ttt(conn->sess);
35068c2ecf20Sopenharmony_ci	}
35078c2ecf20Sopenharmony_ci	hdr->opcode = ISCSI_OP_TEXT_RSP;
35088c2ecf20Sopenharmony_ci	padding = ((-text_length) & 3);
35098c2ecf20Sopenharmony_ci	hton24(hdr->dlength, text_length);
35108c2ecf20Sopenharmony_ci	hdr->itt = cmd->init_task_tag;
35118c2ecf20Sopenharmony_ci	hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
35128c2ecf20Sopenharmony_ci	cmd->stat_sn = conn->stat_sn++;
35138c2ecf20Sopenharmony_ci	hdr->statsn = cpu_to_be32(cmd->stat_sn);
35148c2ecf20Sopenharmony_ci
35158c2ecf20Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
35168c2ecf20Sopenharmony_ci	/*
35178c2ecf20Sopenharmony_ci	 * Reset maxcmdsn_inc in multi-part text payload exchanges to
35188c2ecf20Sopenharmony_ci	 * correctly increment MaxCmdSN for each response answering a
35198c2ecf20Sopenharmony_ci	 * non immediate text request with a valid CmdSN.
35208c2ecf20Sopenharmony_ci	 */
35218c2ecf20Sopenharmony_ci	cmd->maxcmdsn_inc = 0;
35228c2ecf20Sopenharmony_ci	hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
35238c2ecf20Sopenharmony_ci	hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
35248c2ecf20Sopenharmony_ci
35258c2ecf20Sopenharmony_ci	pr_debug("Built Text Response: ITT: 0x%08x, TTT: 0x%08x, StatSN: 0x%08x,"
35268c2ecf20Sopenharmony_ci		" Length: %u, CID: %hu F: %d C: %d\n", cmd->init_task_tag,
35278c2ecf20Sopenharmony_ci		cmd->targ_xfer_tag, cmd->stat_sn, text_length, conn->cid,
35288c2ecf20Sopenharmony_ci		!!(hdr->flags & ISCSI_FLAG_CMD_FINAL),
35298c2ecf20Sopenharmony_ci		!!(hdr->flags & ISCSI_FLAG_TEXT_CONTINUE));
35308c2ecf20Sopenharmony_ci
35318c2ecf20Sopenharmony_ci	return text_length + padding;
35328c2ecf20Sopenharmony_ci}
35338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_text_rsp);
35348c2ecf20Sopenharmony_ci
35358c2ecf20Sopenharmony_cistatic int iscsit_send_text_rsp(
35368c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
35378c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
35388c2ecf20Sopenharmony_ci{
35398c2ecf20Sopenharmony_ci	struct iscsi_text_rsp *hdr = (struct iscsi_text_rsp *)cmd->pdu;
35408c2ecf20Sopenharmony_ci	int text_length;
35418c2ecf20Sopenharmony_ci
35428c2ecf20Sopenharmony_ci	text_length = iscsit_build_text_rsp(cmd, conn, hdr,
35438c2ecf20Sopenharmony_ci				conn->conn_transport->transport_type);
35448c2ecf20Sopenharmony_ci	if (text_length < 0)
35458c2ecf20Sopenharmony_ci		return text_length;
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
35488c2ecf20Sopenharmony_ci						     cmd->buf_ptr,
35498c2ecf20Sopenharmony_ci						     text_length);
35508c2ecf20Sopenharmony_ci}
35518c2ecf20Sopenharmony_ci
35528c2ecf20Sopenharmony_civoid
35538c2ecf20Sopenharmony_ciiscsit_build_reject(struct iscsi_cmd *cmd, struct iscsi_conn *conn,
35548c2ecf20Sopenharmony_ci		    struct iscsi_reject *hdr)
35558c2ecf20Sopenharmony_ci{
35568c2ecf20Sopenharmony_ci	hdr->opcode		= ISCSI_OP_REJECT;
35578c2ecf20Sopenharmony_ci	hdr->reason		= cmd->reject_reason;
35588c2ecf20Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
35598c2ecf20Sopenharmony_ci	hton24(hdr->dlength, ISCSI_HDR_LEN);
35608c2ecf20Sopenharmony_ci	hdr->ffffffff		= cpu_to_be32(0xffffffff);
35618c2ecf20Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
35628c2ecf20Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
35638c2ecf20Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
35648c2ecf20Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
35658c2ecf20Sopenharmony_ci
35668c2ecf20Sopenharmony_ci}
35678c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_reject);
35688c2ecf20Sopenharmony_ci
35698c2ecf20Sopenharmony_cistatic int iscsit_send_reject(
35708c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
35718c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
35728c2ecf20Sopenharmony_ci{
35738c2ecf20Sopenharmony_ci	struct iscsi_reject *hdr = (struct iscsi_reject *)&cmd->pdu[0];
35748c2ecf20Sopenharmony_ci
35758c2ecf20Sopenharmony_ci	iscsit_build_reject(cmd, conn, hdr);
35768c2ecf20Sopenharmony_ci
35778c2ecf20Sopenharmony_ci	pr_debug("Built Reject PDU StatSN: 0x%08x, Reason: 0x%02x,"
35788c2ecf20Sopenharmony_ci		" CID: %hu\n", ntohl(hdr->statsn), hdr->reason, conn->cid);
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
35818c2ecf20Sopenharmony_ci						     cmd->buf_ptr,
35828c2ecf20Sopenharmony_ci						     ISCSI_HDR_LEN);
35838c2ecf20Sopenharmony_ci}
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_civoid iscsit_thread_get_cpumask(struct iscsi_conn *conn)
35868c2ecf20Sopenharmony_ci{
35878c2ecf20Sopenharmony_ci	int ord, cpu;
35888c2ecf20Sopenharmony_ci	/*
35898c2ecf20Sopenharmony_ci	 * bitmap_id is assigned from iscsit_global->ts_bitmap from
35908c2ecf20Sopenharmony_ci	 * within iscsit_start_kthreads()
35918c2ecf20Sopenharmony_ci	 *
35928c2ecf20Sopenharmony_ci	 * Here we use bitmap_id to determine which CPU that this
35938c2ecf20Sopenharmony_ci	 * iSCSI connection's RX/TX threads will be scheduled to
35948c2ecf20Sopenharmony_ci	 * execute upon.
35958c2ecf20Sopenharmony_ci	 */
35968c2ecf20Sopenharmony_ci	ord = conn->bitmap_id % cpumask_weight(cpu_online_mask);
35978c2ecf20Sopenharmony_ci	for_each_online_cpu(cpu) {
35988c2ecf20Sopenharmony_ci		if (ord-- == 0) {
35998c2ecf20Sopenharmony_ci			cpumask_set_cpu(cpu, conn->conn_cpumask);
36008c2ecf20Sopenharmony_ci			return;
36018c2ecf20Sopenharmony_ci		}
36028c2ecf20Sopenharmony_ci	}
36038c2ecf20Sopenharmony_ci	/*
36048c2ecf20Sopenharmony_ci	 * This should never be reached..
36058c2ecf20Sopenharmony_ci	 */
36068c2ecf20Sopenharmony_ci	dump_stack();
36078c2ecf20Sopenharmony_ci	cpumask_setall(conn->conn_cpumask);
36088c2ecf20Sopenharmony_ci}
36098c2ecf20Sopenharmony_ci
36108c2ecf20Sopenharmony_ciint
36118c2ecf20Sopenharmony_ciiscsit_immediate_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state)
36128c2ecf20Sopenharmony_ci{
36138c2ecf20Sopenharmony_ci	int ret;
36148c2ecf20Sopenharmony_ci
36158c2ecf20Sopenharmony_ci	switch (state) {
36168c2ecf20Sopenharmony_ci	case ISTATE_SEND_R2T:
36178c2ecf20Sopenharmony_ci		ret = iscsit_send_r2t(cmd, conn);
36188c2ecf20Sopenharmony_ci		if (ret < 0)
36198c2ecf20Sopenharmony_ci			goto err;
36208c2ecf20Sopenharmony_ci		break;
36218c2ecf20Sopenharmony_ci	case ISTATE_REMOVE:
36228c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
36238c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
36248c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
36258c2ecf20Sopenharmony_ci
36268c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, false);
36278c2ecf20Sopenharmony_ci		break;
36288c2ecf20Sopenharmony_ci	case ISTATE_SEND_NOPIN_WANT_RESPONSE:
36298c2ecf20Sopenharmony_ci		iscsit_mod_nopin_response_timer(conn);
36308c2ecf20Sopenharmony_ci		ret = iscsit_send_unsolicited_nopin(cmd, conn, 1);
36318c2ecf20Sopenharmony_ci		if (ret < 0)
36328c2ecf20Sopenharmony_ci			goto err;
36338c2ecf20Sopenharmony_ci		break;
36348c2ecf20Sopenharmony_ci	case ISTATE_SEND_NOPIN_NO_RESPONSE:
36358c2ecf20Sopenharmony_ci		ret = iscsit_send_unsolicited_nopin(cmd, conn, 0);
36368c2ecf20Sopenharmony_ci		if (ret < 0)
36378c2ecf20Sopenharmony_ci			goto err;
36388c2ecf20Sopenharmony_ci		break;
36398c2ecf20Sopenharmony_ci	default:
36408c2ecf20Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
36418c2ecf20Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
36428c2ecf20Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag, state,
36438c2ecf20Sopenharmony_ci		       conn->cid);
36448c2ecf20Sopenharmony_ci		goto err;
36458c2ecf20Sopenharmony_ci	}
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci	return 0;
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_cierr:
36508c2ecf20Sopenharmony_ci	return -1;
36518c2ecf20Sopenharmony_ci}
36528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_immediate_queue);
36538c2ecf20Sopenharmony_ci
36548c2ecf20Sopenharmony_cistatic int
36558c2ecf20Sopenharmony_ciiscsit_handle_immediate_queue(struct iscsi_conn *conn)
36568c2ecf20Sopenharmony_ci{
36578c2ecf20Sopenharmony_ci	struct iscsit_transport *t = conn->conn_transport;
36588c2ecf20Sopenharmony_ci	struct iscsi_queue_req *qr;
36598c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
36608c2ecf20Sopenharmony_ci	u8 state;
36618c2ecf20Sopenharmony_ci	int ret;
36628c2ecf20Sopenharmony_ci
36638c2ecf20Sopenharmony_ci	while ((qr = iscsit_get_cmd_from_immediate_queue(conn))) {
36648c2ecf20Sopenharmony_ci		atomic_set(&conn->check_immediate_queue, 0);
36658c2ecf20Sopenharmony_ci		cmd = qr->cmd;
36668c2ecf20Sopenharmony_ci		state = qr->state;
36678c2ecf20Sopenharmony_ci		kmem_cache_free(lio_qr_cache, qr);
36688c2ecf20Sopenharmony_ci
36698c2ecf20Sopenharmony_ci		ret = t->iscsit_immediate_queue(conn, cmd, state);
36708c2ecf20Sopenharmony_ci		if (ret < 0)
36718c2ecf20Sopenharmony_ci			return ret;
36728c2ecf20Sopenharmony_ci	}
36738c2ecf20Sopenharmony_ci
36748c2ecf20Sopenharmony_ci	return 0;
36758c2ecf20Sopenharmony_ci}
36768c2ecf20Sopenharmony_ci
36778c2ecf20Sopenharmony_ciint
36788c2ecf20Sopenharmony_ciiscsit_response_queue(struct iscsi_conn *conn, struct iscsi_cmd *cmd, int state)
36798c2ecf20Sopenharmony_ci{
36808c2ecf20Sopenharmony_ci	int ret;
36818c2ecf20Sopenharmony_ci
36828c2ecf20Sopenharmony_cicheck_rsp_state:
36838c2ecf20Sopenharmony_ci	switch (state) {
36848c2ecf20Sopenharmony_ci	case ISTATE_SEND_DATAIN:
36858c2ecf20Sopenharmony_ci		ret = iscsit_send_datain(cmd, conn);
36868c2ecf20Sopenharmony_ci		if (ret < 0)
36878c2ecf20Sopenharmony_ci			goto err;
36888c2ecf20Sopenharmony_ci		else if (!ret)
36898c2ecf20Sopenharmony_ci			/* more drs */
36908c2ecf20Sopenharmony_ci			goto check_rsp_state;
36918c2ecf20Sopenharmony_ci		else if (ret == 1) {
36928c2ecf20Sopenharmony_ci			/* all done */
36938c2ecf20Sopenharmony_ci			spin_lock_bh(&cmd->istate_lock);
36948c2ecf20Sopenharmony_ci			cmd->i_state = ISTATE_SENT_STATUS;
36958c2ecf20Sopenharmony_ci			spin_unlock_bh(&cmd->istate_lock);
36968c2ecf20Sopenharmony_ci
36978c2ecf20Sopenharmony_ci			if (atomic_read(&conn->check_immediate_queue))
36988c2ecf20Sopenharmony_ci				return 1;
36998c2ecf20Sopenharmony_ci
37008c2ecf20Sopenharmony_ci			return 0;
37018c2ecf20Sopenharmony_ci		} else if (ret == 2) {
37028c2ecf20Sopenharmony_ci			/* Still must send status,
37038c2ecf20Sopenharmony_ci			   SCF_TRANSPORT_TASK_SENSE was set */
37048c2ecf20Sopenharmony_ci			spin_lock_bh(&cmd->istate_lock);
37058c2ecf20Sopenharmony_ci			cmd->i_state = ISTATE_SEND_STATUS;
37068c2ecf20Sopenharmony_ci			spin_unlock_bh(&cmd->istate_lock);
37078c2ecf20Sopenharmony_ci			state = ISTATE_SEND_STATUS;
37088c2ecf20Sopenharmony_ci			goto check_rsp_state;
37098c2ecf20Sopenharmony_ci		}
37108c2ecf20Sopenharmony_ci
37118c2ecf20Sopenharmony_ci		break;
37128c2ecf20Sopenharmony_ci	case ISTATE_SEND_STATUS:
37138c2ecf20Sopenharmony_ci	case ISTATE_SEND_STATUS_RECOVERY:
37148c2ecf20Sopenharmony_ci		ret = iscsit_send_response(cmd, conn);
37158c2ecf20Sopenharmony_ci		break;
37168c2ecf20Sopenharmony_ci	case ISTATE_SEND_LOGOUTRSP:
37178c2ecf20Sopenharmony_ci		ret = iscsit_send_logout(cmd, conn);
37188c2ecf20Sopenharmony_ci		break;
37198c2ecf20Sopenharmony_ci	case ISTATE_SEND_ASYNCMSG:
37208c2ecf20Sopenharmony_ci		ret = iscsit_send_conn_drop_async_message(
37218c2ecf20Sopenharmony_ci			cmd, conn);
37228c2ecf20Sopenharmony_ci		break;
37238c2ecf20Sopenharmony_ci	case ISTATE_SEND_NOPIN:
37248c2ecf20Sopenharmony_ci		ret = iscsit_send_nopin(cmd, conn);
37258c2ecf20Sopenharmony_ci		break;
37268c2ecf20Sopenharmony_ci	case ISTATE_SEND_REJECT:
37278c2ecf20Sopenharmony_ci		ret = iscsit_send_reject(cmd, conn);
37288c2ecf20Sopenharmony_ci		break;
37298c2ecf20Sopenharmony_ci	case ISTATE_SEND_TASKMGTRSP:
37308c2ecf20Sopenharmony_ci		ret = iscsit_send_task_mgt_rsp(cmd, conn);
37318c2ecf20Sopenharmony_ci		if (ret != 0)
37328c2ecf20Sopenharmony_ci			break;
37338c2ecf20Sopenharmony_ci		ret = iscsit_tmr_post_handler(cmd, conn);
37348c2ecf20Sopenharmony_ci		if (ret != 0)
37358c2ecf20Sopenharmony_ci			iscsit_fall_back_to_erl0(conn->sess);
37368c2ecf20Sopenharmony_ci		break;
37378c2ecf20Sopenharmony_ci	case ISTATE_SEND_TEXTRSP:
37388c2ecf20Sopenharmony_ci		ret = iscsit_send_text_rsp(cmd, conn);
37398c2ecf20Sopenharmony_ci		break;
37408c2ecf20Sopenharmony_ci	default:
37418c2ecf20Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
37428c2ecf20Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
37438c2ecf20Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag,
37448c2ecf20Sopenharmony_ci		       state, conn->cid);
37458c2ecf20Sopenharmony_ci		goto err;
37468c2ecf20Sopenharmony_ci	}
37478c2ecf20Sopenharmony_ci	if (ret < 0)
37488c2ecf20Sopenharmony_ci		goto err;
37498c2ecf20Sopenharmony_ci
37508c2ecf20Sopenharmony_ci	switch (state) {
37518c2ecf20Sopenharmony_ci	case ISTATE_SEND_LOGOUTRSP:
37528c2ecf20Sopenharmony_ci		if (!iscsit_logout_post_handler(cmd, conn))
37538c2ecf20Sopenharmony_ci			return -ECONNRESET;
37548c2ecf20Sopenharmony_ci		fallthrough;
37558c2ecf20Sopenharmony_ci	case ISTATE_SEND_STATUS:
37568c2ecf20Sopenharmony_ci	case ISTATE_SEND_ASYNCMSG:
37578c2ecf20Sopenharmony_ci	case ISTATE_SEND_NOPIN:
37588c2ecf20Sopenharmony_ci	case ISTATE_SEND_STATUS_RECOVERY:
37598c2ecf20Sopenharmony_ci	case ISTATE_SEND_TEXTRSP:
37608c2ecf20Sopenharmony_ci	case ISTATE_SEND_TASKMGTRSP:
37618c2ecf20Sopenharmony_ci	case ISTATE_SEND_REJECT:
37628c2ecf20Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
37638c2ecf20Sopenharmony_ci		cmd->i_state = ISTATE_SENT_STATUS;
37648c2ecf20Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
37658c2ecf20Sopenharmony_ci		break;
37668c2ecf20Sopenharmony_ci	default:
37678c2ecf20Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
37688c2ecf20Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
37698c2ecf20Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag,
37708c2ecf20Sopenharmony_ci		       cmd->i_state, conn->cid);
37718c2ecf20Sopenharmony_ci		goto err;
37728c2ecf20Sopenharmony_ci	}
37738c2ecf20Sopenharmony_ci
37748c2ecf20Sopenharmony_ci	if (atomic_read(&conn->check_immediate_queue))
37758c2ecf20Sopenharmony_ci		return 1;
37768c2ecf20Sopenharmony_ci
37778c2ecf20Sopenharmony_ci	return 0;
37788c2ecf20Sopenharmony_ci
37798c2ecf20Sopenharmony_cierr:
37808c2ecf20Sopenharmony_ci	return -1;
37818c2ecf20Sopenharmony_ci}
37828c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_response_queue);
37838c2ecf20Sopenharmony_ci
37848c2ecf20Sopenharmony_cistatic int iscsit_handle_response_queue(struct iscsi_conn *conn)
37858c2ecf20Sopenharmony_ci{
37868c2ecf20Sopenharmony_ci	struct iscsit_transport *t = conn->conn_transport;
37878c2ecf20Sopenharmony_ci	struct iscsi_queue_req *qr;
37888c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
37898c2ecf20Sopenharmony_ci	u8 state;
37908c2ecf20Sopenharmony_ci	int ret;
37918c2ecf20Sopenharmony_ci
37928c2ecf20Sopenharmony_ci	while ((qr = iscsit_get_cmd_from_response_queue(conn))) {
37938c2ecf20Sopenharmony_ci		cmd = qr->cmd;
37948c2ecf20Sopenharmony_ci		state = qr->state;
37958c2ecf20Sopenharmony_ci		kmem_cache_free(lio_qr_cache, qr);
37968c2ecf20Sopenharmony_ci
37978c2ecf20Sopenharmony_ci		ret = t->iscsit_response_queue(conn, cmd, state);
37988c2ecf20Sopenharmony_ci		if (ret == 1 || ret < 0)
37998c2ecf20Sopenharmony_ci			return ret;
38008c2ecf20Sopenharmony_ci	}
38018c2ecf20Sopenharmony_ci
38028c2ecf20Sopenharmony_ci	return 0;
38038c2ecf20Sopenharmony_ci}
38048c2ecf20Sopenharmony_ci
38058c2ecf20Sopenharmony_ciint iscsi_target_tx_thread(void *arg)
38068c2ecf20Sopenharmony_ci{
38078c2ecf20Sopenharmony_ci	int ret = 0;
38088c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = arg;
38098c2ecf20Sopenharmony_ci	bool conn_freed = false;
38108c2ecf20Sopenharmony_ci
38118c2ecf20Sopenharmony_ci	/*
38128c2ecf20Sopenharmony_ci	 * Allow ourselves to be interrupted by SIGINT so that a
38138c2ecf20Sopenharmony_ci	 * connection recovery / failure event can be triggered externally.
38148c2ecf20Sopenharmony_ci	 */
38158c2ecf20Sopenharmony_ci	allow_signal(SIGINT);
38168c2ecf20Sopenharmony_ci
38178c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
38188c2ecf20Sopenharmony_ci		/*
38198c2ecf20Sopenharmony_ci		 * Ensure that both TX and RX per connection kthreads
38208c2ecf20Sopenharmony_ci		 * are scheduled to run on the same CPU.
38218c2ecf20Sopenharmony_ci		 */
38228c2ecf20Sopenharmony_ci		iscsit_thread_check_cpumask(conn, current, 1);
38238c2ecf20Sopenharmony_ci
38248c2ecf20Sopenharmony_ci		wait_event_interruptible(conn->queues_wq,
38258c2ecf20Sopenharmony_ci					 !iscsit_conn_all_queues_empty(conn));
38268c2ecf20Sopenharmony_ci
38278c2ecf20Sopenharmony_ci		if (signal_pending(current))
38288c2ecf20Sopenharmony_ci			goto transport_err;
38298c2ecf20Sopenharmony_ci
38308c2ecf20Sopenharmony_ciget_immediate:
38318c2ecf20Sopenharmony_ci		ret = iscsit_handle_immediate_queue(conn);
38328c2ecf20Sopenharmony_ci		if (ret < 0)
38338c2ecf20Sopenharmony_ci			goto transport_err;
38348c2ecf20Sopenharmony_ci
38358c2ecf20Sopenharmony_ci		ret = iscsit_handle_response_queue(conn);
38368c2ecf20Sopenharmony_ci		if (ret == 1) {
38378c2ecf20Sopenharmony_ci			goto get_immediate;
38388c2ecf20Sopenharmony_ci		} else if (ret == -ECONNRESET) {
38398c2ecf20Sopenharmony_ci			conn_freed = true;
38408c2ecf20Sopenharmony_ci			goto out;
38418c2ecf20Sopenharmony_ci		} else if (ret < 0) {
38428c2ecf20Sopenharmony_ci			goto transport_err;
38438c2ecf20Sopenharmony_ci		}
38448c2ecf20Sopenharmony_ci	}
38458c2ecf20Sopenharmony_ci
38468c2ecf20Sopenharmony_citransport_err:
38478c2ecf20Sopenharmony_ci	/*
38488c2ecf20Sopenharmony_ci	 * Avoid the normal connection failure code-path if this connection
38498c2ecf20Sopenharmony_ci	 * is still within LOGIN mode, and iscsi_np process context is
38508c2ecf20Sopenharmony_ci	 * responsible for cleaning up the early connection failure.
38518c2ecf20Sopenharmony_ci	 */
38528c2ecf20Sopenharmony_ci	if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
38538c2ecf20Sopenharmony_ci		iscsit_take_action_for_connection_exit(conn, &conn_freed);
38548c2ecf20Sopenharmony_ciout:
38558c2ecf20Sopenharmony_ci	if (!conn_freed) {
38568c2ecf20Sopenharmony_ci		while (!kthread_should_stop()) {
38578c2ecf20Sopenharmony_ci			msleep(100);
38588c2ecf20Sopenharmony_ci		}
38598c2ecf20Sopenharmony_ci	}
38608c2ecf20Sopenharmony_ci	return 0;
38618c2ecf20Sopenharmony_ci}
38628c2ecf20Sopenharmony_ci
38638c2ecf20Sopenharmony_cistatic int iscsi_target_rx_opcode(struct iscsi_conn *conn, unsigned char *buf)
38648c2ecf20Sopenharmony_ci{
38658c2ecf20Sopenharmony_ci	struct iscsi_hdr *hdr = (struct iscsi_hdr *)buf;
38668c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
38678c2ecf20Sopenharmony_ci	int ret = 0;
38688c2ecf20Sopenharmony_ci
38698c2ecf20Sopenharmony_ci	switch (hdr->opcode & ISCSI_OPCODE_MASK) {
38708c2ecf20Sopenharmony_ci	case ISCSI_OP_SCSI_CMD:
38718c2ecf20Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
38728c2ecf20Sopenharmony_ci		if (!cmd)
38738c2ecf20Sopenharmony_ci			goto reject;
38748c2ecf20Sopenharmony_ci
38758c2ecf20Sopenharmony_ci		ret = iscsit_handle_scsi_cmd(conn, cmd, buf);
38768c2ecf20Sopenharmony_ci		break;
38778c2ecf20Sopenharmony_ci	case ISCSI_OP_SCSI_DATA_OUT:
38788c2ecf20Sopenharmony_ci		ret = iscsit_handle_data_out(conn, buf);
38798c2ecf20Sopenharmony_ci		break;
38808c2ecf20Sopenharmony_ci	case ISCSI_OP_NOOP_OUT:
38818c2ecf20Sopenharmony_ci		cmd = NULL;
38828c2ecf20Sopenharmony_ci		if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
38838c2ecf20Sopenharmony_ci			cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
38848c2ecf20Sopenharmony_ci			if (!cmd)
38858c2ecf20Sopenharmony_ci				goto reject;
38868c2ecf20Sopenharmony_ci		}
38878c2ecf20Sopenharmony_ci		ret = iscsit_handle_nop_out(conn, cmd, buf);
38888c2ecf20Sopenharmony_ci		break;
38898c2ecf20Sopenharmony_ci	case ISCSI_OP_SCSI_TMFUNC:
38908c2ecf20Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
38918c2ecf20Sopenharmony_ci		if (!cmd)
38928c2ecf20Sopenharmony_ci			goto reject;
38938c2ecf20Sopenharmony_ci
38948c2ecf20Sopenharmony_ci		ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf);
38958c2ecf20Sopenharmony_ci		break;
38968c2ecf20Sopenharmony_ci	case ISCSI_OP_TEXT:
38978c2ecf20Sopenharmony_ci		if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
38988c2ecf20Sopenharmony_ci			cmd = iscsit_find_cmd_from_itt(conn, hdr->itt);
38998c2ecf20Sopenharmony_ci			if (!cmd)
39008c2ecf20Sopenharmony_ci				goto reject;
39018c2ecf20Sopenharmony_ci		} else {
39028c2ecf20Sopenharmony_ci			cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
39038c2ecf20Sopenharmony_ci			if (!cmd)
39048c2ecf20Sopenharmony_ci				goto reject;
39058c2ecf20Sopenharmony_ci		}
39068c2ecf20Sopenharmony_ci
39078c2ecf20Sopenharmony_ci		ret = iscsit_handle_text_cmd(conn, cmd, buf);
39088c2ecf20Sopenharmony_ci		break;
39098c2ecf20Sopenharmony_ci	case ISCSI_OP_LOGOUT:
39108c2ecf20Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
39118c2ecf20Sopenharmony_ci		if (!cmd)
39128c2ecf20Sopenharmony_ci			goto reject;
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci		ret = iscsit_handle_logout_cmd(conn, cmd, buf);
39158c2ecf20Sopenharmony_ci		if (ret > 0)
39168c2ecf20Sopenharmony_ci			wait_for_completion_timeout(&conn->conn_logout_comp,
39178c2ecf20Sopenharmony_ci					SECONDS_FOR_LOGOUT_COMP * HZ);
39188c2ecf20Sopenharmony_ci		break;
39198c2ecf20Sopenharmony_ci	case ISCSI_OP_SNACK:
39208c2ecf20Sopenharmony_ci		ret = iscsit_handle_snack(conn, buf);
39218c2ecf20Sopenharmony_ci		break;
39228c2ecf20Sopenharmony_ci	default:
39238c2ecf20Sopenharmony_ci		pr_err("Got unknown iSCSI OpCode: 0x%02x\n", hdr->opcode);
39248c2ecf20Sopenharmony_ci		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
39258c2ecf20Sopenharmony_ci			pr_err("Cannot recover from unknown"
39268c2ecf20Sopenharmony_ci			" opcode while ERL=0, closing iSCSI connection.\n");
39278c2ecf20Sopenharmony_ci			return -1;
39288c2ecf20Sopenharmony_ci		}
39298c2ecf20Sopenharmony_ci		pr_err("Unable to recover from unknown opcode while OFMarker=No,"
39308c2ecf20Sopenharmony_ci		       " closing iSCSI connection.\n");
39318c2ecf20Sopenharmony_ci		ret = -1;
39328c2ecf20Sopenharmony_ci		break;
39338c2ecf20Sopenharmony_ci	}
39348c2ecf20Sopenharmony_ci
39358c2ecf20Sopenharmony_ci	return ret;
39368c2ecf20Sopenharmony_cireject:
39378c2ecf20Sopenharmony_ci	return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
39388c2ecf20Sopenharmony_ci}
39398c2ecf20Sopenharmony_ci
39408c2ecf20Sopenharmony_cistatic bool iscsi_target_check_conn_state(struct iscsi_conn *conn)
39418c2ecf20Sopenharmony_ci{
39428c2ecf20Sopenharmony_ci	bool ret;
39438c2ecf20Sopenharmony_ci
39448c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
39458c2ecf20Sopenharmony_ci	ret = (conn->conn_state != TARG_CONN_STATE_LOGGED_IN);
39468c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
39478c2ecf20Sopenharmony_ci
39488c2ecf20Sopenharmony_ci	return ret;
39498c2ecf20Sopenharmony_ci}
39508c2ecf20Sopenharmony_ci
39518c2ecf20Sopenharmony_cistatic void iscsit_get_rx_pdu(struct iscsi_conn *conn)
39528c2ecf20Sopenharmony_ci{
39538c2ecf20Sopenharmony_ci	int ret;
39548c2ecf20Sopenharmony_ci	u8 *buffer, opcode;
39558c2ecf20Sopenharmony_ci	u32 checksum = 0, digest = 0;
39568c2ecf20Sopenharmony_ci	struct kvec iov;
39578c2ecf20Sopenharmony_ci
39588c2ecf20Sopenharmony_ci	buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL);
39598c2ecf20Sopenharmony_ci	if (!buffer)
39608c2ecf20Sopenharmony_ci		return;
39618c2ecf20Sopenharmony_ci
39628c2ecf20Sopenharmony_ci	while (!kthread_should_stop()) {
39638c2ecf20Sopenharmony_ci		/*
39648c2ecf20Sopenharmony_ci		 * Ensure that both TX and RX per connection kthreads
39658c2ecf20Sopenharmony_ci		 * are scheduled to run on the same CPU.
39668c2ecf20Sopenharmony_ci		 */
39678c2ecf20Sopenharmony_ci		iscsit_thread_check_cpumask(conn, current, 0);
39688c2ecf20Sopenharmony_ci
39698c2ecf20Sopenharmony_ci		memset(&iov, 0, sizeof(struct kvec));
39708c2ecf20Sopenharmony_ci
39718c2ecf20Sopenharmony_ci		iov.iov_base	= buffer;
39728c2ecf20Sopenharmony_ci		iov.iov_len	= ISCSI_HDR_LEN;
39738c2ecf20Sopenharmony_ci
39748c2ecf20Sopenharmony_ci		ret = rx_data(conn, &iov, 1, ISCSI_HDR_LEN);
39758c2ecf20Sopenharmony_ci		if (ret != ISCSI_HDR_LEN) {
39768c2ecf20Sopenharmony_ci			iscsit_rx_thread_wait_for_tcp(conn);
39778c2ecf20Sopenharmony_ci			break;
39788c2ecf20Sopenharmony_ci		}
39798c2ecf20Sopenharmony_ci
39808c2ecf20Sopenharmony_ci		if (conn->conn_ops->HeaderDigest) {
39818c2ecf20Sopenharmony_ci			iov.iov_base	= &digest;
39828c2ecf20Sopenharmony_ci			iov.iov_len	= ISCSI_CRC_LEN;
39838c2ecf20Sopenharmony_ci
39848c2ecf20Sopenharmony_ci			ret = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
39858c2ecf20Sopenharmony_ci			if (ret != ISCSI_CRC_LEN) {
39868c2ecf20Sopenharmony_ci				iscsit_rx_thread_wait_for_tcp(conn);
39878c2ecf20Sopenharmony_ci				break;
39888c2ecf20Sopenharmony_ci			}
39898c2ecf20Sopenharmony_ci
39908c2ecf20Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash, buffer,
39918c2ecf20Sopenharmony_ci						  ISCSI_HDR_LEN, 0, NULL,
39928c2ecf20Sopenharmony_ci						  &checksum);
39938c2ecf20Sopenharmony_ci
39948c2ecf20Sopenharmony_ci			if (digest != checksum) {
39958c2ecf20Sopenharmony_ci				pr_err("HeaderDigest CRC32C failed,"
39968c2ecf20Sopenharmony_ci					" received 0x%08x, computed 0x%08x\n",
39978c2ecf20Sopenharmony_ci					digest, checksum);
39988c2ecf20Sopenharmony_ci				/*
39998c2ecf20Sopenharmony_ci				 * Set the PDU to 0xff so it will intentionally
40008c2ecf20Sopenharmony_ci				 * hit default in the switch below.
40018c2ecf20Sopenharmony_ci				 */
40028c2ecf20Sopenharmony_ci				memset(buffer, 0xff, ISCSI_HDR_LEN);
40038c2ecf20Sopenharmony_ci				atomic_long_inc(&conn->sess->conn_digest_errors);
40048c2ecf20Sopenharmony_ci			} else {
40058c2ecf20Sopenharmony_ci				pr_debug("Got HeaderDigest CRC32C"
40068c2ecf20Sopenharmony_ci						" 0x%08x\n", checksum);
40078c2ecf20Sopenharmony_ci			}
40088c2ecf20Sopenharmony_ci		}
40098c2ecf20Sopenharmony_ci
40108c2ecf20Sopenharmony_ci		if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT)
40118c2ecf20Sopenharmony_ci			break;
40128c2ecf20Sopenharmony_ci
40138c2ecf20Sopenharmony_ci		opcode = buffer[0] & ISCSI_OPCODE_MASK;
40148c2ecf20Sopenharmony_ci
40158c2ecf20Sopenharmony_ci		if (conn->sess->sess_ops->SessionType &&
40168c2ecf20Sopenharmony_ci		   ((!(opcode & ISCSI_OP_TEXT)) ||
40178c2ecf20Sopenharmony_ci		    (!(opcode & ISCSI_OP_LOGOUT)))) {
40188c2ecf20Sopenharmony_ci			pr_err("Received illegal iSCSI Opcode: 0x%02x"
40198c2ecf20Sopenharmony_ci			" while in Discovery Session, rejecting.\n", opcode);
40208c2ecf20Sopenharmony_ci			iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
40218c2ecf20Sopenharmony_ci					  buffer);
40228c2ecf20Sopenharmony_ci			break;
40238c2ecf20Sopenharmony_ci		}
40248c2ecf20Sopenharmony_ci
40258c2ecf20Sopenharmony_ci		ret = iscsi_target_rx_opcode(conn, buffer);
40268c2ecf20Sopenharmony_ci		if (ret < 0)
40278c2ecf20Sopenharmony_ci			break;
40288c2ecf20Sopenharmony_ci	}
40298c2ecf20Sopenharmony_ci
40308c2ecf20Sopenharmony_ci	kfree(buffer);
40318c2ecf20Sopenharmony_ci}
40328c2ecf20Sopenharmony_ci
40338c2ecf20Sopenharmony_ciint iscsi_target_rx_thread(void *arg)
40348c2ecf20Sopenharmony_ci{
40358c2ecf20Sopenharmony_ci	int rc;
40368c2ecf20Sopenharmony_ci	struct iscsi_conn *conn = arg;
40378c2ecf20Sopenharmony_ci	bool conn_freed = false;
40388c2ecf20Sopenharmony_ci
40398c2ecf20Sopenharmony_ci	/*
40408c2ecf20Sopenharmony_ci	 * Allow ourselves to be interrupted by SIGINT so that a
40418c2ecf20Sopenharmony_ci	 * connection recovery / failure event can be triggered externally.
40428c2ecf20Sopenharmony_ci	 */
40438c2ecf20Sopenharmony_ci	allow_signal(SIGINT);
40448c2ecf20Sopenharmony_ci	/*
40458c2ecf20Sopenharmony_ci	 * Wait for iscsi_post_login_handler() to complete before allowing
40468c2ecf20Sopenharmony_ci	 * incoming iscsi/tcp socket I/O, and/or failing the connection.
40478c2ecf20Sopenharmony_ci	 */
40488c2ecf20Sopenharmony_ci	rc = wait_for_completion_interruptible(&conn->rx_login_comp);
40498c2ecf20Sopenharmony_ci	if (rc < 0 || iscsi_target_check_conn_state(conn))
40508c2ecf20Sopenharmony_ci		goto out;
40518c2ecf20Sopenharmony_ci
40528c2ecf20Sopenharmony_ci	if (!conn->conn_transport->iscsit_get_rx_pdu)
40538c2ecf20Sopenharmony_ci		return 0;
40548c2ecf20Sopenharmony_ci
40558c2ecf20Sopenharmony_ci	conn->conn_transport->iscsit_get_rx_pdu(conn);
40568c2ecf20Sopenharmony_ci
40578c2ecf20Sopenharmony_ci	if (!signal_pending(current))
40588c2ecf20Sopenharmony_ci		atomic_set(&conn->transport_failed, 1);
40598c2ecf20Sopenharmony_ci	iscsit_take_action_for_connection_exit(conn, &conn_freed);
40608c2ecf20Sopenharmony_ci
40618c2ecf20Sopenharmony_ciout:
40628c2ecf20Sopenharmony_ci	if (!conn_freed) {
40638c2ecf20Sopenharmony_ci		while (!kthread_should_stop()) {
40648c2ecf20Sopenharmony_ci			msleep(100);
40658c2ecf20Sopenharmony_ci		}
40668c2ecf20Sopenharmony_ci	}
40678c2ecf20Sopenharmony_ci
40688c2ecf20Sopenharmony_ci	return 0;
40698c2ecf20Sopenharmony_ci}
40708c2ecf20Sopenharmony_ci
40718c2ecf20Sopenharmony_cistatic void iscsit_release_commands_from_conn(struct iscsi_conn *conn)
40728c2ecf20Sopenharmony_ci{
40738c2ecf20Sopenharmony_ci	LIST_HEAD(tmp_list);
40748c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd = NULL, *cmd_tmp = NULL;
40758c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
40768c2ecf20Sopenharmony_ci	/*
40778c2ecf20Sopenharmony_ci	 * We expect this function to only ever be called from either RX or TX
40788c2ecf20Sopenharmony_ci	 * thread context via iscsit_close_connection() once the other context
40798c2ecf20Sopenharmony_ci	 * has been reset -> returned sleeping pre-handler state.
40808c2ecf20Sopenharmony_ci	 */
40818c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
40828c2ecf20Sopenharmony_ci	list_splice_init(&conn->conn_cmd_list, &tmp_list);
40838c2ecf20Sopenharmony_ci
40848c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
40858c2ecf20Sopenharmony_ci		struct se_cmd *se_cmd = &cmd->se_cmd;
40868c2ecf20Sopenharmony_ci
40878c2ecf20Sopenharmony_ci		if (!se_cmd->se_tfo)
40888c2ecf20Sopenharmony_ci			continue;
40898c2ecf20Sopenharmony_ci
40908c2ecf20Sopenharmony_ci		spin_lock_irq(&se_cmd->t_state_lock);
40918c2ecf20Sopenharmony_ci		if (se_cmd->transport_state & CMD_T_ABORTED) {
40928c2ecf20Sopenharmony_ci			if (!(se_cmd->transport_state & CMD_T_TAS))
40938c2ecf20Sopenharmony_ci				/*
40948c2ecf20Sopenharmony_ci				 * LIO's abort path owns the cleanup for this,
40958c2ecf20Sopenharmony_ci				 * so put it back on the list and let
40968c2ecf20Sopenharmony_ci				 * aborted_task handle it.
40978c2ecf20Sopenharmony_ci				 */
40988c2ecf20Sopenharmony_ci				list_move_tail(&cmd->i_conn_node,
40998c2ecf20Sopenharmony_ci					       &conn->conn_cmd_list);
41008c2ecf20Sopenharmony_ci		} else {
41018c2ecf20Sopenharmony_ci			se_cmd->transport_state |= CMD_T_FABRIC_STOP;
41028c2ecf20Sopenharmony_ci		}
41038c2ecf20Sopenharmony_ci		spin_unlock_irq(&se_cmd->t_state_lock);
41048c2ecf20Sopenharmony_ci	}
41058c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
41068c2ecf20Sopenharmony_ci
41078c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
41088c2ecf20Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
41098c2ecf20Sopenharmony_ci
41108c2ecf20Sopenharmony_ci		iscsit_increment_maxcmdsn(cmd, sess);
41118c2ecf20Sopenharmony_ci		iscsit_free_cmd(cmd, true);
41128c2ecf20Sopenharmony_ci
41138c2ecf20Sopenharmony_ci	}
41148c2ecf20Sopenharmony_ci}
41158c2ecf20Sopenharmony_ci
41168c2ecf20Sopenharmony_cistatic void iscsit_stop_timers_for_cmds(
41178c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
41188c2ecf20Sopenharmony_ci{
41198c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd;
41208c2ecf20Sopenharmony_ci
41218c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
41228c2ecf20Sopenharmony_ci	list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
41238c2ecf20Sopenharmony_ci		if (cmd->data_direction == DMA_TO_DEVICE)
41248c2ecf20Sopenharmony_ci			iscsit_stop_dataout_timer(cmd);
41258c2ecf20Sopenharmony_ci	}
41268c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
41278c2ecf20Sopenharmony_ci}
41288c2ecf20Sopenharmony_ci
41298c2ecf20Sopenharmony_ciint iscsit_close_connection(
41308c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
41318c2ecf20Sopenharmony_ci{
41328c2ecf20Sopenharmony_ci	int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT);
41338c2ecf20Sopenharmony_ci	struct iscsi_session	*sess = conn->sess;
41348c2ecf20Sopenharmony_ci
41358c2ecf20Sopenharmony_ci	pr_debug("Closing iSCSI connection CID %hu on SID:"
41368c2ecf20Sopenharmony_ci		" %u\n", conn->cid, sess->sid);
41378c2ecf20Sopenharmony_ci	/*
41388c2ecf20Sopenharmony_ci	 * Always up conn_logout_comp for the traditional TCP and HW_OFFLOAD
41398c2ecf20Sopenharmony_ci	 * case just in case the RX Thread in iscsi_target_rx_opcode() is
41408c2ecf20Sopenharmony_ci	 * sleeping and the logout response never got sent because the
41418c2ecf20Sopenharmony_ci	 * connection failed.
41428c2ecf20Sopenharmony_ci	 *
41438c2ecf20Sopenharmony_ci	 * However for iser-target, isert_wait4logout() is using conn_logout_comp
41448c2ecf20Sopenharmony_ci	 * to signal logout response TX interrupt completion.  Go ahead and skip
41458c2ecf20Sopenharmony_ci	 * this for iser since isert_rx_opcode() does not wait on logout failure,
41468c2ecf20Sopenharmony_ci	 * and to avoid iscsi_conn pointer dereference in iser-target code.
41478c2ecf20Sopenharmony_ci	 */
41488c2ecf20Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown)
41498c2ecf20Sopenharmony_ci		complete(&conn->conn_logout_comp);
41508c2ecf20Sopenharmony_ci
41518c2ecf20Sopenharmony_ci	if (!strcmp(current->comm, ISCSI_RX_THREAD_NAME)) {
41528c2ecf20Sopenharmony_ci		if (conn->tx_thread &&
41538c2ecf20Sopenharmony_ci		    cmpxchg(&conn->tx_thread_active, true, false)) {
41548c2ecf20Sopenharmony_ci			send_sig(SIGINT, conn->tx_thread, 1);
41558c2ecf20Sopenharmony_ci			kthread_stop(conn->tx_thread);
41568c2ecf20Sopenharmony_ci		}
41578c2ecf20Sopenharmony_ci	} else if (!strcmp(current->comm, ISCSI_TX_THREAD_NAME)) {
41588c2ecf20Sopenharmony_ci		if (conn->rx_thread &&
41598c2ecf20Sopenharmony_ci		    cmpxchg(&conn->rx_thread_active, true, false)) {
41608c2ecf20Sopenharmony_ci			send_sig(SIGINT, conn->rx_thread, 1);
41618c2ecf20Sopenharmony_ci			kthread_stop(conn->rx_thread);
41628c2ecf20Sopenharmony_ci		}
41638c2ecf20Sopenharmony_ci	}
41648c2ecf20Sopenharmony_ci
41658c2ecf20Sopenharmony_ci	spin_lock(&iscsit_global->ts_bitmap_lock);
41668c2ecf20Sopenharmony_ci	bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id,
41678c2ecf20Sopenharmony_ci			      get_order(1));
41688c2ecf20Sopenharmony_ci	spin_unlock(&iscsit_global->ts_bitmap_lock);
41698c2ecf20Sopenharmony_ci
41708c2ecf20Sopenharmony_ci	iscsit_stop_timers_for_cmds(conn);
41718c2ecf20Sopenharmony_ci	iscsit_stop_nopin_response_timer(conn);
41728c2ecf20Sopenharmony_ci	iscsit_stop_nopin_timer(conn);
41738c2ecf20Sopenharmony_ci
41748c2ecf20Sopenharmony_ci	if (conn->conn_transport->iscsit_wait_conn)
41758c2ecf20Sopenharmony_ci		conn->conn_transport->iscsit_wait_conn(conn);
41768c2ecf20Sopenharmony_ci
41778c2ecf20Sopenharmony_ci	/*
41788c2ecf20Sopenharmony_ci	 * During Connection recovery drop unacknowledged out of order
41798c2ecf20Sopenharmony_ci	 * commands for this connection, and prepare the other commands
41808c2ecf20Sopenharmony_ci	 * for reallegiance.
41818c2ecf20Sopenharmony_ci	 *
41828c2ecf20Sopenharmony_ci	 * During normal operation clear the out of order commands (but
41838c2ecf20Sopenharmony_ci	 * do not free the struct iscsi_ooo_cmdsn's) and release all
41848c2ecf20Sopenharmony_ci	 * struct iscsi_cmds.
41858c2ecf20Sopenharmony_ci	 */
41868c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_recovery)) {
41878c2ecf20Sopenharmony_ci		iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn);
41888c2ecf20Sopenharmony_ci		iscsit_prepare_cmds_for_reallegiance(conn);
41898c2ecf20Sopenharmony_ci	} else {
41908c2ecf20Sopenharmony_ci		iscsit_clear_ooo_cmdsns_for_conn(conn);
41918c2ecf20Sopenharmony_ci		iscsit_release_commands_from_conn(conn);
41928c2ecf20Sopenharmony_ci	}
41938c2ecf20Sopenharmony_ci	iscsit_free_queue_reqs_for_conn(conn);
41948c2ecf20Sopenharmony_ci
41958c2ecf20Sopenharmony_ci	/*
41968c2ecf20Sopenharmony_ci	 * Handle decrementing session or connection usage count if
41978c2ecf20Sopenharmony_ci	 * a logout response was not able to be sent because the
41988c2ecf20Sopenharmony_ci	 * connection failed.  Fall back to Session Recovery here.
41998c2ecf20Sopenharmony_ci	 */
42008c2ecf20Sopenharmony_ci	if (atomic_read(&conn->conn_logout_remove)) {
42018c2ecf20Sopenharmony_ci		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
42028c2ecf20Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
42038c2ecf20Sopenharmony_ci			iscsit_dec_session_usage_count(sess);
42048c2ecf20Sopenharmony_ci		}
42058c2ecf20Sopenharmony_ci		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION)
42068c2ecf20Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
42078c2ecf20Sopenharmony_ci
42088c2ecf20Sopenharmony_ci		atomic_set(&conn->conn_logout_remove, 0);
42098c2ecf20Sopenharmony_ci		atomic_set(&sess->session_reinstatement, 0);
42108c2ecf20Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
42118c2ecf20Sopenharmony_ci	}
42128c2ecf20Sopenharmony_ci
42138c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
42148c2ecf20Sopenharmony_ci	list_del(&conn->conn_list);
42158c2ecf20Sopenharmony_ci
42168c2ecf20Sopenharmony_ci	/*
42178c2ecf20Sopenharmony_ci	 * Attempt to let the Initiator know this connection failed by
42188c2ecf20Sopenharmony_ci	 * sending an Connection Dropped Async Message on another
42198c2ecf20Sopenharmony_ci	 * active connection.
42208c2ecf20Sopenharmony_ci	 */
42218c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_recovery))
42228c2ecf20Sopenharmony_ci		iscsit_build_conn_drop_async_message(conn);
42238c2ecf20Sopenharmony_ci
42248c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
42258c2ecf20Sopenharmony_ci
42268c2ecf20Sopenharmony_ci	/*
42278c2ecf20Sopenharmony_ci	 * If connection reinstatement is being performed on this connection,
42288c2ecf20Sopenharmony_ci	 * up the connection reinstatement semaphore that is being blocked on
42298c2ecf20Sopenharmony_ci	 * in iscsit_cause_connection_reinstatement().
42308c2ecf20Sopenharmony_ci	 */
42318c2ecf20Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
42328c2ecf20Sopenharmony_ci	if (atomic_read(&conn->sleep_on_conn_wait_comp)) {
42338c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
42348c2ecf20Sopenharmony_ci		complete(&conn->conn_wait_comp);
42358c2ecf20Sopenharmony_ci		wait_for_completion(&conn->conn_post_wait_comp);
42368c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
42378c2ecf20Sopenharmony_ci	}
42388c2ecf20Sopenharmony_ci
42398c2ecf20Sopenharmony_ci	/*
42408c2ecf20Sopenharmony_ci	 * If connection reinstatement is being performed on this connection
42418c2ecf20Sopenharmony_ci	 * by receiving a REMOVECONNFORRECOVERY logout request, up the
42428c2ecf20Sopenharmony_ci	 * connection wait rcfr semaphore that is being blocked on
42438c2ecf20Sopenharmony_ci	 * an iscsit_connection_reinstatement_rcfr().
42448c2ecf20Sopenharmony_ci	 */
42458c2ecf20Sopenharmony_ci	if (atomic_read(&conn->connection_wait_rcfr)) {
42468c2ecf20Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
42478c2ecf20Sopenharmony_ci		complete(&conn->conn_wait_rcfr_comp);
42488c2ecf20Sopenharmony_ci		wait_for_completion(&conn->conn_post_wait_comp);
42498c2ecf20Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
42508c2ecf20Sopenharmony_ci	}
42518c2ecf20Sopenharmony_ci	atomic_set(&conn->connection_reinstatement, 1);
42528c2ecf20Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
42538c2ecf20Sopenharmony_ci
42548c2ecf20Sopenharmony_ci	/*
42558c2ecf20Sopenharmony_ci	 * If any other processes are accessing this connection pointer we
42568c2ecf20Sopenharmony_ci	 * must wait until they have completed.
42578c2ecf20Sopenharmony_ci	 */
42588c2ecf20Sopenharmony_ci	iscsit_check_conn_usage_count(conn);
42598c2ecf20Sopenharmony_ci
42608c2ecf20Sopenharmony_ci	ahash_request_free(conn->conn_tx_hash);
42618c2ecf20Sopenharmony_ci	if (conn->conn_rx_hash) {
42628c2ecf20Sopenharmony_ci		struct crypto_ahash *tfm;
42638c2ecf20Sopenharmony_ci
42648c2ecf20Sopenharmony_ci		tfm = crypto_ahash_reqtfm(conn->conn_rx_hash);
42658c2ecf20Sopenharmony_ci		ahash_request_free(conn->conn_rx_hash);
42668c2ecf20Sopenharmony_ci		crypto_free_ahash(tfm);
42678c2ecf20Sopenharmony_ci	}
42688c2ecf20Sopenharmony_ci
42698c2ecf20Sopenharmony_ci	if (conn->sock)
42708c2ecf20Sopenharmony_ci		sock_release(conn->sock);
42718c2ecf20Sopenharmony_ci
42728c2ecf20Sopenharmony_ci	if (conn->conn_transport->iscsit_free_conn)
42738c2ecf20Sopenharmony_ci		conn->conn_transport->iscsit_free_conn(conn);
42748c2ecf20Sopenharmony_ci
42758c2ecf20Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
42768c2ecf20Sopenharmony_ci	conn->conn_state = TARG_CONN_STATE_FREE;
42778c2ecf20Sopenharmony_ci	iscsit_free_conn(conn);
42788c2ecf20Sopenharmony_ci
42798c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
42808c2ecf20Sopenharmony_ci	atomic_dec(&sess->nconn);
42818c2ecf20Sopenharmony_ci	pr_debug("Decremented iSCSI connection count to %hu from node:"
42828c2ecf20Sopenharmony_ci		" %s\n", atomic_read(&sess->nconn),
42838c2ecf20Sopenharmony_ci		sess->sess_ops->InitiatorName);
42848c2ecf20Sopenharmony_ci	/*
42858c2ecf20Sopenharmony_ci	 * Make sure that if one connection fails in an non ERL=2 iSCSI
42868c2ecf20Sopenharmony_ci	 * Session that they all fail.
42878c2ecf20Sopenharmony_ci	 */
42888c2ecf20Sopenharmony_ci	if ((sess->sess_ops->ErrorRecoveryLevel != 2) && !conn_logout &&
42898c2ecf20Sopenharmony_ci	     !atomic_read(&sess->session_logout))
42908c2ecf20Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
42918c2ecf20Sopenharmony_ci
42928c2ecf20Sopenharmony_ci	/*
42938c2ecf20Sopenharmony_ci	 * If this was not the last connection in the session, and we are
42948c2ecf20Sopenharmony_ci	 * performing session reinstatement or falling back to ERL=0, call
42958c2ecf20Sopenharmony_ci	 * iscsit_stop_session() without sleeping to shutdown the other
42968c2ecf20Sopenharmony_ci	 * active connections.
42978c2ecf20Sopenharmony_ci	 */
42988c2ecf20Sopenharmony_ci	if (atomic_read(&sess->nconn)) {
42998c2ecf20Sopenharmony_ci		if (!atomic_read(&sess->session_reinstatement) &&
43008c2ecf20Sopenharmony_ci		    !atomic_read(&sess->session_fall_back_to_erl0)) {
43018c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43028c2ecf20Sopenharmony_ci			return 0;
43038c2ecf20Sopenharmony_ci		}
43048c2ecf20Sopenharmony_ci		if (!atomic_read(&sess->session_stop_active)) {
43058c2ecf20Sopenharmony_ci			atomic_set(&sess->session_stop_active, 1);
43068c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43078c2ecf20Sopenharmony_ci			iscsit_stop_session(sess, 0, 0);
43088c2ecf20Sopenharmony_ci			return 0;
43098c2ecf20Sopenharmony_ci		}
43108c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
43118c2ecf20Sopenharmony_ci		return 0;
43128c2ecf20Sopenharmony_ci	}
43138c2ecf20Sopenharmony_ci
43148c2ecf20Sopenharmony_ci	/*
43158c2ecf20Sopenharmony_ci	 * If this was the last connection in the session and one of the
43168c2ecf20Sopenharmony_ci	 * following is occurring:
43178c2ecf20Sopenharmony_ci	 *
43188c2ecf20Sopenharmony_ci	 * Session Reinstatement is not being performed, and are falling back
43198c2ecf20Sopenharmony_ci	 * to ERL=0 call iscsit_close_session().
43208c2ecf20Sopenharmony_ci	 *
43218c2ecf20Sopenharmony_ci	 * Session Logout was requested.  iscsit_close_session() will be called
43228c2ecf20Sopenharmony_ci	 * elsewhere.
43238c2ecf20Sopenharmony_ci	 *
43248c2ecf20Sopenharmony_ci	 * Session Continuation is not being performed, start the Time2Retain
43258c2ecf20Sopenharmony_ci	 * handler and check if sleep_on_sess_wait_sem is active.
43268c2ecf20Sopenharmony_ci	 */
43278c2ecf20Sopenharmony_ci	if (!atomic_read(&sess->session_reinstatement) &&
43288c2ecf20Sopenharmony_ci	     atomic_read(&sess->session_fall_back_to_erl0)) {
43298c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
43308c2ecf20Sopenharmony_ci		complete_all(&sess->session_wait_comp);
43318c2ecf20Sopenharmony_ci		iscsit_close_session(sess);
43328c2ecf20Sopenharmony_ci
43338c2ecf20Sopenharmony_ci		return 0;
43348c2ecf20Sopenharmony_ci	} else if (atomic_read(&sess->session_logout)) {
43358c2ecf20Sopenharmony_ci		pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
43368c2ecf20Sopenharmony_ci		sess->session_state = TARG_SESS_STATE_FREE;
43378c2ecf20Sopenharmony_ci
43388c2ecf20Sopenharmony_ci		if (atomic_read(&sess->session_close)) {
43398c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43408c2ecf20Sopenharmony_ci			complete_all(&sess->session_wait_comp);
43418c2ecf20Sopenharmony_ci			iscsit_close_session(sess);
43428c2ecf20Sopenharmony_ci		} else {
43438c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43448c2ecf20Sopenharmony_ci		}
43458c2ecf20Sopenharmony_ci
43468c2ecf20Sopenharmony_ci		return 0;
43478c2ecf20Sopenharmony_ci	} else {
43488c2ecf20Sopenharmony_ci		pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
43498c2ecf20Sopenharmony_ci		sess->session_state = TARG_SESS_STATE_FAILED;
43508c2ecf20Sopenharmony_ci
43518c2ecf20Sopenharmony_ci		if (!atomic_read(&sess->session_continuation))
43528c2ecf20Sopenharmony_ci			iscsit_start_time2retain_handler(sess);
43538c2ecf20Sopenharmony_ci
43548c2ecf20Sopenharmony_ci		if (atomic_read(&sess->session_close)) {
43558c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43568c2ecf20Sopenharmony_ci			complete_all(&sess->session_wait_comp);
43578c2ecf20Sopenharmony_ci			iscsit_close_session(sess);
43588c2ecf20Sopenharmony_ci		} else {
43598c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
43608c2ecf20Sopenharmony_ci		}
43618c2ecf20Sopenharmony_ci
43628c2ecf20Sopenharmony_ci		return 0;
43638c2ecf20Sopenharmony_ci	}
43648c2ecf20Sopenharmony_ci}
43658c2ecf20Sopenharmony_ci
43668c2ecf20Sopenharmony_ci/*
43678c2ecf20Sopenharmony_ci * If the iSCSI Session for the iSCSI Initiator Node exists,
43688c2ecf20Sopenharmony_ci * forcefully shutdown the iSCSI NEXUS.
43698c2ecf20Sopenharmony_ci */
43708c2ecf20Sopenharmony_ciint iscsit_close_session(struct iscsi_session *sess)
43718c2ecf20Sopenharmony_ci{
43728c2ecf20Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
43738c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
43748c2ecf20Sopenharmony_ci
43758c2ecf20Sopenharmony_ci	if (atomic_read(&sess->nconn)) {
43768c2ecf20Sopenharmony_ci		pr_err("%d connection(s) still exist for iSCSI session"
43778c2ecf20Sopenharmony_ci			" to %s\n", atomic_read(&sess->nconn),
43788c2ecf20Sopenharmony_ci			sess->sess_ops->InitiatorName);
43798c2ecf20Sopenharmony_ci		BUG();
43808c2ecf20Sopenharmony_ci	}
43818c2ecf20Sopenharmony_ci
43828c2ecf20Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
43838c2ecf20Sopenharmony_ci	atomic_set(&sess->session_logout, 1);
43848c2ecf20Sopenharmony_ci	atomic_set(&sess->session_reinstatement, 1);
43858c2ecf20Sopenharmony_ci	iscsit_stop_time2retain_timer(sess);
43868c2ecf20Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
43878c2ecf20Sopenharmony_ci
43888c2ecf20Sopenharmony_ci	if (sess->sess_ops->ErrorRecoveryLevel == 2)
43898c2ecf20Sopenharmony_ci		iscsit_free_connection_recovery_entries(sess);
43908c2ecf20Sopenharmony_ci
43918c2ecf20Sopenharmony_ci	/*
43928c2ecf20Sopenharmony_ci	 * transport_deregister_session_configfs() will clear the
43938c2ecf20Sopenharmony_ci	 * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context
43948c2ecf20Sopenharmony_ci	 * can be setting it again with __transport_register_session() in
43958c2ecf20Sopenharmony_ci	 * iscsi_post_login_handler() again after the iscsit_stop_session()
43968c2ecf20Sopenharmony_ci	 * completes in iscsi_np context.
43978c2ecf20Sopenharmony_ci	 */
43988c2ecf20Sopenharmony_ci	transport_deregister_session_configfs(sess->se_sess);
43998c2ecf20Sopenharmony_ci
44008c2ecf20Sopenharmony_ci	/*
44018c2ecf20Sopenharmony_ci	 * If any other processes are accessing this session pointer we must
44028c2ecf20Sopenharmony_ci	 * wait until they have completed.  If we are in an interrupt (the
44038c2ecf20Sopenharmony_ci	 * time2retain handler) and contain and active session usage count we
44048c2ecf20Sopenharmony_ci	 * restart the timer and exit.
44058c2ecf20Sopenharmony_ci	 */
44068c2ecf20Sopenharmony_ci	if (!in_interrupt()) {
44078c2ecf20Sopenharmony_ci		iscsit_check_session_usage_count(sess);
44088c2ecf20Sopenharmony_ci	} else {
44098c2ecf20Sopenharmony_ci		if (iscsit_check_session_usage_count(sess) == 2) {
44108c2ecf20Sopenharmony_ci			atomic_set(&sess->session_logout, 0);
44118c2ecf20Sopenharmony_ci			iscsit_start_time2retain_handler(sess);
44128c2ecf20Sopenharmony_ci			return 0;
44138c2ecf20Sopenharmony_ci		}
44148c2ecf20Sopenharmony_ci	}
44158c2ecf20Sopenharmony_ci
44168c2ecf20Sopenharmony_ci	transport_deregister_session(sess->se_sess);
44178c2ecf20Sopenharmony_ci
44188c2ecf20Sopenharmony_ci	iscsit_free_all_ooo_cmdsns(sess);
44198c2ecf20Sopenharmony_ci
44208c2ecf20Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
44218c2ecf20Sopenharmony_ci	pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
44228c2ecf20Sopenharmony_ci	sess->session_state = TARG_SESS_STATE_FREE;
44238c2ecf20Sopenharmony_ci	pr_debug("Released iSCSI session from node: %s\n",
44248c2ecf20Sopenharmony_ci			sess->sess_ops->InitiatorName);
44258c2ecf20Sopenharmony_ci	tpg->nsessions--;
44268c2ecf20Sopenharmony_ci	if (tpg->tpg_tiqn)
44278c2ecf20Sopenharmony_ci		tpg->tpg_tiqn->tiqn_nsessions--;
44288c2ecf20Sopenharmony_ci
44298c2ecf20Sopenharmony_ci	pr_debug("Decremented number of active iSCSI Sessions on"
44308c2ecf20Sopenharmony_ci		" iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions);
44318c2ecf20Sopenharmony_ci
44328c2ecf20Sopenharmony_ci	ida_free(&sess_ida, sess->session_index);
44338c2ecf20Sopenharmony_ci	kfree(sess->sess_ops);
44348c2ecf20Sopenharmony_ci	sess->sess_ops = NULL;
44358c2ecf20Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
44368c2ecf20Sopenharmony_ci
44378c2ecf20Sopenharmony_ci	kfree(sess);
44388c2ecf20Sopenharmony_ci	return 0;
44398c2ecf20Sopenharmony_ci}
44408c2ecf20Sopenharmony_ci
44418c2ecf20Sopenharmony_cistatic void iscsit_logout_post_handler_closesession(
44428c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
44438c2ecf20Sopenharmony_ci{
44448c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
44458c2ecf20Sopenharmony_ci	int sleep = 1;
44468c2ecf20Sopenharmony_ci	/*
44478c2ecf20Sopenharmony_ci	 * Traditional iscsi/tcp will invoke this logic from TX thread
44488c2ecf20Sopenharmony_ci	 * context during session logout, so clear tx_thread_active and
44498c2ecf20Sopenharmony_ci	 * sleep if iscsit_close_connection() has not already occured.
44508c2ecf20Sopenharmony_ci	 *
44518c2ecf20Sopenharmony_ci	 * Since iser-target invokes this logic from it's own workqueue,
44528c2ecf20Sopenharmony_ci	 * always sleep waiting for RX/TX thread shutdown to complete
44538c2ecf20Sopenharmony_ci	 * within iscsit_close_connection().
44548c2ecf20Sopenharmony_ci	 */
44558c2ecf20Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown) {
44568c2ecf20Sopenharmony_ci		sleep = cmpxchg(&conn->tx_thread_active, true, false);
44578c2ecf20Sopenharmony_ci		if (!sleep)
44588c2ecf20Sopenharmony_ci			return;
44598c2ecf20Sopenharmony_ci	}
44608c2ecf20Sopenharmony_ci
44618c2ecf20Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 0);
44628c2ecf20Sopenharmony_ci	complete(&conn->conn_logout_comp);
44638c2ecf20Sopenharmony_ci
44648c2ecf20Sopenharmony_ci	iscsit_dec_conn_usage_count(conn);
44658c2ecf20Sopenharmony_ci	atomic_set(&sess->session_close, 1);
44668c2ecf20Sopenharmony_ci	iscsit_stop_session(sess, sleep, sleep);
44678c2ecf20Sopenharmony_ci	iscsit_dec_session_usage_count(sess);
44688c2ecf20Sopenharmony_ci}
44698c2ecf20Sopenharmony_ci
44708c2ecf20Sopenharmony_cistatic void iscsit_logout_post_handler_samecid(
44718c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
44728c2ecf20Sopenharmony_ci{
44738c2ecf20Sopenharmony_ci	int sleep = 1;
44748c2ecf20Sopenharmony_ci
44758c2ecf20Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown) {
44768c2ecf20Sopenharmony_ci		sleep = cmpxchg(&conn->tx_thread_active, true, false);
44778c2ecf20Sopenharmony_ci		if (!sleep)
44788c2ecf20Sopenharmony_ci			return;
44798c2ecf20Sopenharmony_ci	}
44808c2ecf20Sopenharmony_ci
44818c2ecf20Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 0);
44828c2ecf20Sopenharmony_ci	complete(&conn->conn_logout_comp);
44838c2ecf20Sopenharmony_ci
44848c2ecf20Sopenharmony_ci	iscsit_cause_connection_reinstatement(conn, sleep);
44858c2ecf20Sopenharmony_ci	iscsit_dec_conn_usage_count(conn);
44868c2ecf20Sopenharmony_ci}
44878c2ecf20Sopenharmony_ci
44888c2ecf20Sopenharmony_cistatic void iscsit_logout_post_handler_diffcid(
44898c2ecf20Sopenharmony_ci	struct iscsi_conn *conn,
44908c2ecf20Sopenharmony_ci	u16 cid)
44918c2ecf20Sopenharmony_ci{
44928c2ecf20Sopenharmony_ci	struct iscsi_conn *l_conn;
44938c2ecf20Sopenharmony_ci	struct iscsi_session *sess = conn->sess;
44948c2ecf20Sopenharmony_ci	bool conn_found = false;
44958c2ecf20Sopenharmony_ci
44968c2ecf20Sopenharmony_ci	if (!sess)
44978c2ecf20Sopenharmony_ci		return;
44988c2ecf20Sopenharmony_ci
44998c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
45008c2ecf20Sopenharmony_ci	list_for_each_entry(l_conn, &sess->sess_conn_list, conn_list) {
45018c2ecf20Sopenharmony_ci		if (l_conn->cid == cid) {
45028c2ecf20Sopenharmony_ci			iscsit_inc_conn_usage_count(l_conn);
45038c2ecf20Sopenharmony_ci			conn_found = true;
45048c2ecf20Sopenharmony_ci			break;
45058c2ecf20Sopenharmony_ci		}
45068c2ecf20Sopenharmony_ci	}
45078c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
45088c2ecf20Sopenharmony_ci
45098c2ecf20Sopenharmony_ci	if (!conn_found)
45108c2ecf20Sopenharmony_ci		return;
45118c2ecf20Sopenharmony_ci
45128c2ecf20Sopenharmony_ci	if (l_conn->sock)
45138c2ecf20Sopenharmony_ci		l_conn->sock->ops->shutdown(l_conn->sock, RCV_SHUTDOWN);
45148c2ecf20Sopenharmony_ci
45158c2ecf20Sopenharmony_ci	spin_lock_bh(&l_conn->state_lock);
45168c2ecf20Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
45178c2ecf20Sopenharmony_ci	l_conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
45188c2ecf20Sopenharmony_ci	spin_unlock_bh(&l_conn->state_lock);
45198c2ecf20Sopenharmony_ci
45208c2ecf20Sopenharmony_ci	iscsit_cause_connection_reinstatement(l_conn, 1);
45218c2ecf20Sopenharmony_ci	iscsit_dec_conn_usage_count(l_conn);
45228c2ecf20Sopenharmony_ci}
45238c2ecf20Sopenharmony_ci
45248c2ecf20Sopenharmony_ci/*
45258c2ecf20Sopenharmony_ci *	Return of 0 causes the TX thread to restart.
45268c2ecf20Sopenharmony_ci */
45278c2ecf20Sopenharmony_ciint iscsit_logout_post_handler(
45288c2ecf20Sopenharmony_ci	struct iscsi_cmd *cmd,
45298c2ecf20Sopenharmony_ci	struct iscsi_conn *conn)
45308c2ecf20Sopenharmony_ci{
45318c2ecf20Sopenharmony_ci	int ret = 0;
45328c2ecf20Sopenharmony_ci
45338c2ecf20Sopenharmony_ci	switch (cmd->logout_reason) {
45348c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
45358c2ecf20Sopenharmony_ci		switch (cmd->logout_response) {
45368c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_SUCCESS:
45378c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_CLEANUP_FAILED:
45388c2ecf20Sopenharmony_ci		default:
45398c2ecf20Sopenharmony_ci			iscsit_logout_post_handler_closesession(conn);
45408c2ecf20Sopenharmony_ci			break;
45418c2ecf20Sopenharmony_ci		}
45428c2ecf20Sopenharmony_ci		break;
45438c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
45448c2ecf20Sopenharmony_ci		if (conn->cid == cmd->logout_cid) {
45458c2ecf20Sopenharmony_ci			switch (cmd->logout_response) {
45468c2ecf20Sopenharmony_ci			case ISCSI_LOGOUT_SUCCESS:
45478c2ecf20Sopenharmony_ci			case ISCSI_LOGOUT_CLEANUP_FAILED:
45488c2ecf20Sopenharmony_ci			default:
45498c2ecf20Sopenharmony_ci				iscsit_logout_post_handler_samecid(conn);
45508c2ecf20Sopenharmony_ci				break;
45518c2ecf20Sopenharmony_ci			}
45528c2ecf20Sopenharmony_ci		} else {
45538c2ecf20Sopenharmony_ci			switch (cmd->logout_response) {
45548c2ecf20Sopenharmony_ci			case ISCSI_LOGOUT_SUCCESS:
45558c2ecf20Sopenharmony_ci				iscsit_logout_post_handler_diffcid(conn,
45568c2ecf20Sopenharmony_ci					cmd->logout_cid);
45578c2ecf20Sopenharmony_ci				break;
45588c2ecf20Sopenharmony_ci			case ISCSI_LOGOUT_CID_NOT_FOUND:
45598c2ecf20Sopenharmony_ci			case ISCSI_LOGOUT_CLEANUP_FAILED:
45608c2ecf20Sopenharmony_ci			default:
45618c2ecf20Sopenharmony_ci				break;
45628c2ecf20Sopenharmony_ci			}
45638c2ecf20Sopenharmony_ci			ret = 1;
45648c2ecf20Sopenharmony_ci		}
45658c2ecf20Sopenharmony_ci		break;
45668c2ecf20Sopenharmony_ci	case ISCSI_LOGOUT_REASON_RECOVERY:
45678c2ecf20Sopenharmony_ci		switch (cmd->logout_response) {
45688c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_SUCCESS:
45698c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_CID_NOT_FOUND:
45708c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED:
45718c2ecf20Sopenharmony_ci		case ISCSI_LOGOUT_CLEANUP_FAILED:
45728c2ecf20Sopenharmony_ci		default:
45738c2ecf20Sopenharmony_ci			break;
45748c2ecf20Sopenharmony_ci		}
45758c2ecf20Sopenharmony_ci		ret = 1;
45768c2ecf20Sopenharmony_ci		break;
45778c2ecf20Sopenharmony_ci	default:
45788c2ecf20Sopenharmony_ci		break;
45798c2ecf20Sopenharmony_ci
45808c2ecf20Sopenharmony_ci	}
45818c2ecf20Sopenharmony_ci	return ret;
45828c2ecf20Sopenharmony_ci}
45838c2ecf20Sopenharmony_ciEXPORT_SYMBOL(iscsit_logout_post_handler);
45848c2ecf20Sopenharmony_ci
45858c2ecf20Sopenharmony_civoid iscsit_fail_session(struct iscsi_session *sess)
45868c2ecf20Sopenharmony_ci{
45878c2ecf20Sopenharmony_ci	struct iscsi_conn *conn;
45888c2ecf20Sopenharmony_ci
45898c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
45908c2ecf20Sopenharmony_ci	list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
45918c2ecf20Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
45928c2ecf20Sopenharmony_ci		conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
45938c2ecf20Sopenharmony_ci	}
45948c2ecf20Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
45958c2ecf20Sopenharmony_ci
45968c2ecf20Sopenharmony_ci	pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
45978c2ecf20Sopenharmony_ci	sess->session_state = TARG_SESS_STATE_FAILED;
45988c2ecf20Sopenharmony_ci}
45998c2ecf20Sopenharmony_ci
46008c2ecf20Sopenharmony_civoid iscsit_stop_session(
46018c2ecf20Sopenharmony_ci	struct iscsi_session *sess,
46028c2ecf20Sopenharmony_ci	int session_sleep,
46038c2ecf20Sopenharmony_ci	int connection_sleep)
46048c2ecf20Sopenharmony_ci{
46058c2ecf20Sopenharmony_ci	u16 conn_count = atomic_read(&sess->nconn);
46068c2ecf20Sopenharmony_ci	struct iscsi_conn *conn, *conn_tmp = NULL;
46078c2ecf20Sopenharmony_ci	int is_last;
46088c2ecf20Sopenharmony_ci
46098c2ecf20Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
46108c2ecf20Sopenharmony_ci
46118c2ecf20Sopenharmony_ci	if (connection_sleep) {
46128c2ecf20Sopenharmony_ci		list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
46138c2ecf20Sopenharmony_ci				conn_list) {
46148c2ecf20Sopenharmony_ci			if (conn_count == 0)
46158c2ecf20Sopenharmony_ci				break;
46168c2ecf20Sopenharmony_ci
46178c2ecf20Sopenharmony_ci			if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
46188c2ecf20Sopenharmony_ci				is_last = 1;
46198c2ecf20Sopenharmony_ci			} else {
46208c2ecf20Sopenharmony_ci				iscsit_inc_conn_usage_count(conn_tmp);
46218c2ecf20Sopenharmony_ci				is_last = 0;
46228c2ecf20Sopenharmony_ci			}
46238c2ecf20Sopenharmony_ci			iscsit_inc_conn_usage_count(conn);
46248c2ecf20Sopenharmony_ci
46258c2ecf20Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
46268c2ecf20Sopenharmony_ci			iscsit_cause_connection_reinstatement(conn, 1);
46278c2ecf20Sopenharmony_ci			spin_lock_bh(&sess->conn_lock);
46288c2ecf20Sopenharmony_ci
46298c2ecf20Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
46308c2ecf20Sopenharmony_ci			if (is_last == 0)
46318c2ecf20Sopenharmony_ci				iscsit_dec_conn_usage_count(conn_tmp);
46328c2ecf20Sopenharmony_ci			conn_count--;
46338c2ecf20Sopenharmony_ci		}
46348c2ecf20Sopenharmony_ci	} else {
46358c2ecf20Sopenharmony_ci		list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
46368c2ecf20Sopenharmony_ci			iscsit_cause_connection_reinstatement(conn, 0);
46378c2ecf20Sopenharmony_ci	}
46388c2ecf20Sopenharmony_ci
46398c2ecf20Sopenharmony_ci	if (session_sleep && atomic_read(&sess->nconn)) {
46408c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
46418c2ecf20Sopenharmony_ci		wait_for_completion(&sess->session_wait_comp);
46428c2ecf20Sopenharmony_ci	} else
46438c2ecf20Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
46448c2ecf20Sopenharmony_ci}
46458c2ecf20Sopenharmony_ci
46468c2ecf20Sopenharmony_ciint iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
46478c2ecf20Sopenharmony_ci{
46488c2ecf20Sopenharmony_ci	struct iscsi_session *sess;
46498c2ecf20Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
46508c2ecf20Sopenharmony_ci	struct se_session *se_sess, *se_sess_tmp;
46518c2ecf20Sopenharmony_ci	LIST_HEAD(free_list);
46528c2ecf20Sopenharmony_ci	int session_count = 0;
46538c2ecf20Sopenharmony_ci
46548c2ecf20Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
46558c2ecf20Sopenharmony_ci	if (tpg->nsessions && !force) {
46568c2ecf20Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
46578c2ecf20Sopenharmony_ci		return -1;
46588c2ecf20Sopenharmony_ci	}
46598c2ecf20Sopenharmony_ci
46608c2ecf20Sopenharmony_ci	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
46618c2ecf20Sopenharmony_ci			sess_list) {
46628c2ecf20Sopenharmony_ci		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46638c2ecf20Sopenharmony_ci
46648c2ecf20Sopenharmony_ci		spin_lock(&sess->conn_lock);
46658c2ecf20Sopenharmony_ci		if (atomic_read(&sess->session_fall_back_to_erl0) ||
46668c2ecf20Sopenharmony_ci		    atomic_read(&sess->session_logout) ||
46678c2ecf20Sopenharmony_ci		    atomic_read(&sess->session_close) ||
46688c2ecf20Sopenharmony_ci		    (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
46698c2ecf20Sopenharmony_ci			spin_unlock(&sess->conn_lock);
46708c2ecf20Sopenharmony_ci			continue;
46718c2ecf20Sopenharmony_ci		}
46728c2ecf20Sopenharmony_ci		iscsit_inc_session_usage_count(sess);
46738c2ecf20Sopenharmony_ci		atomic_set(&sess->session_reinstatement, 1);
46748c2ecf20Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
46758c2ecf20Sopenharmony_ci		atomic_set(&sess->session_close, 1);
46768c2ecf20Sopenharmony_ci		spin_unlock(&sess->conn_lock);
46778c2ecf20Sopenharmony_ci
46788c2ecf20Sopenharmony_ci		list_move_tail(&se_sess->sess_list, &free_list);
46798c2ecf20Sopenharmony_ci	}
46808c2ecf20Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
46818c2ecf20Sopenharmony_ci
46828c2ecf20Sopenharmony_ci	list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
46838c2ecf20Sopenharmony_ci		sess = (struct iscsi_session *)se_sess->fabric_sess_ptr;
46848c2ecf20Sopenharmony_ci
46858c2ecf20Sopenharmony_ci		list_del_init(&se_sess->sess_list);
46868c2ecf20Sopenharmony_ci		iscsit_stop_session(sess, 1, 1);
46878c2ecf20Sopenharmony_ci		iscsit_dec_session_usage_count(sess);
46888c2ecf20Sopenharmony_ci		session_count++;
46898c2ecf20Sopenharmony_ci	}
46908c2ecf20Sopenharmony_ci
46918c2ecf20Sopenharmony_ci	pr_debug("Released %d iSCSI Session(s) from Target Portal"
46928c2ecf20Sopenharmony_ci			" Group: %hu\n", session_count, tpg->tpgt);
46938c2ecf20Sopenharmony_ci	return 0;
46948c2ecf20Sopenharmony_ci}
46958c2ecf20Sopenharmony_ci
46968c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iSCSI-Target Driver for mainline target infrastructure");
46978c2ecf20Sopenharmony_ciMODULE_VERSION("4.1.x");
46988c2ecf20Sopenharmony_ciMODULE_AUTHOR("nab@Linux-iSCSI.org");
46998c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
47008c2ecf20Sopenharmony_ci
47018c2ecf20Sopenharmony_cimodule_init(iscsi_target_init_module);
47028c2ecf20Sopenharmony_cimodule_exit(iscsi_target_cleanup_module);
4703