162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*******************************************************************************
362306a36Sopenharmony_ci * This file contains main functions related to the iSCSI Target Core Driver.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (c) Copyright 2007-2013 Datera, Inc.
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci ******************************************************************************/
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <crypto/hash.h>
1262306a36Sopenharmony_ci#include <linux/string.h>
1362306a36Sopenharmony_ci#include <linux/kthread.h>
1462306a36Sopenharmony_ci#include <linux/completion.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/vmalloc.h>
1762306a36Sopenharmony_ci#include <linux/idr.h>
1862306a36Sopenharmony_ci#include <linux/delay.h>
1962306a36Sopenharmony_ci#include <linux/sched/signal.h>
2062306a36Sopenharmony_ci#include <asm/unaligned.h>
2162306a36Sopenharmony_ci#include <linux/inet.h>
2262306a36Sopenharmony_ci#include <net/ipv6.h>
2362306a36Sopenharmony_ci#include <scsi/scsi_proto.h>
2462306a36Sopenharmony_ci#include <scsi/iscsi_proto.h>
2562306a36Sopenharmony_ci#include <scsi/scsi_tcq.h>
2662306a36Sopenharmony_ci#include <target/target_core_base.h>
2762306a36Sopenharmony_ci#include <target/target_core_fabric.h>
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#include <target/target_core_backend.h>
3062306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_core.h>
3162306a36Sopenharmony_ci#include "iscsi_target_parameters.h"
3262306a36Sopenharmony_ci#include "iscsi_target_seq_pdu_list.h"
3362306a36Sopenharmony_ci#include "iscsi_target_datain_values.h"
3462306a36Sopenharmony_ci#include "iscsi_target_erl0.h"
3562306a36Sopenharmony_ci#include "iscsi_target_erl1.h"
3662306a36Sopenharmony_ci#include "iscsi_target_erl2.h"
3762306a36Sopenharmony_ci#include "iscsi_target_login.h"
3862306a36Sopenharmony_ci#include "iscsi_target_tmr.h"
3962306a36Sopenharmony_ci#include "iscsi_target_tpg.h"
4062306a36Sopenharmony_ci#include "iscsi_target_util.h"
4162306a36Sopenharmony_ci#include "iscsi_target.h"
4262306a36Sopenharmony_ci#include "iscsi_target_device.h"
4362306a36Sopenharmony_ci#include <target/iscsi/iscsi_target_stat.h>
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci#include <target/iscsi/iscsi_transport.h>
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic LIST_HEAD(g_tiqn_list);
4862306a36Sopenharmony_cistatic LIST_HEAD(g_np_list);
4962306a36Sopenharmony_cistatic DEFINE_SPINLOCK(tiqn_lock);
5062306a36Sopenharmony_cistatic DEFINE_MUTEX(np_lock);
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_cistatic struct idr tiqn_idr;
5362306a36Sopenharmony_ciDEFINE_IDA(sess_ida);
5462306a36Sopenharmony_cistruct mutex auth_id_lock;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistruct iscsit_global *iscsit_global;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistruct kmem_cache *lio_qr_cache;
5962306a36Sopenharmony_cistruct kmem_cache *lio_dr_cache;
6062306a36Sopenharmony_cistruct kmem_cache *lio_ooo_cache;
6162306a36Sopenharmony_cistruct kmem_cache *lio_r2t_cache;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int iscsit_handle_immediate_data(struct iscsit_cmd *,
6462306a36Sopenharmony_ci			struct iscsi_scsi_req *, u32);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistruct iscsi_tiqn *iscsit_get_tiqn_for_login(unsigned char *buf)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn = NULL;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	spin_lock(&tiqn_lock);
7162306a36Sopenharmony_ci	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
7262306a36Sopenharmony_ci		if (!strcmp(tiqn->tiqn, buf)) {
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci			spin_lock(&tiqn->tiqn_state_lock);
7562306a36Sopenharmony_ci			if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
7662306a36Sopenharmony_ci				tiqn->tiqn_access_count++;
7762306a36Sopenharmony_ci				spin_unlock(&tiqn->tiqn_state_lock);
7862306a36Sopenharmony_ci				spin_unlock(&tiqn_lock);
7962306a36Sopenharmony_ci				return tiqn;
8062306a36Sopenharmony_ci			}
8162306a36Sopenharmony_ci			spin_unlock(&tiqn->tiqn_state_lock);
8262306a36Sopenharmony_ci		}
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	spin_unlock(&tiqn_lock);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	return NULL;
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic int iscsit_set_tiqn_shutdown(struct iscsi_tiqn *tiqn)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
9262306a36Sopenharmony_ci	if (tiqn->tiqn_state == TIQN_STATE_ACTIVE) {
9362306a36Sopenharmony_ci		tiqn->tiqn_state = TIQN_STATE_SHUTDOWN;
9462306a36Sopenharmony_ci		spin_unlock(&tiqn->tiqn_state_lock);
9562306a36Sopenharmony_ci		return 0;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	return -1;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_civoid iscsit_put_tiqn_for_login(struct iscsi_tiqn *tiqn)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
10562306a36Sopenharmony_ci	tiqn->tiqn_access_count--;
10662306a36Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Note that IQN formatting is expected to be done in userspace, and
11162306a36Sopenharmony_ci * no explict IQN format checks are done here.
11262306a36Sopenharmony_ci */
11362306a36Sopenharmony_cistruct iscsi_tiqn *iscsit_add_tiqn(unsigned char *buf)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn = NULL;
11662306a36Sopenharmony_ci	int ret;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	if (strlen(buf) >= ISCSI_IQN_LEN) {
11962306a36Sopenharmony_ci		pr_err("Target IQN exceeds %d bytes\n",
12062306a36Sopenharmony_ci				ISCSI_IQN_LEN);
12162306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
12262306a36Sopenharmony_ci	}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	tiqn = kzalloc(sizeof(*tiqn), GFP_KERNEL);
12562306a36Sopenharmony_ci	if (!tiqn)
12662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	sprintf(tiqn->tiqn, "%s", buf);
12962306a36Sopenharmony_ci	INIT_LIST_HEAD(&tiqn->tiqn_list);
13062306a36Sopenharmony_ci	INIT_LIST_HEAD(&tiqn->tiqn_tpg_list);
13162306a36Sopenharmony_ci	spin_lock_init(&tiqn->tiqn_state_lock);
13262306a36Sopenharmony_ci	spin_lock_init(&tiqn->tiqn_tpg_lock);
13362306a36Sopenharmony_ci	spin_lock_init(&tiqn->sess_err_stats.lock);
13462306a36Sopenharmony_ci	spin_lock_init(&tiqn->login_stats.lock);
13562306a36Sopenharmony_ci	spin_lock_init(&tiqn->logout_stats.lock);
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	tiqn->tiqn_state = TIQN_STATE_ACTIVE;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	idr_preload(GFP_KERNEL);
14062306a36Sopenharmony_ci	spin_lock(&tiqn_lock);
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	ret = idr_alloc(&tiqn_idr, NULL, 0, 0, GFP_NOWAIT);
14362306a36Sopenharmony_ci	if (ret < 0) {
14462306a36Sopenharmony_ci		pr_err("idr_alloc() failed for tiqn->tiqn_index\n");
14562306a36Sopenharmony_ci		spin_unlock(&tiqn_lock);
14662306a36Sopenharmony_ci		idr_preload_end();
14762306a36Sopenharmony_ci		kfree(tiqn);
14862306a36Sopenharmony_ci		return ERR_PTR(ret);
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci	tiqn->tiqn_index = ret;
15162306a36Sopenharmony_ci	list_add_tail(&tiqn->tiqn_list, &g_tiqn_list);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	spin_unlock(&tiqn_lock);
15462306a36Sopenharmony_ci	idr_preload_end();
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	pr_debug("CORE[0] - Added iSCSI Target IQN: %s\n", tiqn->tiqn);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return tiqn;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic void iscsit_wait_for_tiqn(struct iscsi_tiqn *tiqn)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	/*
16562306a36Sopenharmony_ci	 * Wait for accesses to said struct iscsi_tiqn to end.
16662306a36Sopenharmony_ci	 */
16762306a36Sopenharmony_ci	spin_lock(&tiqn->tiqn_state_lock);
16862306a36Sopenharmony_ci	while (tiqn->tiqn_access_count != 0) {
16962306a36Sopenharmony_ci		spin_unlock(&tiqn->tiqn_state_lock);
17062306a36Sopenharmony_ci		msleep(10);
17162306a36Sopenharmony_ci		spin_lock(&tiqn->tiqn_state_lock);
17262306a36Sopenharmony_ci	}
17362306a36Sopenharmony_ci	spin_unlock(&tiqn->tiqn_state_lock);
17462306a36Sopenharmony_ci}
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_civoid iscsit_del_tiqn(struct iscsi_tiqn *tiqn)
17762306a36Sopenharmony_ci{
17862306a36Sopenharmony_ci	/*
17962306a36Sopenharmony_ci	 * iscsit_set_tiqn_shutdown sets tiqn->tiqn_state = TIQN_STATE_SHUTDOWN
18062306a36Sopenharmony_ci	 * while holding tiqn->tiqn_state_lock.  This means that all subsequent
18162306a36Sopenharmony_ci	 * attempts to access this struct iscsi_tiqn will fail from both transport
18262306a36Sopenharmony_ci	 * fabric and control code paths.
18362306a36Sopenharmony_ci	 */
18462306a36Sopenharmony_ci	if (iscsit_set_tiqn_shutdown(tiqn) < 0) {
18562306a36Sopenharmony_ci		pr_err("iscsit_set_tiqn_shutdown() failed\n");
18662306a36Sopenharmony_ci		return;
18762306a36Sopenharmony_ci	}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	iscsit_wait_for_tiqn(tiqn);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	spin_lock(&tiqn_lock);
19262306a36Sopenharmony_ci	list_del(&tiqn->tiqn_list);
19362306a36Sopenharmony_ci	idr_remove(&tiqn_idr, tiqn->tiqn_index);
19462306a36Sopenharmony_ci	spin_unlock(&tiqn_lock);
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	pr_debug("CORE[0] - Deleted iSCSI Target IQN: %s\n",
19762306a36Sopenharmony_ci			tiqn->tiqn);
19862306a36Sopenharmony_ci	kfree(tiqn);
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ciint iscsit_access_np(struct iscsi_np *np, struct iscsi_portal_group *tpg)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	int ret;
20462306a36Sopenharmony_ci	/*
20562306a36Sopenharmony_ci	 * Determine if the network portal is accepting storage traffic.
20662306a36Sopenharmony_ci	 */
20762306a36Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
20862306a36Sopenharmony_ci	if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
20962306a36Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
21062306a36Sopenharmony_ci		return -1;
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
21362306a36Sopenharmony_ci	/*
21462306a36Sopenharmony_ci	 * Determine if the portal group is accepting storage traffic.
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	spin_lock_bh(&tpg->tpg_state_lock);
21762306a36Sopenharmony_ci	if (tpg->tpg_state != TPG_STATE_ACTIVE) {
21862306a36Sopenharmony_ci		spin_unlock_bh(&tpg->tpg_state_lock);
21962306a36Sopenharmony_ci		return -1;
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci	spin_unlock_bh(&tpg->tpg_state_lock);
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	/*
22462306a36Sopenharmony_ci	 * Here we serialize access across the TIQN+TPG Tuple.
22562306a36Sopenharmony_ci	 */
22662306a36Sopenharmony_ci	ret = down_interruptible(&tpg->np_login_sem);
22762306a36Sopenharmony_ci	if (ret != 0)
22862306a36Sopenharmony_ci		return -1;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spin_lock_bh(&tpg->tpg_state_lock);
23162306a36Sopenharmony_ci	if (tpg->tpg_state != TPG_STATE_ACTIVE) {
23262306a36Sopenharmony_ci		spin_unlock_bh(&tpg->tpg_state_lock);
23362306a36Sopenharmony_ci		up(&tpg->np_login_sem);
23462306a36Sopenharmony_ci		return -1;
23562306a36Sopenharmony_ci	}
23662306a36Sopenharmony_ci	spin_unlock_bh(&tpg->tpg_state_lock);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	return 0;
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_civoid iscsit_login_kref_put(struct kref *kref)
24262306a36Sopenharmony_ci{
24362306a36Sopenharmony_ci	struct iscsi_tpg_np *tpg_np = container_of(kref,
24462306a36Sopenharmony_ci				struct iscsi_tpg_np, tpg_np_kref);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	complete(&tpg_np->tpg_np_comp);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ciint iscsit_deaccess_np(struct iscsi_np *np, struct iscsi_portal_group *tpg,
25062306a36Sopenharmony_ci		       struct iscsi_tpg_np *tpg_np)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn = tpg->tpg_tiqn;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	up(&tpg->np_login_sem);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	if (tpg_np)
25762306a36Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	if (tiqn)
26062306a36Sopenharmony_ci		iscsit_put_tiqn_for_login(tiqn);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cibool iscsit_check_np_match(
26662306a36Sopenharmony_ci	struct sockaddr_storage *sockaddr,
26762306a36Sopenharmony_ci	struct iscsi_np *np,
26862306a36Sopenharmony_ci	int network_transport)
26962306a36Sopenharmony_ci{
27062306a36Sopenharmony_ci	struct sockaddr_in *sock_in, *sock_in_e;
27162306a36Sopenharmony_ci	struct sockaddr_in6 *sock_in6, *sock_in6_e;
27262306a36Sopenharmony_ci	bool ip_match = false;
27362306a36Sopenharmony_ci	u16 port, port_e;
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_ci	if (sockaddr->ss_family == AF_INET6) {
27662306a36Sopenharmony_ci		sock_in6 = (struct sockaddr_in6 *)sockaddr;
27762306a36Sopenharmony_ci		sock_in6_e = (struct sockaddr_in6 *)&np->np_sockaddr;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci		if (!memcmp(&sock_in6->sin6_addr.in6_u,
28062306a36Sopenharmony_ci			    &sock_in6_e->sin6_addr.in6_u,
28162306a36Sopenharmony_ci			    sizeof(struct in6_addr)))
28262306a36Sopenharmony_ci			ip_match = true;
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci		port = ntohs(sock_in6->sin6_port);
28562306a36Sopenharmony_ci		port_e = ntohs(sock_in6_e->sin6_port);
28662306a36Sopenharmony_ci	} else {
28762306a36Sopenharmony_ci		sock_in = (struct sockaddr_in *)sockaddr;
28862306a36Sopenharmony_ci		sock_in_e = (struct sockaddr_in *)&np->np_sockaddr;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci		if (sock_in->sin_addr.s_addr == sock_in_e->sin_addr.s_addr)
29162306a36Sopenharmony_ci			ip_match = true;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci		port = ntohs(sock_in->sin_port);
29462306a36Sopenharmony_ci		port_e = ntohs(sock_in_e->sin_port);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (ip_match && (port_e == port) &&
29862306a36Sopenharmony_ci	    (np->np_network_transport == network_transport))
29962306a36Sopenharmony_ci		return true;
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return false;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_cistatic struct iscsi_np *iscsit_get_np(
30562306a36Sopenharmony_ci	struct sockaddr_storage *sockaddr,
30662306a36Sopenharmony_ci	int network_transport)
30762306a36Sopenharmony_ci{
30862306a36Sopenharmony_ci	struct iscsi_np *np;
30962306a36Sopenharmony_ci	bool match;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	lockdep_assert_held(&np_lock);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	list_for_each_entry(np, &g_np_list, np_list) {
31462306a36Sopenharmony_ci		spin_lock_bh(&np->np_thread_lock);
31562306a36Sopenharmony_ci		if (np->np_thread_state != ISCSI_NP_THREAD_ACTIVE) {
31662306a36Sopenharmony_ci			spin_unlock_bh(&np->np_thread_lock);
31762306a36Sopenharmony_ci			continue;
31862306a36Sopenharmony_ci		}
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci		match = iscsit_check_np_match(sockaddr, np, network_transport);
32162306a36Sopenharmony_ci		if (match) {
32262306a36Sopenharmony_ci			/*
32362306a36Sopenharmony_ci			 * Increment the np_exports reference count now to
32462306a36Sopenharmony_ci			 * prevent iscsit_del_np() below from being called
32562306a36Sopenharmony_ci			 * while iscsi_tpg_add_network_portal() is called.
32662306a36Sopenharmony_ci			 */
32762306a36Sopenharmony_ci			np->np_exports++;
32862306a36Sopenharmony_ci			spin_unlock_bh(&np->np_thread_lock);
32962306a36Sopenharmony_ci			return np;
33062306a36Sopenharmony_ci		}
33162306a36Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
33262306a36Sopenharmony_ci	}
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	return NULL;
33562306a36Sopenharmony_ci}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_cistruct iscsi_np *iscsit_add_np(
33862306a36Sopenharmony_ci	struct sockaddr_storage *sockaddr,
33962306a36Sopenharmony_ci	int network_transport)
34062306a36Sopenharmony_ci{
34162306a36Sopenharmony_ci	struct iscsi_np *np;
34262306a36Sopenharmony_ci	int ret;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	mutex_lock(&np_lock);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	/*
34762306a36Sopenharmony_ci	 * Locate the existing struct iscsi_np if already active..
34862306a36Sopenharmony_ci	 */
34962306a36Sopenharmony_ci	np = iscsit_get_np(sockaddr, network_transport);
35062306a36Sopenharmony_ci	if (np) {
35162306a36Sopenharmony_ci		mutex_unlock(&np_lock);
35262306a36Sopenharmony_ci		return np;
35362306a36Sopenharmony_ci	}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	np = kzalloc(sizeof(*np), GFP_KERNEL);
35662306a36Sopenharmony_ci	if (!np) {
35762306a36Sopenharmony_ci		mutex_unlock(&np_lock);
35862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	np->np_flags |= NPF_IP_NETWORK;
36262306a36Sopenharmony_ci	np->np_network_transport = network_transport;
36362306a36Sopenharmony_ci	spin_lock_init(&np->np_thread_lock);
36462306a36Sopenharmony_ci	init_completion(&np->np_restart_comp);
36562306a36Sopenharmony_ci	INIT_LIST_HEAD(&np->np_list);
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	ret = iscsi_target_setup_login_socket(np, sockaddr);
36862306a36Sopenharmony_ci	if (ret != 0) {
36962306a36Sopenharmony_ci		kfree(np);
37062306a36Sopenharmony_ci		mutex_unlock(&np_lock);
37162306a36Sopenharmony_ci		return ERR_PTR(ret);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	np->np_thread = kthread_run(iscsi_target_login_thread, np, "iscsi_np");
37562306a36Sopenharmony_ci	if (IS_ERR(np->np_thread)) {
37662306a36Sopenharmony_ci		pr_err("Unable to create kthread: iscsi_np\n");
37762306a36Sopenharmony_ci		ret = PTR_ERR(np->np_thread);
37862306a36Sopenharmony_ci		kfree(np);
37962306a36Sopenharmony_ci		mutex_unlock(&np_lock);
38062306a36Sopenharmony_ci		return ERR_PTR(ret);
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci	/*
38362306a36Sopenharmony_ci	 * Increment the np_exports reference count now to prevent
38462306a36Sopenharmony_ci	 * iscsit_del_np() below from being run while a new call to
38562306a36Sopenharmony_ci	 * iscsi_tpg_add_network_portal() for a matching iscsi_np is
38662306a36Sopenharmony_ci	 * active.  We don't need to hold np->np_thread_lock at this
38762306a36Sopenharmony_ci	 * point because iscsi_np has not been added to g_np_list yet.
38862306a36Sopenharmony_ci	 */
38962306a36Sopenharmony_ci	np->np_exports = 1;
39062306a36Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_ACTIVE;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	list_add_tail(&np->np_list, &g_np_list);
39362306a36Sopenharmony_ci	mutex_unlock(&np_lock);
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	pr_debug("CORE[0] - Added Network Portal: %pISpc on %s\n",
39662306a36Sopenharmony_ci		&np->np_sockaddr, np->np_transport->name);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	return np;
39962306a36Sopenharmony_ci}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ciint iscsit_reset_np_thread(
40262306a36Sopenharmony_ci	struct iscsi_np *np,
40362306a36Sopenharmony_ci	struct iscsi_tpg_np *tpg_np,
40462306a36Sopenharmony_ci	struct iscsi_portal_group *tpg,
40562306a36Sopenharmony_ci	bool shutdown)
40662306a36Sopenharmony_ci{
40762306a36Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
40862306a36Sopenharmony_ci	if (np->np_thread_state == ISCSI_NP_THREAD_INACTIVE) {
40962306a36Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
41062306a36Sopenharmony_ci		return 0;
41162306a36Sopenharmony_ci	}
41262306a36Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_RESET;
41362306a36Sopenharmony_ci	atomic_inc(&np->np_reset_count);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci	if (np->np_thread) {
41662306a36Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
41762306a36Sopenharmony_ci		send_sig(SIGINT, np->np_thread, 1);
41862306a36Sopenharmony_ci		wait_for_completion(&np->np_restart_comp);
41962306a36Sopenharmony_ci		spin_lock_bh(&np->np_thread_lock);
42062306a36Sopenharmony_ci	}
42162306a36Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	if (tpg_np && shutdown) {
42462306a36Sopenharmony_ci		kref_put(&tpg_np->tpg_np_kref, iscsit_login_kref_put);
42562306a36Sopenharmony_ci
42662306a36Sopenharmony_ci		wait_for_completion(&tpg_np->tpg_np_comp);
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	return 0;
43062306a36Sopenharmony_ci}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_cistatic void iscsit_free_np(struct iscsi_np *np)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	if (np->np_socket)
43562306a36Sopenharmony_ci		sock_release(np->np_socket);
43662306a36Sopenharmony_ci}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ciint iscsit_del_np(struct iscsi_np *np)
43962306a36Sopenharmony_ci{
44062306a36Sopenharmony_ci	spin_lock_bh(&np->np_thread_lock);
44162306a36Sopenharmony_ci	np->np_exports--;
44262306a36Sopenharmony_ci	if (np->np_exports) {
44362306a36Sopenharmony_ci		np->enabled = true;
44462306a36Sopenharmony_ci		spin_unlock_bh(&np->np_thread_lock);
44562306a36Sopenharmony_ci		return 0;
44662306a36Sopenharmony_ci	}
44762306a36Sopenharmony_ci	np->np_thread_state = ISCSI_NP_THREAD_SHUTDOWN;
44862306a36Sopenharmony_ci	spin_unlock_bh(&np->np_thread_lock);
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	if (np->np_thread) {
45162306a36Sopenharmony_ci		/*
45262306a36Sopenharmony_ci		 * We need to send the signal to wakeup Linux/Net
45362306a36Sopenharmony_ci		 * which may be sleeping in sock_accept()..
45462306a36Sopenharmony_ci		 */
45562306a36Sopenharmony_ci		send_sig(SIGINT, np->np_thread, 1);
45662306a36Sopenharmony_ci		kthread_stop(np->np_thread);
45762306a36Sopenharmony_ci		np->np_thread = NULL;
45862306a36Sopenharmony_ci	}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	np->np_transport->iscsit_free_np(np);
46162306a36Sopenharmony_ci
46262306a36Sopenharmony_ci	mutex_lock(&np_lock);
46362306a36Sopenharmony_ci	list_del(&np->np_list);
46462306a36Sopenharmony_ci	mutex_unlock(&np_lock);
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci	pr_debug("CORE[0] - Removed Network Portal: %pISpc on %s\n",
46762306a36Sopenharmony_ci		&np->np_sockaddr, np->np_transport->name);
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	iscsit_put_transport(np->np_transport);
47062306a36Sopenharmony_ci	kfree(np);
47162306a36Sopenharmony_ci	return 0;
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_cistatic void iscsit_get_rx_pdu(struct iscsit_conn *);
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ciint iscsit_queue_rsp(struct iscsit_conn *conn, struct iscsit_cmd *cmd)
47762306a36Sopenharmony_ci{
47862306a36Sopenharmony_ci	return iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state);
47962306a36Sopenharmony_ci}
48062306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_queue_rsp);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_civoid iscsit_aborted_task(struct iscsit_conn *conn, struct iscsit_cmd *cmd)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
48562306a36Sopenharmony_ci	if (!list_empty(&cmd->i_conn_node))
48662306a36Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
48762306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	__iscsit_free_cmd(cmd, true);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_aborted_task);
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_cistatic void iscsit_do_crypto_hash_buf(struct ahash_request *, const void *,
49462306a36Sopenharmony_ci				      u32, u32, const void *, void *);
49562306a36Sopenharmony_cistatic void iscsit_tx_thread_wait_for_tcp(struct iscsit_conn *);
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_cistatic int
49862306a36Sopenharmony_ciiscsit_xmit_nondatain_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
49962306a36Sopenharmony_ci			  const void *data_buf, u32 data_buf_len)
50062306a36Sopenharmony_ci{
50162306a36Sopenharmony_ci	struct iscsi_hdr *hdr = (struct iscsi_hdr *)cmd->pdu;
50262306a36Sopenharmony_ci	struct kvec *iov;
50362306a36Sopenharmony_ci	u32 niov = 0, tx_size = ISCSI_HDR_LEN;
50462306a36Sopenharmony_ci	int ret;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	iov = &cmd->iov_misc[0];
50762306a36Sopenharmony_ci	iov[niov].iov_base	= cmd->pdu;
50862306a36Sopenharmony_ci	iov[niov++].iov_len	= ISCSI_HDR_LEN;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	if (conn->conn_ops->HeaderDigest) {
51162306a36Sopenharmony_ci		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci		iscsit_do_crypto_hash_buf(conn->conn_tx_hash, hdr,
51462306a36Sopenharmony_ci					  ISCSI_HDR_LEN, 0, NULL,
51562306a36Sopenharmony_ci					  header_digest);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci		iov[0].iov_len += ISCSI_CRC_LEN;
51862306a36Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
51962306a36Sopenharmony_ci		pr_debug("Attaching CRC32C HeaderDigest"
52062306a36Sopenharmony_ci			 " to opcode 0x%x 0x%08x\n",
52162306a36Sopenharmony_ci			 hdr->opcode, *header_digest);
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (data_buf_len) {
52562306a36Sopenharmony_ci		u32 padding = ((-data_buf_len) & 3);
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci		iov[niov].iov_base	= (void *)data_buf;
52862306a36Sopenharmony_ci		iov[niov++].iov_len	= data_buf_len;
52962306a36Sopenharmony_ci		tx_size += data_buf_len;
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci		if (padding != 0) {
53262306a36Sopenharmony_ci			iov[niov].iov_base = &cmd->pad_bytes;
53362306a36Sopenharmony_ci			iov[niov++].iov_len = padding;
53462306a36Sopenharmony_ci			tx_size += padding;
53562306a36Sopenharmony_ci			pr_debug("Attaching %u additional"
53662306a36Sopenharmony_ci				 " padding bytes.\n", padding);
53762306a36Sopenharmony_ci		}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
54062306a36Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_tx_hash,
54162306a36Sopenharmony_ci						  data_buf, data_buf_len,
54262306a36Sopenharmony_ci						  padding, &cmd->pad_bytes,
54362306a36Sopenharmony_ci						  &cmd->data_crc);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci			iov[niov].iov_base = &cmd->data_crc;
54662306a36Sopenharmony_ci			iov[niov++].iov_len = ISCSI_CRC_LEN;
54762306a36Sopenharmony_ci			tx_size += ISCSI_CRC_LEN;
54862306a36Sopenharmony_ci			pr_debug("Attached DataDigest for %u"
54962306a36Sopenharmony_ci				 " bytes opcode 0x%x, CRC 0x%08x\n",
55062306a36Sopenharmony_ci				 data_buf_len, hdr->opcode, cmd->data_crc);
55162306a36Sopenharmony_ci		}
55262306a36Sopenharmony_ci	}
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	cmd->iov_misc_count = niov;
55562306a36Sopenharmony_ci	cmd->tx_size = tx_size;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	ret = iscsit_send_tx_data(cmd, conn, 1);
55862306a36Sopenharmony_ci	if (ret < 0) {
55962306a36Sopenharmony_ci		iscsit_tx_thread_wait_for_tcp(conn);
56062306a36Sopenharmony_ci		return ret;
56162306a36Sopenharmony_ci	}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	return 0;
56462306a36Sopenharmony_ci}
56562306a36Sopenharmony_ci
56662306a36Sopenharmony_cistatic int iscsit_map_iovec(struct iscsit_cmd *cmd, struct kvec *iov, int nvec,
56762306a36Sopenharmony_ci			    u32 data_offset, u32 data_length);
56862306a36Sopenharmony_cistatic void iscsit_unmap_iovec(struct iscsit_cmd *);
56962306a36Sopenharmony_cistatic u32 iscsit_do_crypto_hash_sg(struct ahash_request *, struct iscsit_cmd *,
57062306a36Sopenharmony_ci				    u32, u32, u32, u8 *);
57162306a36Sopenharmony_cistatic int
57262306a36Sopenharmony_ciiscsit_xmit_datain_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
57362306a36Sopenharmony_ci		       const struct iscsi_datain *datain)
57462306a36Sopenharmony_ci{
57562306a36Sopenharmony_ci	struct kvec *iov;
57662306a36Sopenharmony_ci	u32 iov_count = 0, tx_size = 0;
57762306a36Sopenharmony_ci	int ret, iov_ret;
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	iov = &cmd->iov_data[0];
58062306a36Sopenharmony_ci	iov[iov_count].iov_base	= cmd->pdu;
58162306a36Sopenharmony_ci	iov[iov_count++].iov_len = ISCSI_HDR_LEN;
58262306a36Sopenharmony_ci	tx_size += ISCSI_HDR_LEN;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci	if (conn->conn_ops->HeaderDigest) {
58562306a36Sopenharmony_ci		u32 *header_digest = (u32 *)&cmd->pdu[ISCSI_HDR_LEN];
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci		iscsit_do_crypto_hash_buf(conn->conn_tx_hash, cmd->pdu,
58862306a36Sopenharmony_ci					  ISCSI_HDR_LEN, 0, NULL,
58962306a36Sopenharmony_ci					  header_digest);
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_ci		iov[0].iov_len += ISCSI_CRC_LEN;
59262306a36Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		pr_debug("Attaching CRC32 HeaderDigest for DataIN PDU 0x%08x\n",
59562306a36Sopenharmony_ci			 *header_digest);
59662306a36Sopenharmony_ci	}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, &cmd->iov_data[iov_count],
59962306a36Sopenharmony_ci				   cmd->orig_iov_data_count - (iov_count + 2),
60062306a36Sopenharmony_ci				   datain->offset, datain->length);
60162306a36Sopenharmony_ci	if (iov_ret < 0)
60262306a36Sopenharmony_ci		return -1;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	iov_count += iov_ret;
60562306a36Sopenharmony_ci	tx_size += datain->length;
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci	cmd->padding = ((-datain->length) & 3);
60862306a36Sopenharmony_ci	if (cmd->padding) {
60962306a36Sopenharmony_ci		iov[iov_count].iov_base		= cmd->pad_bytes;
61062306a36Sopenharmony_ci		iov[iov_count++].iov_len	= cmd->padding;
61162306a36Sopenharmony_ci		tx_size += cmd->padding;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci		pr_debug("Attaching %u padding bytes\n", cmd->padding);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
61762306a36Sopenharmony_ci		cmd->data_crc = iscsit_do_crypto_hash_sg(conn->conn_tx_hash,
61862306a36Sopenharmony_ci							 cmd, datain->offset,
61962306a36Sopenharmony_ci							 datain->length,
62062306a36Sopenharmony_ci							 cmd->padding,
62162306a36Sopenharmony_ci							 cmd->pad_bytes);
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci		iov[iov_count].iov_base	= &cmd->data_crc;
62462306a36Sopenharmony_ci		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
62562306a36Sopenharmony_ci		tx_size += ISCSI_CRC_LEN;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci		pr_debug("Attached CRC32C DataDigest %d bytes, crc 0x%08x\n",
62862306a36Sopenharmony_ci			 datain->length + cmd->padding, cmd->data_crc);
62962306a36Sopenharmony_ci	}
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	cmd->iov_data_count = iov_count;
63262306a36Sopenharmony_ci	cmd->tx_size = tx_size;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	ret = iscsit_fe_sendpage_sg(cmd, conn);
63562306a36Sopenharmony_ci
63662306a36Sopenharmony_ci	iscsit_unmap_iovec(cmd);
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	if (ret < 0) {
63962306a36Sopenharmony_ci		iscsit_tx_thread_wait_for_tcp(conn);
64062306a36Sopenharmony_ci		return ret;
64162306a36Sopenharmony_ci	}
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	return 0;
64462306a36Sopenharmony_ci}
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_cistatic int iscsit_xmit_pdu(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
64762306a36Sopenharmony_ci			   struct iscsi_datain_req *dr, const void *buf,
64862306a36Sopenharmony_ci			   u32 buf_len)
64962306a36Sopenharmony_ci{
65062306a36Sopenharmony_ci	if (dr)
65162306a36Sopenharmony_ci		return iscsit_xmit_datain_pdu(conn, cmd, buf);
65262306a36Sopenharmony_ci	else
65362306a36Sopenharmony_ci		return iscsit_xmit_nondatain_pdu(conn, cmd, buf, buf_len);
65462306a36Sopenharmony_ci}
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_cistatic enum target_prot_op iscsit_get_sup_prot_ops(struct iscsit_conn *conn)
65762306a36Sopenharmony_ci{
65862306a36Sopenharmony_ci	return TARGET_PROT_NORMAL;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_cistatic struct iscsit_transport iscsi_target_transport = {
66262306a36Sopenharmony_ci	.name			= "iSCSI/TCP",
66362306a36Sopenharmony_ci	.transport_type		= ISCSI_TCP,
66462306a36Sopenharmony_ci	.rdma_shutdown		= false,
66562306a36Sopenharmony_ci	.owner			= NULL,
66662306a36Sopenharmony_ci	.iscsit_setup_np	= iscsit_setup_np,
66762306a36Sopenharmony_ci	.iscsit_accept_np	= iscsit_accept_np,
66862306a36Sopenharmony_ci	.iscsit_free_np		= iscsit_free_np,
66962306a36Sopenharmony_ci	.iscsit_get_login_rx	= iscsit_get_login_rx,
67062306a36Sopenharmony_ci	.iscsit_put_login_tx	= iscsit_put_login_tx,
67162306a36Sopenharmony_ci	.iscsit_get_dataout	= iscsit_build_r2ts_for_cmd,
67262306a36Sopenharmony_ci	.iscsit_immediate_queue	= iscsit_immediate_queue,
67362306a36Sopenharmony_ci	.iscsit_response_queue	= iscsit_response_queue,
67462306a36Sopenharmony_ci	.iscsit_queue_data_in	= iscsit_queue_rsp,
67562306a36Sopenharmony_ci	.iscsit_queue_status	= iscsit_queue_rsp,
67662306a36Sopenharmony_ci	.iscsit_aborted_task	= iscsit_aborted_task,
67762306a36Sopenharmony_ci	.iscsit_xmit_pdu	= iscsit_xmit_pdu,
67862306a36Sopenharmony_ci	.iscsit_get_rx_pdu	= iscsit_get_rx_pdu,
67962306a36Sopenharmony_ci	.iscsit_get_sup_prot_ops = iscsit_get_sup_prot_ops,
68062306a36Sopenharmony_ci};
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_cistatic int __init iscsi_target_init_module(void)
68362306a36Sopenharmony_ci{
68462306a36Sopenharmony_ci	int ret = 0, size;
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci	pr_debug("iSCSI-Target "ISCSIT_VERSION"\n");
68762306a36Sopenharmony_ci	iscsit_global = kzalloc(sizeof(*iscsit_global), GFP_KERNEL);
68862306a36Sopenharmony_ci	if (!iscsit_global)
68962306a36Sopenharmony_ci		return -1;
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	spin_lock_init(&iscsit_global->ts_bitmap_lock);
69262306a36Sopenharmony_ci	mutex_init(&auth_id_lock);
69362306a36Sopenharmony_ci	idr_init(&tiqn_idr);
69462306a36Sopenharmony_ci
69562306a36Sopenharmony_ci	ret = target_register_template(&iscsi_ops);
69662306a36Sopenharmony_ci	if (ret)
69762306a36Sopenharmony_ci		goto out;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	size = BITS_TO_LONGS(ISCSIT_BITMAP_BITS) * sizeof(long);
70062306a36Sopenharmony_ci	iscsit_global->ts_bitmap = vzalloc(size);
70162306a36Sopenharmony_ci	if (!iscsit_global->ts_bitmap)
70262306a36Sopenharmony_ci		goto configfs_out;
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_ci	if (!zalloc_cpumask_var(&iscsit_global->allowed_cpumask, GFP_KERNEL)) {
70562306a36Sopenharmony_ci		pr_err("Unable to allocate iscsit_global->allowed_cpumask\n");
70662306a36Sopenharmony_ci		goto bitmap_out;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci	cpumask_setall(iscsit_global->allowed_cpumask);
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	lio_qr_cache = kmem_cache_create("lio_qr_cache",
71162306a36Sopenharmony_ci			sizeof(struct iscsi_queue_req),
71262306a36Sopenharmony_ci			__alignof__(struct iscsi_queue_req), 0, NULL);
71362306a36Sopenharmony_ci	if (!lio_qr_cache) {
71462306a36Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
71562306a36Sopenharmony_ci				" lio_qr_cache\n");
71662306a36Sopenharmony_ci		goto cpumask_out;
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	lio_dr_cache = kmem_cache_create("lio_dr_cache",
72062306a36Sopenharmony_ci			sizeof(struct iscsi_datain_req),
72162306a36Sopenharmony_ci			__alignof__(struct iscsi_datain_req), 0, NULL);
72262306a36Sopenharmony_ci	if (!lio_dr_cache) {
72362306a36Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
72462306a36Sopenharmony_ci				" lio_dr_cache\n");
72562306a36Sopenharmony_ci		goto qr_out;
72662306a36Sopenharmony_ci	}
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	lio_ooo_cache = kmem_cache_create("lio_ooo_cache",
72962306a36Sopenharmony_ci			sizeof(struct iscsi_ooo_cmdsn),
73062306a36Sopenharmony_ci			__alignof__(struct iscsi_ooo_cmdsn), 0, NULL);
73162306a36Sopenharmony_ci	if (!lio_ooo_cache) {
73262306a36Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
73362306a36Sopenharmony_ci				" lio_ooo_cache\n");
73462306a36Sopenharmony_ci		goto dr_out;
73562306a36Sopenharmony_ci	}
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	lio_r2t_cache = kmem_cache_create("lio_r2t_cache",
73862306a36Sopenharmony_ci			sizeof(struct iscsi_r2t), __alignof__(struct iscsi_r2t),
73962306a36Sopenharmony_ci			0, NULL);
74062306a36Sopenharmony_ci	if (!lio_r2t_cache) {
74162306a36Sopenharmony_ci		pr_err("Unable to kmem_cache_create() for"
74262306a36Sopenharmony_ci				" lio_r2t_cache\n");
74362306a36Sopenharmony_ci		goto ooo_out;
74462306a36Sopenharmony_ci	}
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	iscsit_register_transport(&iscsi_target_transport);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci	if (iscsit_load_discovery_tpg() < 0)
74962306a36Sopenharmony_ci		goto r2t_out;
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	return ret;
75262306a36Sopenharmony_cir2t_out:
75362306a36Sopenharmony_ci	iscsit_unregister_transport(&iscsi_target_transport);
75462306a36Sopenharmony_ci	kmem_cache_destroy(lio_r2t_cache);
75562306a36Sopenharmony_ciooo_out:
75662306a36Sopenharmony_ci	kmem_cache_destroy(lio_ooo_cache);
75762306a36Sopenharmony_cidr_out:
75862306a36Sopenharmony_ci	kmem_cache_destroy(lio_dr_cache);
75962306a36Sopenharmony_ciqr_out:
76062306a36Sopenharmony_ci	kmem_cache_destroy(lio_qr_cache);
76162306a36Sopenharmony_cicpumask_out:
76262306a36Sopenharmony_ci	free_cpumask_var(iscsit_global->allowed_cpumask);
76362306a36Sopenharmony_cibitmap_out:
76462306a36Sopenharmony_ci	vfree(iscsit_global->ts_bitmap);
76562306a36Sopenharmony_ciconfigfs_out:
76662306a36Sopenharmony_ci	/* XXX: this probably wants it to be it's own unwind step.. */
76762306a36Sopenharmony_ci	if (iscsit_global->discovery_tpg)
76862306a36Sopenharmony_ci		iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
76962306a36Sopenharmony_ci	target_unregister_template(&iscsi_ops);
77062306a36Sopenharmony_ciout:
77162306a36Sopenharmony_ci	kfree(iscsit_global);
77262306a36Sopenharmony_ci	return -ENOMEM;
77362306a36Sopenharmony_ci}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_cistatic void __exit iscsi_target_cleanup_module(void)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	iscsit_release_discovery_tpg();
77862306a36Sopenharmony_ci	iscsit_unregister_transport(&iscsi_target_transport);
77962306a36Sopenharmony_ci	kmem_cache_destroy(lio_qr_cache);
78062306a36Sopenharmony_ci	kmem_cache_destroy(lio_dr_cache);
78162306a36Sopenharmony_ci	kmem_cache_destroy(lio_ooo_cache);
78262306a36Sopenharmony_ci	kmem_cache_destroy(lio_r2t_cache);
78362306a36Sopenharmony_ci
78462306a36Sopenharmony_ci	/*
78562306a36Sopenharmony_ci	 * Shutdown discovery sessions and disable discovery TPG
78662306a36Sopenharmony_ci	 */
78762306a36Sopenharmony_ci	if (iscsit_global->discovery_tpg)
78862306a36Sopenharmony_ci		iscsit_tpg_disable_portal_group(iscsit_global->discovery_tpg, 1);
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_ci	target_unregister_template(&iscsi_ops);
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	free_cpumask_var(iscsit_global->allowed_cpumask);
79362306a36Sopenharmony_ci	vfree(iscsit_global->ts_bitmap);
79462306a36Sopenharmony_ci	kfree(iscsit_global);
79562306a36Sopenharmony_ci}
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ciint iscsit_add_reject(
79862306a36Sopenharmony_ci	struct iscsit_conn *conn,
79962306a36Sopenharmony_ci	u8 reason,
80062306a36Sopenharmony_ci	unsigned char *buf)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci	cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
80562306a36Sopenharmony_ci	if (!cmd)
80662306a36Sopenharmony_ci		return -1;
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_REJECT;
80962306a36Sopenharmony_ci	cmd->reject_reason = reason;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
81262306a36Sopenharmony_ci	if (!cmd->buf_ptr) {
81362306a36Sopenharmony_ci		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
81462306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
81562306a36Sopenharmony_ci		return -1;
81662306a36Sopenharmony_ci	}
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
81962306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
82062306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
82162306a36Sopenharmony_ci
82262306a36Sopenharmony_ci	cmd->i_state = ISTATE_SEND_REJECT;
82362306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci	return -1;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_add_reject);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistatic int iscsit_add_reject_from_cmd(
83062306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
83162306a36Sopenharmony_ci	u8 reason,
83262306a36Sopenharmony_ci	bool add_to_conn,
83362306a36Sopenharmony_ci	unsigned char *buf)
83462306a36Sopenharmony_ci{
83562306a36Sopenharmony_ci	struct iscsit_conn *conn;
83662306a36Sopenharmony_ci	const bool do_put = cmd->se_cmd.se_tfo != NULL;
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	if (!cmd->conn) {
83962306a36Sopenharmony_ci		pr_err("cmd->conn is NULL for ITT: 0x%08x\n",
84062306a36Sopenharmony_ci				cmd->init_task_tag);
84162306a36Sopenharmony_ci		return -1;
84262306a36Sopenharmony_ci	}
84362306a36Sopenharmony_ci	conn = cmd->conn;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_REJECT;
84662306a36Sopenharmony_ci	cmd->reject_reason = reason;
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	cmd->buf_ptr = kmemdup(buf, ISCSI_HDR_LEN, GFP_KERNEL);
84962306a36Sopenharmony_ci	if (!cmd->buf_ptr) {
85062306a36Sopenharmony_ci		pr_err("Unable to allocate memory for cmd->buf_ptr\n");
85162306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
85262306a36Sopenharmony_ci		return -1;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	if (add_to_conn) {
85662306a36Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
85762306a36Sopenharmony_ci		list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
85862306a36Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
85962306a36Sopenharmony_ci	}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	cmd->i_state = ISTATE_SEND_REJECT;
86262306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
86362306a36Sopenharmony_ci	/*
86462306a36Sopenharmony_ci	 * Perform the kref_put now if se_cmd has already been setup by
86562306a36Sopenharmony_ci	 * scsit_setup_scsi_cmd()
86662306a36Sopenharmony_ci	 */
86762306a36Sopenharmony_ci	if (do_put) {
86862306a36Sopenharmony_ci		pr_debug("iscsi reject: calling target_put_sess_cmd >>>>>>\n");
86962306a36Sopenharmony_ci		target_put_sess_cmd(&cmd->se_cmd);
87062306a36Sopenharmony_ci	}
87162306a36Sopenharmony_ci	return -1;
87262306a36Sopenharmony_ci}
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_cistatic int iscsit_add_reject_cmd(struct iscsit_cmd *cmd, u8 reason,
87562306a36Sopenharmony_ci				 unsigned char *buf)
87662306a36Sopenharmony_ci{
87762306a36Sopenharmony_ci	return iscsit_add_reject_from_cmd(cmd, reason, true, buf);
87862306a36Sopenharmony_ci}
87962306a36Sopenharmony_ci
88062306a36Sopenharmony_ciint iscsit_reject_cmd(struct iscsit_cmd *cmd, u8 reason, unsigned char *buf)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	return iscsit_add_reject_from_cmd(cmd, reason, false, buf);
88362306a36Sopenharmony_ci}
88462306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_reject_cmd);
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_ci/*
88762306a36Sopenharmony_ci * Map some portion of the allocated scatterlist to an iovec, suitable for
88862306a36Sopenharmony_ci * kernel sockets to copy data in/out.
88962306a36Sopenharmony_ci */
89062306a36Sopenharmony_cistatic int iscsit_map_iovec(struct iscsit_cmd *cmd, struct kvec *iov, int nvec,
89162306a36Sopenharmony_ci			    u32 data_offset, u32 data_length)
89262306a36Sopenharmony_ci{
89362306a36Sopenharmony_ci	u32 i = 0, orig_data_length = data_length;
89462306a36Sopenharmony_ci	struct scatterlist *sg;
89562306a36Sopenharmony_ci	unsigned int page_off;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	/*
89862306a36Sopenharmony_ci	 * We know each entry in t_data_sg contains a page.
89962306a36Sopenharmony_ci	 */
90062306a36Sopenharmony_ci	u32 ent = data_offset / PAGE_SIZE;
90162306a36Sopenharmony_ci
90262306a36Sopenharmony_ci	if (!data_length)
90362306a36Sopenharmony_ci		return 0;
90462306a36Sopenharmony_ci
90562306a36Sopenharmony_ci	if (ent >= cmd->se_cmd.t_data_nents) {
90662306a36Sopenharmony_ci		pr_err("Initial page entry out-of-bounds\n");
90762306a36Sopenharmony_ci		goto overflow;
90862306a36Sopenharmony_ci	}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	sg = &cmd->se_cmd.t_data_sg[ent];
91162306a36Sopenharmony_ci	page_off = (data_offset % PAGE_SIZE);
91262306a36Sopenharmony_ci
91362306a36Sopenharmony_ci	cmd->first_data_sg = sg;
91462306a36Sopenharmony_ci	cmd->first_data_sg_off = page_off;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	while (data_length) {
91762306a36Sopenharmony_ci		u32 cur_len;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci		if (WARN_ON_ONCE(!sg || i >= nvec))
92062306a36Sopenharmony_ci			goto overflow;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		cur_len = min_t(u32, data_length, sg->length - page_off);
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci		iov[i].iov_base = kmap(sg_page(sg)) + sg->offset + page_off;
92562306a36Sopenharmony_ci		iov[i].iov_len = cur_len;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci		data_length -= cur_len;
92862306a36Sopenharmony_ci		page_off = 0;
92962306a36Sopenharmony_ci		sg = sg_next(sg);
93062306a36Sopenharmony_ci		i++;
93162306a36Sopenharmony_ci	}
93262306a36Sopenharmony_ci
93362306a36Sopenharmony_ci	cmd->kmapped_nents = i;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	return i;
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_cioverflow:
93862306a36Sopenharmony_ci	pr_err("offset %d + length %d overflow; %d/%d; sg-list:\n",
93962306a36Sopenharmony_ci	       data_offset, orig_data_length, i, nvec);
94062306a36Sopenharmony_ci	for_each_sg(cmd->se_cmd.t_data_sg, sg,
94162306a36Sopenharmony_ci		    cmd->se_cmd.t_data_nents, i) {
94262306a36Sopenharmony_ci		pr_err("[%d] off %d len %d\n",
94362306a36Sopenharmony_ci		       i, sg->offset, sg->length);
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci	return -1;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic void iscsit_unmap_iovec(struct iscsit_cmd *cmd)
94962306a36Sopenharmony_ci{
95062306a36Sopenharmony_ci	u32 i;
95162306a36Sopenharmony_ci	struct scatterlist *sg;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	sg = cmd->first_data_sg;
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	for (i = 0; i < cmd->kmapped_nents; i++)
95662306a36Sopenharmony_ci		kunmap(sg_page(&sg[i]));
95762306a36Sopenharmony_ci}
95862306a36Sopenharmony_ci
95962306a36Sopenharmony_cistatic void iscsit_ack_from_expstatsn(struct iscsit_conn *conn, u32 exp_statsn)
96062306a36Sopenharmony_ci{
96162306a36Sopenharmony_ci	LIST_HEAD(ack_list);
96262306a36Sopenharmony_ci	struct iscsit_cmd *cmd, *cmd_p;
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci	conn->exp_statsn = exp_statsn;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (conn->sess->sess_ops->RDMAExtensions)
96762306a36Sopenharmony_ci		return;
96862306a36Sopenharmony_ci
96962306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
97062306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_p, &conn->conn_cmd_list, i_conn_node) {
97162306a36Sopenharmony_ci		spin_lock(&cmd->istate_lock);
97262306a36Sopenharmony_ci		if ((cmd->i_state == ISTATE_SENT_STATUS) &&
97362306a36Sopenharmony_ci		    iscsi_sna_lt(cmd->stat_sn, exp_statsn)) {
97462306a36Sopenharmony_ci			cmd->i_state = ISTATE_REMOVE;
97562306a36Sopenharmony_ci			spin_unlock(&cmd->istate_lock);
97662306a36Sopenharmony_ci			list_move_tail(&cmd->i_conn_node, &ack_list);
97762306a36Sopenharmony_ci			continue;
97862306a36Sopenharmony_ci		}
97962306a36Sopenharmony_ci		spin_unlock(&cmd->istate_lock);
98062306a36Sopenharmony_ci	}
98162306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_p, &ack_list, i_conn_node) {
98462306a36Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
98562306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
98662306a36Sopenharmony_ci	}
98762306a36Sopenharmony_ci}
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_cistatic int iscsit_allocate_iovecs(struct iscsit_cmd *cmd)
99062306a36Sopenharmony_ci{
99162306a36Sopenharmony_ci	u32 iov_count = max(1UL, DIV_ROUND_UP(cmd->se_cmd.data_length, PAGE_SIZE));
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	iov_count += ISCSI_IOV_DATA_BUFFER;
99462306a36Sopenharmony_ci	cmd->iov_data = kcalloc(iov_count, sizeof(*cmd->iov_data), GFP_KERNEL);
99562306a36Sopenharmony_ci	if (!cmd->iov_data)
99662306a36Sopenharmony_ci		return -ENOMEM;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	cmd->orig_iov_data_count = iov_count;
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ciint iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
100362306a36Sopenharmony_ci			  unsigned char *buf)
100462306a36Sopenharmony_ci{
100562306a36Sopenharmony_ci	int data_direction, payload_length;
100662306a36Sopenharmony_ci	struct iscsi_ecdb_ahdr *ecdb_ahdr;
100762306a36Sopenharmony_ci	struct iscsi_scsi_req *hdr;
100862306a36Sopenharmony_ci	int iscsi_task_attr;
100962306a36Sopenharmony_ci	unsigned char *cdb;
101062306a36Sopenharmony_ci	int sam_task_attr;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	atomic_long_inc(&conn->sess->cmd_pdus);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	hdr			= (struct iscsi_scsi_req *) buf;
101562306a36Sopenharmony_ci	payload_length		= ntoh24(hdr->dlength);
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* FIXME; Add checks for AdditionalHeaderSegment */
101862306a36Sopenharmony_ci
101962306a36Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_WRITE) &&
102062306a36Sopenharmony_ci	    !(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
102162306a36Sopenharmony_ci		pr_err("ISCSI_FLAG_CMD_WRITE & ISCSI_FLAG_CMD_FINAL"
102262306a36Sopenharmony_ci				" not set. Bad iSCSI Initiator.\n");
102362306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
102462306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
102562306a36Sopenharmony_ci	}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (((hdr->flags & ISCSI_FLAG_CMD_READ) ||
102862306a36Sopenharmony_ci	     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) && !hdr->data_length) {
102962306a36Sopenharmony_ci		/*
103062306a36Sopenharmony_ci		 * From RFC-3720 Section 10.3.1:
103162306a36Sopenharmony_ci		 *
103262306a36Sopenharmony_ci		 * "Either or both of R and W MAY be 1 when either the
103362306a36Sopenharmony_ci		 *  Expected Data Transfer Length and/or Bidirectional Read
103462306a36Sopenharmony_ci		 *  Expected Data Transfer Length are 0"
103562306a36Sopenharmony_ci		 *
103662306a36Sopenharmony_ci		 * For this case, go ahead and clear the unnecssary bits
103762306a36Sopenharmony_ci		 * to avoid any confusion with ->data_direction.
103862306a36Sopenharmony_ci		 */
103962306a36Sopenharmony_ci		hdr->flags &= ~ISCSI_FLAG_CMD_READ;
104062306a36Sopenharmony_ci		hdr->flags &= ~ISCSI_FLAG_CMD_WRITE;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci		pr_warn("ISCSI_FLAG_CMD_READ or ISCSI_FLAG_CMD_WRITE"
104362306a36Sopenharmony_ci			" set when Expected Data Transfer Length is 0 for"
104462306a36Sopenharmony_ci			" CDB: 0x%02x, Fixing up flags\n", hdr->cdb[0]);
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_READ) &&
104862306a36Sopenharmony_ci	    !(hdr->flags & ISCSI_FLAG_CMD_WRITE) && (hdr->data_length != 0)) {
104962306a36Sopenharmony_ci		pr_err("ISCSI_FLAG_CMD_READ and/or ISCSI_FLAG_CMD_WRITE"
105062306a36Sopenharmony_ci			" MUST be set if Expected Data Transfer Length is not 0."
105162306a36Sopenharmony_ci			" Bad iSCSI Initiator\n");
105262306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
105362306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
105462306a36Sopenharmony_ci	}
105562306a36Sopenharmony_ci
105662306a36Sopenharmony_ci	if ((hdr->flags & ISCSI_FLAG_CMD_READ) &&
105762306a36Sopenharmony_ci	    (hdr->flags & ISCSI_FLAG_CMD_WRITE)) {
105862306a36Sopenharmony_ci		pr_err("Bidirectional operations not supported!\n");
105962306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
106062306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
106162306a36Sopenharmony_ci	}
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
106462306a36Sopenharmony_ci		pr_err("Illegally set Immediate Bit in iSCSI Initiator"
106562306a36Sopenharmony_ci				" Scsi Command PDU.\n");
106662306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
106762306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
106862306a36Sopenharmony_ci	}
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	if (payload_length && !conn->sess->sess_ops->ImmediateData) {
107162306a36Sopenharmony_ci		pr_err("ImmediateData=No but DataSegmentLength=%u,"
107262306a36Sopenharmony_ci			" protocol error.\n", payload_length);
107362306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
107462306a36Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	if ((be32_to_cpu(hdr->data_length) == payload_length) &&
107862306a36Sopenharmony_ci	    (!(hdr->flags & ISCSI_FLAG_CMD_FINAL))) {
107962306a36Sopenharmony_ci		pr_err("Expected Data Transfer Length and Length of"
108062306a36Sopenharmony_ci			" Immediate Data are the same, but ISCSI_FLAG_CMD_FINAL"
108162306a36Sopenharmony_ci			" bit is not set protocol error\n");
108262306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
108362306a36Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
108462306a36Sopenharmony_ci	}
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	if (payload_length > be32_to_cpu(hdr->data_length)) {
108762306a36Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
108862306a36Sopenharmony_ci			" EDTL: %u, protocol error.\n", payload_length,
108962306a36Sopenharmony_ci				hdr->data_length);
109062306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
109162306a36Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
109262306a36Sopenharmony_ci	}
109362306a36Sopenharmony_ci
109462306a36Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
109562306a36Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
109662306a36Sopenharmony_ci			" MaxXmitDataSegmentLength: %u, protocol error.\n",
109762306a36Sopenharmony_ci			payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
109862306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
109962306a36Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (payload_length > conn->sess->sess_ops->FirstBurstLength) {
110362306a36Sopenharmony_ci		pr_err("DataSegmentLength: %u is greater than"
110462306a36Sopenharmony_ci			" FirstBurstLength: %u, protocol error.\n",
110562306a36Sopenharmony_ci			payload_length, conn->sess->sess_ops->FirstBurstLength);
110662306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
110762306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_INVALID, buf);
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	cdb = hdr->cdb;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	if (hdr->hlength) {
111362306a36Sopenharmony_ci		ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1);
111462306a36Sopenharmony_ci		if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) {
111562306a36Sopenharmony_ci			pr_err("Additional Header Segment type %d not supported!\n",
111662306a36Sopenharmony_ci			       ecdb_ahdr->ahstype);
111762306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
111862306a36Sopenharmony_ci				ISCSI_REASON_CMD_NOT_SUPPORTED, buf);
111962306a36Sopenharmony_ci		}
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci		cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15,
112262306a36Sopenharmony_ci			      GFP_KERNEL);
112362306a36Sopenharmony_ci		if (cdb == NULL)
112462306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
112562306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
112662306a36Sopenharmony_ci		memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE);
112762306a36Sopenharmony_ci		memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb,
112862306a36Sopenharmony_ci		       be16_to_cpu(ecdb_ahdr->ahslength) - 1);
112962306a36Sopenharmony_ci	}
113062306a36Sopenharmony_ci
113162306a36Sopenharmony_ci	data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE :
113262306a36Sopenharmony_ci			 (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE :
113362306a36Sopenharmony_ci			  DMA_NONE;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	cmd->data_direction = data_direction;
113662306a36Sopenharmony_ci	iscsi_task_attr = hdr->flags & ISCSI_FLAG_CMD_ATTR_MASK;
113762306a36Sopenharmony_ci	/*
113862306a36Sopenharmony_ci	 * Figure out the SAM Task Attribute for the incoming SCSI CDB
113962306a36Sopenharmony_ci	 */
114062306a36Sopenharmony_ci	if ((iscsi_task_attr == ISCSI_ATTR_UNTAGGED) ||
114162306a36Sopenharmony_ci	    (iscsi_task_attr == ISCSI_ATTR_SIMPLE))
114262306a36Sopenharmony_ci		sam_task_attr = TCM_SIMPLE_TAG;
114362306a36Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_ORDERED)
114462306a36Sopenharmony_ci		sam_task_attr = TCM_ORDERED_TAG;
114562306a36Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_HEAD_OF_QUEUE)
114662306a36Sopenharmony_ci		sam_task_attr = TCM_HEAD_TAG;
114762306a36Sopenharmony_ci	else if (iscsi_task_attr == ISCSI_ATTR_ACA)
114862306a36Sopenharmony_ci		sam_task_attr = TCM_ACA_TAG;
114962306a36Sopenharmony_ci	else {
115062306a36Sopenharmony_ci		pr_debug("Unknown iSCSI Task Attribute: 0x%02x, using"
115162306a36Sopenharmony_ci			" TCM_SIMPLE_TAG\n", iscsi_task_attr);
115262306a36Sopenharmony_ci		sam_task_attr = TCM_SIMPLE_TAG;
115362306a36Sopenharmony_ci	}
115462306a36Sopenharmony_ci
115562306a36Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_SCSI_CMD;
115662306a36Sopenharmony_ci	cmd->i_state		= ISTATE_NEW_CMD;
115762306a36Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
115862306a36Sopenharmony_ci	cmd->immediate_data	= (payload_length) ? 1 : 0;
115962306a36Sopenharmony_ci	cmd->unsolicited_data	= ((!(hdr->flags & ISCSI_FLAG_CMD_FINAL) &&
116062306a36Sopenharmony_ci				     (hdr->flags & ISCSI_FLAG_CMD_WRITE)) ? 1 : 0);
116162306a36Sopenharmony_ci	if (cmd->unsolicited_data)
116262306a36Sopenharmony_ci		cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA;
116362306a36Sopenharmony_ci
116462306a36Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
116562306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_READ)
116662306a36Sopenharmony_ci		cmd->targ_xfer_tag = session_get_next_ttt(conn->sess);
116762306a36Sopenharmony_ci	else
116862306a36Sopenharmony_ci		cmd->targ_xfer_tag = 0xFFFFFFFF;
116962306a36Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
117062306a36Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
117162306a36Sopenharmony_ci	cmd->first_burst_len	= payload_length;
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	if (!conn->sess->sess_ops->RDMAExtensions &&
117462306a36Sopenharmony_ci	     cmd->data_direction == DMA_FROM_DEVICE) {
117562306a36Sopenharmony_ci		struct iscsi_datain_req *dr;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		dr = iscsit_allocate_datain_req();
117862306a36Sopenharmony_ci		if (!dr) {
117962306a36Sopenharmony_ci			if (cdb != hdr->cdb)
118062306a36Sopenharmony_ci				kfree(cdb);
118162306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
118262306a36Sopenharmony_ci					ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
118362306a36Sopenharmony_ci		}
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci		iscsit_attach_datain_req(cmd, dr);
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/*
118962306a36Sopenharmony_ci	 * Initialize struct se_cmd descriptor from target_core_mod infrastructure
119062306a36Sopenharmony_ci	 */
119162306a36Sopenharmony_ci	__target_init_cmd(&cmd->se_cmd, &iscsi_ops,
119262306a36Sopenharmony_ci			  conn->sess->se_sess, be32_to_cpu(hdr->data_length),
119362306a36Sopenharmony_ci			  cmd->data_direction, sam_task_attr,
119462306a36Sopenharmony_ci			  cmd->sense_buffer + 2, scsilun_to_int(&hdr->lun),
119562306a36Sopenharmony_ci			  conn->cmd_cnt);
119662306a36Sopenharmony_ci
119762306a36Sopenharmony_ci	pr_debug("Got SCSI Command, ITT: 0x%08x, CmdSN: 0x%08x,"
119862306a36Sopenharmony_ci		" ExpXferLen: %u, Length: %u, CID: %hu\n", hdr->itt,
119962306a36Sopenharmony_ci		hdr->cmdsn, be32_to_cpu(hdr->data_length), payload_length,
120062306a36Sopenharmony_ci		conn->cid);
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	target_get_sess_cmd(&cmd->se_cmd, true);
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	cmd->se_cmd.tag = (__force u32)cmd->init_task_tag;
120562306a36Sopenharmony_ci	cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, cdb,
120662306a36Sopenharmony_ci						GFP_KERNEL);
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (cdb != hdr->cdb)
120962306a36Sopenharmony_ci		kfree(cdb);
121062306a36Sopenharmony_ci
121162306a36Sopenharmony_ci	if (cmd->sense_reason) {
121262306a36Sopenharmony_ci		if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) {
121362306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
121462306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
121562306a36Sopenharmony_ci		}
121662306a36Sopenharmony_ci
121762306a36Sopenharmony_ci		goto attach_cmd;
121862306a36Sopenharmony_ci	}
121962306a36Sopenharmony_ci
122062306a36Sopenharmony_ci	cmd->sense_reason = transport_lookup_cmd_lun(&cmd->se_cmd);
122162306a36Sopenharmony_ci	if (cmd->sense_reason)
122262306a36Sopenharmony_ci		goto attach_cmd;
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci	cmd->sense_reason = target_cmd_parse_cdb(&cmd->se_cmd);
122562306a36Sopenharmony_ci	if (cmd->sense_reason)
122662306a36Sopenharmony_ci		goto attach_cmd;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	if (iscsit_build_pdu_and_seq_lists(cmd, payload_length) < 0) {
122962306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
123062306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
123162306a36Sopenharmony_ci	}
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ciattach_cmd:
123462306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
123562306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
123662306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
123762306a36Sopenharmony_ci	/*
123862306a36Sopenharmony_ci	 * Check if we need to delay processing because of ALUA
123962306a36Sopenharmony_ci	 * Active/NonOptimized primary access state..
124062306a36Sopenharmony_ci	 */
124162306a36Sopenharmony_ci	core_alua_check_nonop_delay(&cmd->se_cmd);
124262306a36Sopenharmony_ci
124362306a36Sopenharmony_ci	return 0;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_scsi_cmd);
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_civoid iscsit_set_unsolicited_dataout(struct iscsit_cmd *cmd)
124862306a36Sopenharmony_ci{
124962306a36Sopenharmony_ci	iscsit_set_dataout_sequence_values(cmd);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	spin_lock_bh(&cmd->dataout_timeout_lock);
125262306a36Sopenharmony_ci	iscsit_start_dataout_timer(cmd, cmd->conn);
125362306a36Sopenharmony_ci	spin_unlock_bh(&cmd->dataout_timeout_lock);
125462306a36Sopenharmony_ci}
125562306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_set_unsolicited_dataout);
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ciint iscsit_process_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
125862306a36Sopenharmony_ci			    struct iscsi_scsi_req *hdr)
125962306a36Sopenharmony_ci{
126062306a36Sopenharmony_ci	int cmdsn_ret = 0;
126162306a36Sopenharmony_ci	/*
126262306a36Sopenharmony_ci	 * Check the CmdSN against ExpCmdSN/MaxCmdSN here if
126362306a36Sopenharmony_ci	 * the Immediate Bit is not set, and no Immediate
126462306a36Sopenharmony_ci	 * Data is attached.
126562306a36Sopenharmony_ci	 *
126662306a36Sopenharmony_ci	 * A PDU/CmdSN carrying Immediate Data can only
126762306a36Sopenharmony_ci	 * be processed after the DataCRC has passed.
126862306a36Sopenharmony_ci	 * If the DataCRC fails, the CmdSN MUST NOT
126962306a36Sopenharmony_ci	 * be acknowledged. (See below)
127062306a36Sopenharmony_ci	 */
127162306a36Sopenharmony_ci	if (!cmd->immediate_data) {
127262306a36Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
127362306a36Sopenharmony_ci					(unsigned char *)hdr, hdr->cmdsn);
127462306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
127562306a36Sopenharmony_ci			return -1;
127662306a36Sopenharmony_ci		else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
127762306a36Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
127862306a36Sopenharmony_ci			return 0;
127962306a36Sopenharmony_ci		}
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	/*
128562306a36Sopenharmony_ci	 * If no Immediate Data is attached, it's OK to return now.
128662306a36Sopenharmony_ci	 */
128762306a36Sopenharmony_ci	if (!cmd->immediate_data) {
128862306a36Sopenharmony_ci		if (!cmd->sense_reason && cmd->unsolicited_data)
128962306a36Sopenharmony_ci			iscsit_set_unsolicited_dataout(cmd);
129062306a36Sopenharmony_ci		if (!cmd->sense_reason)
129162306a36Sopenharmony_ci			return 0;
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci		target_put_sess_cmd(&cmd->se_cmd);
129462306a36Sopenharmony_ci		return 0;
129562306a36Sopenharmony_ci	}
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	/*
129862306a36Sopenharmony_ci	 * Early CHECK_CONDITIONs with ImmediateData never make it to command
129962306a36Sopenharmony_ci	 * execution.  These exceptions are processed in CmdSN order using
130062306a36Sopenharmony_ci	 * iscsit_check_received_cmdsn() in iscsit_get_immediate_data() below.
130162306a36Sopenharmony_ci	 */
130262306a36Sopenharmony_ci	if (cmd->sense_reason)
130362306a36Sopenharmony_ci		return 1;
130462306a36Sopenharmony_ci	/*
130562306a36Sopenharmony_ci	 * Call directly into transport_generic_new_cmd() to perform
130662306a36Sopenharmony_ci	 * the backend memory allocation.
130762306a36Sopenharmony_ci	 */
130862306a36Sopenharmony_ci	cmd->sense_reason = transport_generic_new_cmd(&cmd->se_cmd);
130962306a36Sopenharmony_ci	if (cmd->sense_reason)
131062306a36Sopenharmony_ci		return 1;
131162306a36Sopenharmony_ci
131262306a36Sopenharmony_ci	return 0;
131362306a36Sopenharmony_ci}
131462306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_scsi_cmd);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_cistatic int
131762306a36Sopenharmony_ciiscsit_get_immediate_data(struct iscsit_cmd *cmd, struct iscsi_scsi_req *hdr,
131862306a36Sopenharmony_ci			  bool dump_payload)
131962306a36Sopenharmony_ci{
132062306a36Sopenharmony_ci	int cmdsn_ret = 0, immed_ret = IMMEDIATE_DATA_NORMAL_OPERATION;
132162306a36Sopenharmony_ci	int rc;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	/*
132462306a36Sopenharmony_ci	 * Special case for Unsupported SAM WRITE Opcodes and ImmediateData=Yes.
132562306a36Sopenharmony_ci	 */
132662306a36Sopenharmony_ci	if (dump_payload) {
132762306a36Sopenharmony_ci		u32 length = min(cmd->se_cmd.data_length - cmd->write_data_done,
132862306a36Sopenharmony_ci				 cmd->first_burst_len);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci		pr_debug("Dumping min(%d - %d, %d) = %d bytes of immediate data\n",
133162306a36Sopenharmony_ci			 cmd->se_cmd.data_length, cmd->write_data_done,
133262306a36Sopenharmony_ci			 cmd->first_burst_len, length);
133362306a36Sopenharmony_ci		rc = iscsit_dump_data_payload(cmd->conn, length, 1);
133462306a36Sopenharmony_ci		pr_debug("Finished dumping immediate data\n");
133562306a36Sopenharmony_ci		if (rc < 0)
133662306a36Sopenharmony_ci			immed_ret = IMMEDIATE_DATA_CANNOT_RECOVER;
133762306a36Sopenharmony_ci	} else {
133862306a36Sopenharmony_ci		immed_ret = iscsit_handle_immediate_data(cmd, hdr,
133962306a36Sopenharmony_ci							 cmd->first_burst_len);
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	if (immed_ret == IMMEDIATE_DATA_NORMAL_OPERATION) {
134362306a36Sopenharmony_ci		/*
134462306a36Sopenharmony_ci		 * A PDU/CmdSN carrying Immediate Data passed
134562306a36Sopenharmony_ci		 * DataCRC, check against ExpCmdSN/MaxCmdSN if
134662306a36Sopenharmony_ci		 * Immediate Bit is not set.
134762306a36Sopenharmony_ci		 */
134862306a36Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(cmd->conn, cmd,
134962306a36Sopenharmony_ci					(unsigned char *)hdr, hdr->cmdsn);
135062306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
135162306a36Sopenharmony_ci			return -1;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		if (cmd->sense_reason || cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
135462306a36Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_ci			return 0;
135762306a36Sopenharmony_ci		} else if (cmd->unsolicited_data)
135862306a36Sopenharmony_ci			iscsit_set_unsolicited_dataout(cmd);
135962306a36Sopenharmony_ci
136062306a36Sopenharmony_ci	} else if (immed_ret == IMMEDIATE_DATA_ERL1_CRC_FAILURE) {
136162306a36Sopenharmony_ci		/*
136262306a36Sopenharmony_ci		 * Immediate Data failed DataCRC and ERL>=1,
136362306a36Sopenharmony_ci		 * silently drop this PDU and let the initiator
136462306a36Sopenharmony_ci		 * plug the CmdSN gap.
136562306a36Sopenharmony_ci		 *
136662306a36Sopenharmony_ci		 * FIXME: Send Unsolicited NOPIN with reserved
136762306a36Sopenharmony_ci		 * TTT here to help the initiator figure out
136862306a36Sopenharmony_ci		 * the missing CmdSN, although they should be
136962306a36Sopenharmony_ci		 * intelligent enough to determine the missing
137062306a36Sopenharmony_ci		 * CmdSN and issue a retry to plug the sequence.
137162306a36Sopenharmony_ci		 */
137262306a36Sopenharmony_ci		cmd->i_state = ISTATE_REMOVE;
137362306a36Sopenharmony_ci		iscsit_add_cmd_to_immediate_queue(cmd, cmd->conn, cmd->i_state);
137462306a36Sopenharmony_ci	} else /* immed_ret == IMMEDIATE_DATA_CANNOT_RECOVER */
137562306a36Sopenharmony_ci		return -1;
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	return 0;
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic int
138162306a36Sopenharmony_ciiscsit_handle_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
138262306a36Sopenharmony_ci			   unsigned char *buf)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)buf;
138562306a36Sopenharmony_ci	int rc, immed_data;
138662306a36Sopenharmony_ci	bool dump_payload = false;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	rc = iscsit_setup_scsi_cmd(conn, cmd, buf);
138962306a36Sopenharmony_ci	if (rc < 0)
139062306a36Sopenharmony_ci		return 0;
139162306a36Sopenharmony_ci	/*
139262306a36Sopenharmony_ci	 * Allocation iovecs needed for struct socket operations for
139362306a36Sopenharmony_ci	 * traditional iSCSI block I/O.
139462306a36Sopenharmony_ci	 */
139562306a36Sopenharmony_ci	if (iscsit_allocate_iovecs(cmd) < 0) {
139662306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd,
139762306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
139862306a36Sopenharmony_ci	}
139962306a36Sopenharmony_ci	immed_data = cmd->immediate_data;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	rc = iscsit_process_scsi_cmd(conn, cmd, hdr);
140262306a36Sopenharmony_ci	if (rc < 0)
140362306a36Sopenharmony_ci		return rc;
140462306a36Sopenharmony_ci	else if (rc > 0)
140562306a36Sopenharmony_ci		dump_payload = true;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	if (!immed_data)
140862306a36Sopenharmony_ci		return 0;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return iscsit_get_immediate_data(cmd, hdr, dump_payload);
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic u32 iscsit_do_crypto_hash_sg(
141462306a36Sopenharmony_ci	struct ahash_request *hash,
141562306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
141662306a36Sopenharmony_ci	u32 data_offset,
141762306a36Sopenharmony_ci	u32 data_length,
141862306a36Sopenharmony_ci	u32 padding,
141962306a36Sopenharmony_ci	u8 *pad_bytes)
142062306a36Sopenharmony_ci{
142162306a36Sopenharmony_ci	u32 data_crc;
142262306a36Sopenharmony_ci	struct scatterlist *sg;
142362306a36Sopenharmony_ci	unsigned int page_off;
142462306a36Sopenharmony_ci
142562306a36Sopenharmony_ci	crypto_ahash_init(hash);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	sg = cmd->first_data_sg;
142862306a36Sopenharmony_ci	page_off = cmd->first_data_sg_off;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	if (data_length && page_off) {
143162306a36Sopenharmony_ci		struct scatterlist first_sg;
143262306a36Sopenharmony_ci		u32 len = min_t(u32, data_length, sg->length - page_off);
143362306a36Sopenharmony_ci
143462306a36Sopenharmony_ci		sg_init_table(&first_sg, 1);
143562306a36Sopenharmony_ci		sg_set_page(&first_sg, sg_page(sg), len, sg->offset + page_off);
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci		ahash_request_set_crypt(hash, &first_sg, NULL, len);
143862306a36Sopenharmony_ci		crypto_ahash_update(hash);
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci		data_length -= len;
144162306a36Sopenharmony_ci		sg = sg_next(sg);
144262306a36Sopenharmony_ci	}
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	while (data_length) {
144562306a36Sopenharmony_ci		u32 cur_len = min_t(u32, data_length, sg->length);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci		ahash_request_set_crypt(hash, sg, NULL, cur_len);
144862306a36Sopenharmony_ci		crypto_ahash_update(hash);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci		data_length -= cur_len;
145162306a36Sopenharmony_ci		/* iscsit_map_iovec has already checked for invalid sg pointers */
145262306a36Sopenharmony_ci		sg = sg_next(sg);
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	if (padding) {
145662306a36Sopenharmony_ci		struct scatterlist pad_sg;
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci		sg_init_one(&pad_sg, pad_bytes, padding);
145962306a36Sopenharmony_ci		ahash_request_set_crypt(hash, &pad_sg, (u8 *)&data_crc,
146062306a36Sopenharmony_ci					padding);
146162306a36Sopenharmony_ci		crypto_ahash_finup(hash);
146262306a36Sopenharmony_ci	} else {
146362306a36Sopenharmony_ci		ahash_request_set_crypt(hash, NULL, (u8 *)&data_crc, 0);
146462306a36Sopenharmony_ci		crypto_ahash_final(hash);
146562306a36Sopenharmony_ci	}
146662306a36Sopenharmony_ci
146762306a36Sopenharmony_ci	return data_crc;
146862306a36Sopenharmony_ci}
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_cistatic void iscsit_do_crypto_hash_buf(struct ahash_request *hash,
147162306a36Sopenharmony_ci	const void *buf, u32 payload_length, u32 padding,
147262306a36Sopenharmony_ci	const void *pad_bytes, void *data_crc)
147362306a36Sopenharmony_ci{
147462306a36Sopenharmony_ci	struct scatterlist sg[2];
147562306a36Sopenharmony_ci
147662306a36Sopenharmony_ci	sg_init_table(sg, ARRAY_SIZE(sg));
147762306a36Sopenharmony_ci	sg_set_buf(sg, buf, payload_length);
147862306a36Sopenharmony_ci	if (padding)
147962306a36Sopenharmony_ci		sg_set_buf(sg + 1, pad_bytes, padding);
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_ci	ahash_request_set_crypt(hash, sg, data_crc, payload_length + padding);
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	crypto_ahash_digest(hash);
148462306a36Sopenharmony_ci}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ciint
148762306a36Sopenharmony_ci__iscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf,
148862306a36Sopenharmony_ci			   struct iscsit_cmd *cmd, u32 payload_length,
148962306a36Sopenharmony_ci			   bool *success)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct iscsi_data *hdr = buf;
149262306a36Sopenharmony_ci	struct se_cmd *se_cmd;
149362306a36Sopenharmony_ci	int rc;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	/* iSCSI write */
149662306a36Sopenharmony_ci	atomic_long_add(payload_length, &conn->sess->rx_data_octets);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	pr_debug("Got DataOut ITT: 0x%08x, TTT: 0x%08x,"
149962306a36Sopenharmony_ci		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
150062306a36Sopenharmony_ci		hdr->itt, hdr->ttt, hdr->datasn, ntohl(hdr->offset),
150162306a36Sopenharmony_ci		payload_length, conn->cid);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	if (cmd->cmd_flags & ICF_GOT_LAST_DATAOUT) {
150462306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x received DataOUT after"
150562306a36Sopenharmony_ci			" last DataOUT received, dumping payload\n",
150662306a36Sopenharmony_ci			cmd->init_task_tag);
150762306a36Sopenharmony_ci		return iscsit_dump_data_payload(conn, payload_length, 1);
150862306a36Sopenharmony_ci	}
150962306a36Sopenharmony_ci
151062306a36Sopenharmony_ci	if (cmd->data_direction != DMA_TO_DEVICE) {
151162306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x received DataOUT for a"
151262306a36Sopenharmony_ci			" NON-WRITE command.\n", cmd->init_task_tag);
151362306a36Sopenharmony_ci		return iscsit_dump_data_payload(conn, payload_length, 1);
151462306a36Sopenharmony_ci	}
151562306a36Sopenharmony_ci	se_cmd = &cmd->se_cmd;
151662306a36Sopenharmony_ci	iscsit_mod_dataout_timer(cmd);
151762306a36Sopenharmony_ci
151862306a36Sopenharmony_ci	if ((be32_to_cpu(hdr->offset) + payload_length) > cmd->se_cmd.data_length) {
151962306a36Sopenharmony_ci		pr_err("DataOut Offset: %u, Length %u greater than iSCSI Command EDTL %u, protocol error.\n",
152062306a36Sopenharmony_ci		       be32_to_cpu(hdr->offset), payload_length,
152162306a36Sopenharmony_ci		       cmd->se_cmd.data_length);
152262306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_INVALID, buf);
152362306a36Sopenharmony_ci	}
152462306a36Sopenharmony_ci
152562306a36Sopenharmony_ci	if (cmd->unsolicited_data) {
152662306a36Sopenharmony_ci		int dump_unsolicited_data = 0;
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci		if (conn->sess->sess_ops->InitialR2T) {
152962306a36Sopenharmony_ci			pr_err("Received unexpected unsolicited data"
153062306a36Sopenharmony_ci				" while InitialR2T=Yes, protocol error.\n");
153162306a36Sopenharmony_ci			transport_send_check_condition_and_sense(&cmd->se_cmd,
153262306a36Sopenharmony_ci					TCM_UNEXPECTED_UNSOLICITED_DATA, 0);
153362306a36Sopenharmony_ci			return -1;
153462306a36Sopenharmony_ci		}
153562306a36Sopenharmony_ci		/*
153662306a36Sopenharmony_ci		 * Special case for dealing with Unsolicited DataOUT
153762306a36Sopenharmony_ci		 * and Unsupported SAM WRITE Opcodes and SE resource allocation
153862306a36Sopenharmony_ci		 * failures;
153962306a36Sopenharmony_ci		 */
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci		/* Something's amiss if we're not in WRITE_PENDING state... */
154262306a36Sopenharmony_ci		WARN_ON(se_cmd->t_state != TRANSPORT_WRITE_PENDING);
154362306a36Sopenharmony_ci		if (!(se_cmd->se_cmd_flags & SCF_SUPPORTED_SAM_OPCODE))
154462306a36Sopenharmony_ci			dump_unsolicited_data = 1;
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci		if (dump_unsolicited_data) {
154762306a36Sopenharmony_ci			/*
154862306a36Sopenharmony_ci			 * Check if a delayed TASK_ABORTED status needs to
154962306a36Sopenharmony_ci			 * be sent now if the ISCSI_FLAG_CMD_FINAL has been
155062306a36Sopenharmony_ci			 * received with the unsolicited data out.
155162306a36Sopenharmony_ci			 */
155262306a36Sopenharmony_ci			if (hdr->flags & ISCSI_FLAG_CMD_FINAL)
155362306a36Sopenharmony_ci				iscsit_stop_dataout_timer(cmd);
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci			return iscsit_dump_data_payload(conn, payload_length, 1);
155662306a36Sopenharmony_ci		}
155762306a36Sopenharmony_ci	} else {
155862306a36Sopenharmony_ci		/*
155962306a36Sopenharmony_ci		 * For the normal solicited data path:
156062306a36Sopenharmony_ci		 *
156162306a36Sopenharmony_ci		 * Check for a delayed TASK_ABORTED status and dump any
156262306a36Sopenharmony_ci		 * incoming data out payload if one exists.  Also, when the
156362306a36Sopenharmony_ci		 * ISCSI_FLAG_CMD_FINAL is set to denote the end of the current
156462306a36Sopenharmony_ci		 * data out sequence, we decrement outstanding_r2ts.  Once
156562306a36Sopenharmony_ci		 * outstanding_r2ts reaches zero, go ahead and send the delayed
156662306a36Sopenharmony_ci		 * TASK_ABORTED status.
156762306a36Sopenharmony_ci		 */
156862306a36Sopenharmony_ci		if (se_cmd->transport_state & CMD_T_ABORTED) {
156962306a36Sopenharmony_ci			if (hdr->flags & ISCSI_FLAG_CMD_FINAL &&
157062306a36Sopenharmony_ci			    --cmd->outstanding_r2ts < 1)
157162306a36Sopenharmony_ci				iscsit_stop_dataout_timer(cmd);
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ci			return iscsit_dump_data_payload(conn, payload_length, 1);
157462306a36Sopenharmony_ci		}
157562306a36Sopenharmony_ci	}
157662306a36Sopenharmony_ci	/*
157762306a36Sopenharmony_ci	 * Perform DataSN, DataSequenceInOrder, DataPDUInOrder, and
157862306a36Sopenharmony_ci	 * within-command recovery checks before receiving the payload.
157962306a36Sopenharmony_ci	 */
158062306a36Sopenharmony_ci	rc = iscsit_check_pre_dataout(cmd, buf);
158162306a36Sopenharmony_ci	if (rc == DATAOUT_WITHIN_COMMAND_RECOVERY)
158262306a36Sopenharmony_ci		return 0;
158362306a36Sopenharmony_ci	else if (rc == DATAOUT_CANNOT_RECOVER)
158462306a36Sopenharmony_ci		return -1;
158562306a36Sopenharmony_ci	*success = true;
158662306a36Sopenharmony_ci	return 0;
158762306a36Sopenharmony_ci}
158862306a36Sopenharmony_ciEXPORT_SYMBOL(__iscsit_check_dataout_hdr);
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ciint
159162306a36Sopenharmony_ciiscsit_check_dataout_hdr(struct iscsit_conn *conn, void *buf,
159262306a36Sopenharmony_ci			 struct iscsit_cmd **out_cmd)
159362306a36Sopenharmony_ci{
159462306a36Sopenharmony_ci	struct iscsi_data *hdr = buf;
159562306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
159662306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
159762306a36Sopenharmony_ci	int rc;
159862306a36Sopenharmony_ci	bool success = false;
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	if (!payload_length) {
160162306a36Sopenharmony_ci		pr_warn_ratelimited("DataOUT payload is ZERO, ignoring.\n");
160262306a36Sopenharmony_ci		return 0;
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
160662306a36Sopenharmony_ci		pr_err_ratelimited("DataSegmentLength: %u is greater than"
160762306a36Sopenharmony_ci			" MaxXmitDataSegmentLength: %u\n", payload_length,
160862306a36Sopenharmony_ci			conn->conn_ops->MaxXmitDataSegmentLength);
160962306a36Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, buf);
161062306a36Sopenharmony_ci	}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci	cmd = iscsit_find_cmd_from_itt_or_dump(conn, hdr->itt, payload_length);
161362306a36Sopenharmony_ci	if (!cmd)
161462306a36Sopenharmony_ci		return 0;
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci	rc = __iscsit_check_dataout_hdr(conn, buf, cmd, payload_length, &success);
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	if (success)
161962306a36Sopenharmony_ci		*out_cmd = cmd;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	return rc;
162262306a36Sopenharmony_ci}
162362306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_check_dataout_hdr);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_cistatic int
162662306a36Sopenharmony_ciiscsit_get_dataout(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
162762306a36Sopenharmony_ci		   struct iscsi_data *hdr)
162862306a36Sopenharmony_ci{
162962306a36Sopenharmony_ci	struct kvec *iov;
163062306a36Sopenharmony_ci	u32 checksum, iov_count = 0, padding = 0, rx_got = 0, rx_size = 0;
163162306a36Sopenharmony_ci	u32 payload_length;
163262306a36Sopenharmony_ci	int iov_ret, data_crc_failed = 0;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	payload_length = min_t(u32, cmd->se_cmd.data_length,
163562306a36Sopenharmony_ci			       ntoh24(hdr->dlength));
163662306a36Sopenharmony_ci	rx_size += payload_length;
163762306a36Sopenharmony_ci	iov = &cmd->iov_data[0];
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, iov, cmd->orig_iov_data_count - 2,
164062306a36Sopenharmony_ci				   be32_to_cpu(hdr->offset), payload_length);
164162306a36Sopenharmony_ci	if (iov_ret < 0)
164262306a36Sopenharmony_ci		return -1;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	iov_count += iov_ret;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	padding = ((-payload_length) & 3);
164762306a36Sopenharmony_ci	if (padding != 0) {
164862306a36Sopenharmony_ci		iov[iov_count].iov_base	= cmd->pad_bytes;
164962306a36Sopenharmony_ci		iov[iov_count++].iov_len = padding;
165062306a36Sopenharmony_ci		rx_size += padding;
165162306a36Sopenharmony_ci		pr_debug("Receiving %u padding bytes.\n", padding);
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
165562306a36Sopenharmony_ci		iov[iov_count].iov_base = &checksum;
165662306a36Sopenharmony_ci		iov[iov_count++].iov_len = ISCSI_CRC_LEN;
165762306a36Sopenharmony_ci		rx_size += ISCSI_CRC_LEN;
165862306a36Sopenharmony_ci	}
165962306a36Sopenharmony_ci
166062306a36Sopenharmony_ci	WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count);
166162306a36Sopenharmony_ci	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_ci	iscsit_unmap_iovec(cmd);
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if (rx_got != rx_size)
166662306a36Sopenharmony_ci		return -1;
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
166962306a36Sopenharmony_ci		u32 data_crc;
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci		data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd,
167262306a36Sopenharmony_ci						    be32_to_cpu(hdr->offset),
167362306a36Sopenharmony_ci						    payload_length, padding,
167462306a36Sopenharmony_ci						    cmd->pad_bytes);
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci		if (checksum != data_crc) {
167762306a36Sopenharmony_ci			pr_err("ITT: 0x%08x, Offset: %u, Length: %u,"
167862306a36Sopenharmony_ci				" DataSN: 0x%08x, CRC32C DataDigest 0x%08x"
167962306a36Sopenharmony_ci				" does not match computed 0x%08x\n",
168062306a36Sopenharmony_ci				hdr->itt, hdr->offset, payload_length,
168162306a36Sopenharmony_ci				hdr->datasn, checksum, data_crc);
168262306a36Sopenharmony_ci			data_crc_failed = 1;
168362306a36Sopenharmony_ci		} else {
168462306a36Sopenharmony_ci			pr_debug("Got CRC32C DataDigest 0x%08x for"
168562306a36Sopenharmony_ci				" %u bytes of Data Out\n", checksum,
168662306a36Sopenharmony_ci				payload_length);
168762306a36Sopenharmony_ci		}
168862306a36Sopenharmony_ci	}
168962306a36Sopenharmony_ci
169062306a36Sopenharmony_ci	return data_crc_failed;
169162306a36Sopenharmony_ci}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ciint
169462306a36Sopenharmony_ciiscsit_check_dataout_payload(struct iscsit_cmd *cmd, struct iscsi_data *hdr,
169562306a36Sopenharmony_ci			     bool data_crc_failed)
169662306a36Sopenharmony_ci{
169762306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
169862306a36Sopenharmony_ci	int rc, ooo_cmdsn;
169962306a36Sopenharmony_ci	/*
170062306a36Sopenharmony_ci	 * Increment post receive data and CRC values or perform
170162306a36Sopenharmony_ci	 * within-command recovery.
170262306a36Sopenharmony_ci	 */
170362306a36Sopenharmony_ci	rc = iscsit_check_post_dataout(cmd, (unsigned char *)hdr, data_crc_failed);
170462306a36Sopenharmony_ci	if ((rc == DATAOUT_NORMAL) || (rc == DATAOUT_WITHIN_COMMAND_RECOVERY))
170562306a36Sopenharmony_ci		return 0;
170662306a36Sopenharmony_ci	else if (rc == DATAOUT_SEND_R2T) {
170762306a36Sopenharmony_ci		iscsit_set_dataout_sequence_values(cmd);
170862306a36Sopenharmony_ci		conn->conn_transport->iscsit_get_dataout(conn, cmd, false);
170962306a36Sopenharmony_ci	} else if (rc == DATAOUT_SEND_TO_TRANSPORT) {
171062306a36Sopenharmony_ci		/*
171162306a36Sopenharmony_ci		 * Handle extra special case for out of order
171262306a36Sopenharmony_ci		 * Unsolicited Data Out.
171362306a36Sopenharmony_ci		 */
171462306a36Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
171562306a36Sopenharmony_ci		ooo_cmdsn = (cmd->cmd_flags & ICF_OOO_CMDSN);
171662306a36Sopenharmony_ci		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
171762306a36Sopenharmony_ci		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
171862306a36Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		iscsit_stop_dataout_timer(cmd);
172162306a36Sopenharmony_ci		if (ooo_cmdsn)
172262306a36Sopenharmony_ci			return 0;
172362306a36Sopenharmony_ci		target_execute_cmd(&cmd->se_cmd);
172462306a36Sopenharmony_ci		return 0;
172562306a36Sopenharmony_ci	} else /* DATAOUT_CANNOT_RECOVER */
172662306a36Sopenharmony_ci		return -1;
172762306a36Sopenharmony_ci
172862306a36Sopenharmony_ci	return 0;
172962306a36Sopenharmony_ci}
173062306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_check_dataout_payload);
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_cistatic int iscsit_handle_data_out(struct iscsit_conn *conn, unsigned char *buf)
173362306a36Sopenharmony_ci{
173462306a36Sopenharmony_ci	struct iscsit_cmd *cmd = NULL;
173562306a36Sopenharmony_ci	struct iscsi_data *hdr = (struct iscsi_data *)buf;
173662306a36Sopenharmony_ci	int rc;
173762306a36Sopenharmony_ci	bool data_crc_failed = false;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	rc = iscsit_check_dataout_hdr(conn, buf, &cmd);
174062306a36Sopenharmony_ci	if (rc < 0)
174162306a36Sopenharmony_ci		return 0;
174262306a36Sopenharmony_ci	else if (!cmd)
174362306a36Sopenharmony_ci		return 0;
174462306a36Sopenharmony_ci
174562306a36Sopenharmony_ci	rc = iscsit_get_dataout(conn, cmd, hdr);
174662306a36Sopenharmony_ci	if (rc < 0)
174762306a36Sopenharmony_ci		return rc;
174862306a36Sopenharmony_ci	else if (rc > 0)
174962306a36Sopenharmony_ci		data_crc_failed = true;
175062306a36Sopenharmony_ci
175162306a36Sopenharmony_ci	return iscsit_check_dataout_payload(cmd, hdr, data_crc_failed);
175262306a36Sopenharmony_ci}
175362306a36Sopenharmony_ci
175462306a36Sopenharmony_ciint iscsit_setup_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
175562306a36Sopenharmony_ci			 struct iscsi_nopout *hdr)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) {
176062306a36Sopenharmony_ci		pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n");
176162306a36Sopenharmony_ci		if (!cmd)
176262306a36Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
176362306a36Sopenharmony_ci						 (unsigned char *)hdr);
176462306a36Sopenharmony_ci
176562306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
176662306a36Sopenharmony_ci					 (unsigned char *)hdr);
176762306a36Sopenharmony_ci	}
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci	if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
177062306a36Sopenharmony_ci		pr_err("NOPOUT ITT is reserved, but Immediate Bit is"
177162306a36Sopenharmony_ci			" not set, protocol error.\n");
177262306a36Sopenharmony_ci		if (!cmd)
177362306a36Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
177462306a36Sopenharmony_ci						 (unsigned char *)hdr);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
177762306a36Sopenharmony_ci					 (unsigned char *)hdr);
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci
178062306a36Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
178162306a36Sopenharmony_ci		pr_err("NOPOUT Ping Data DataSegmentLength: %u is"
178262306a36Sopenharmony_ci			" greater than MaxXmitDataSegmentLength: %u, protocol"
178362306a36Sopenharmony_ci			" error.\n", payload_length,
178462306a36Sopenharmony_ci			conn->conn_ops->MaxXmitDataSegmentLength);
178562306a36Sopenharmony_ci		if (!cmd)
178662306a36Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
178762306a36Sopenharmony_ci						 (unsigned char *)hdr);
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
179062306a36Sopenharmony_ci					 (unsigned char *)hdr);
179162306a36Sopenharmony_ci	}
179262306a36Sopenharmony_ci
179362306a36Sopenharmony_ci	pr_debug("Got NOPOUT Ping %s ITT: 0x%08x, TTT: 0x%08x,"
179462306a36Sopenharmony_ci		" CmdSN: 0x%08x, ExpStatSN: 0x%08x, Length: %u\n",
179562306a36Sopenharmony_ci		hdr->itt == RESERVED_ITT ? "Response" : "Request",
179662306a36Sopenharmony_ci		hdr->itt, hdr->ttt, hdr->cmdsn, hdr->exp_statsn,
179762306a36Sopenharmony_ci		payload_length);
179862306a36Sopenharmony_ci	/*
179962306a36Sopenharmony_ci	 * This is not a response to a Unsolicited NopIN, which means
180062306a36Sopenharmony_ci	 * it can either be a NOPOUT ping request (with a valid ITT),
180162306a36Sopenharmony_ci	 * or a NOPOUT not requesting a NOPIN (with a reserved ITT).
180262306a36Sopenharmony_ci	 * Either way, make sure we allocate an struct iscsit_cmd, as both
180362306a36Sopenharmony_ci	 * can contain ping data.
180462306a36Sopenharmony_ci	 */
180562306a36Sopenharmony_ci	if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
180662306a36Sopenharmony_ci		cmd->iscsi_opcode	= ISCSI_OP_NOOP_OUT;
180762306a36Sopenharmony_ci		cmd->i_state		= ISTATE_SEND_NOPIN;
180862306a36Sopenharmony_ci		cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ?
180962306a36Sopenharmony_ci						1 : 0);
181062306a36Sopenharmony_ci		conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt;
181162306a36Sopenharmony_ci		cmd->targ_xfer_tag	= 0xFFFFFFFF;
181262306a36Sopenharmony_ci		cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
181362306a36Sopenharmony_ci		cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
181462306a36Sopenharmony_ci		cmd->data_direction	= DMA_NONE;
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	return 0;
181862306a36Sopenharmony_ci}
181962306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_nop_out);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ciint iscsit_process_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
182262306a36Sopenharmony_ci			   struct iscsi_nopout *hdr)
182362306a36Sopenharmony_ci{
182462306a36Sopenharmony_ci	struct iscsit_cmd *cmd_p = NULL;
182562306a36Sopenharmony_ci	int cmdsn_ret = 0;
182662306a36Sopenharmony_ci	/*
182762306a36Sopenharmony_ci	 * Initiator is expecting a NopIN ping reply..
182862306a36Sopenharmony_ci	 */
182962306a36Sopenharmony_ci	if (hdr->itt != RESERVED_ITT) {
183062306a36Sopenharmony_ci		if (!cmd)
183162306a36Sopenharmony_ci			return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
183262306a36Sopenharmony_ci						(unsigned char *)hdr);
183362306a36Sopenharmony_ci
183462306a36Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
183562306a36Sopenharmony_ci		list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
183662306a36Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
183762306a36Sopenharmony_ci
183862306a36Sopenharmony_ci		iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci		if (hdr->opcode & ISCSI_OP_IMMEDIATE) {
184162306a36Sopenharmony_ci			iscsit_add_cmd_to_response_queue(cmd, conn,
184262306a36Sopenharmony_ci							 cmd->i_state);
184362306a36Sopenharmony_ci			return 0;
184462306a36Sopenharmony_ci		}
184562306a36Sopenharmony_ci
184662306a36Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
184762306a36Sopenharmony_ci				(unsigned char *)hdr, hdr->cmdsn);
184862306a36Sopenharmony_ci                if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
184962306a36Sopenharmony_ci			return 0;
185062306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
185162306a36Sopenharmony_ci			return -1;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci		return 0;
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci	/*
185662306a36Sopenharmony_ci	 * This was a response to a unsolicited NOPIN ping.
185762306a36Sopenharmony_ci	 */
185862306a36Sopenharmony_ci	if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
185962306a36Sopenharmony_ci		cmd_p = iscsit_find_cmd_from_ttt(conn, be32_to_cpu(hdr->ttt));
186062306a36Sopenharmony_ci		if (!cmd_p)
186162306a36Sopenharmony_ci			return -EINVAL;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci		iscsit_stop_nopin_response_timer(conn);
186462306a36Sopenharmony_ci
186562306a36Sopenharmony_ci		cmd_p->i_state = ISTATE_REMOVE;
186662306a36Sopenharmony_ci		iscsit_add_cmd_to_immediate_queue(cmd_p, conn, cmd_p->i_state);
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci		iscsit_start_nopin_timer(conn);
186962306a36Sopenharmony_ci		return 0;
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci	/*
187262306a36Sopenharmony_ci	 * Otherwise, initiator is not expecting a NOPIN is response.
187362306a36Sopenharmony_ci	 * Just ignore for now.
187462306a36Sopenharmony_ci	 */
187562306a36Sopenharmony_ci
187662306a36Sopenharmony_ci	if (cmd)
187762306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci        return 0;
188062306a36Sopenharmony_ci}
188162306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_nop_out);
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_cistatic int iscsit_handle_nop_out(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
188462306a36Sopenharmony_ci				 unsigned char *buf)
188562306a36Sopenharmony_ci{
188662306a36Sopenharmony_ci	unsigned char *ping_data = NULL;
188762306a36Sopenharmony_ci	struct iscsi_nopout *hdr = (struct iscsi_nopout *)buf;
188862306a36Sopenharmony_ci	struct kvec *iov = NULL;
188962306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
189062306a36Sopenharmony_ci	int ret;
189162306a36Sopenharmony_ci
189262306a36Sopenharmony_ci	ret = iscsit_setup_nop_out(conn, cmd, hdr);
189362306a36Sopenharmony_ci	if (ret < 0)
189462306a36Sopenharmony_ci		return 0;
189562306a36Sopenharmony_ci	/*
189662306a36Sopenharmony_ci	 * Handle NOP-OUT payload for traditional iSCSI sockets
189762306a36Sopenharmony_ci	 */
189862306a36Sopenharmony_ci	if (payload_length && hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
189962306a36Sopenharmony_ci		u32 checksum, data_crc, padding = 0;
190062306a36Sopenharmony_ci		int niov = 0, rx_got, rx_size = payload_length;
190162306a36Sopenharmony_ci
190262306a36Sopenharmony_ci		ping_data = kzalloc(payload_length + 1, GFP_KERNEL);
190362306a36Sopenharmony_ci		if (!ping_data) {
190462306a36Sopenharmony_ci			ret = -1;
190562306a36Sopenharmony_ci			goto out;
190662306a36Sopenharmony_ci		}
190762306a36Sopenharmony_ci
190862306a36Sopenharmony_ci		iov = &cmd->iov_misc[0];
190962306a36Sopenharmony_ci		iov[niov].iov_base	= ping_data;
191062306a36Sopenharmony_ci		iov[niov++].iov_len	= payload_length;
191162306a36Sopenharmony_ci
191262306a36Sopenharmony_ci		padding = ((-payload_length) & 3);
191362306a36Sopenharmony_ci		if (padding != 0) {
191462306a36Sopenharmony_ci			pr_debug("Receiving %u additional bytes"
191562306a36Sopenharmony_ci				" for padding.\n", padding);
191662306a36Sopenharmony_ci			iov[niov].iov_base	= &cmd->pad_bytes;
191762306a36Sopenharmony_ci			iov[niov++].iov_len	= padding;
191862306a36Sopenharmony_ci			rx_size += padding;
191962306a36Sopenharmony_ci		}
192062306a36Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
192162306a36Sopenharmony_ci			iov[niov].iov_base	= &checksum;
192262306a36Sopenharmony_ci			iov[niov++].iov_len	= ISCSI_CRC_LEN;
192362306a36Sopenharmony_ci			rx_size += ISCSI_CRC_LEN;
192462306a36Sopenharmony_ci		}
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci		WARN_ON_ONCE(niov > ARRAY_SIZE(cmd->iov_misc));
192762306a36Sopenharmony_ci		rx_got = rx_data(conn, &cmd->iov_misc[0], niov, rx_size);
192862306a36Sopenharmony_ci		if (rx_got != rx_size) {
192962306a36Sopenharmony_ci			ret = -1;
193062306a36Sopenharmony_ci			goto out;
193162306a36Sopenharmony_ci		}
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
193462306a36Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash, ping_data,
193562306a36Sopenharmony_ci						  payload_length, padding,
193662306a36Sopenharmony_ci						  cmd->pad_bytes, &data_crc);
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci			if (checksum != data_crc) {
193962306a36Sopenharmony_ci				pr_err("Ping data CRC32C DataDigest"
194062306a36Sopenharmony_ci				" 0x%08x does not match computed 0x%08x\n",
194162306a36Sopenharmony_ci					checksum, data_crc);
194262306a36Sopenharmony_ci				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
194362306a36Sopenharmony_ci					pr_err("Unable to recover from"
194462306a36Sopenharmony_ci					" NOPOUT Ping DataCRC failure while in"
194562306a36Sopenharmony_ci						" ERL=0.\n");
194662306a36Sopenharmony_ci					ret = -1;
194762306a36Sopenharmony_ci					goto out;
194862306a36Sopenharmony_ci				} else {
194962306a36Sopenharmony_ci					/*
195062306a36Sopenharmony_ci					 * Silently drop this PDU and let the
195162306a36Sopenharmony_ci					 * initiator plug the CmdSN gap.
195262306a36Sopenharmony_ci					 */
195362306a36Sopenharmony_ci					pr_debug("Dropping NOPOUT"
195462306a36Sopenharmony_ci					" Command CmdSN: 0x%08x due to"
195562306a36Sopenharmony_ci					" DataCRC error.\n", hdr->cmdsn);
195662306a36Sopenharmony_ci					ret = 0;
195762306a36Sopenharmony_ci					goto out;
195862306a36Sopenharmony_ci				}
195962306a36Sopenharmony_ci			} else {
196062306a36Sopenharmony_ci				pr_debug("Got CRC32C DataDigest"
196162306a36Sopenharmony_ci				" 0x%08x for %u bytes of ping data.\n",
196262306a36Sopenharmony_ci					checksum, payload_length);
196362306a36Sopenharmony_ci			}
196462306a36Sopenharmony_ci		}
196562306a36Sopenharmony_ci
196662306a36Sopenharmony_ci		ping_data[payload_length] = '\0';
196762306a36Sopenharmony_ci		/*
196862306a36Sopenharmony_ci		 * Attach ping data to struct iscsit_cmd->buf_ptr.
196962306a36Sopenharmony_ci		 */
197062306a36Sopenharmony_ci		cmd->buf_ptr = ping_data;
197162306a36Sopenharmony_ci		cmd->buf_ptr_size = payload_length;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci		pr_debug("Got %u bytes of NOPOUT ping"
197462306a36Sopenharmony_ci			" data.\n", payload_length);
197562306a36Sopenharmony_ci		pr_debug("Ping Data: \"%s\"\n", ping_data);
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	return iscsit_process_nop_out(conn, cmd, hdr);
197962306a36Sopenharmony_ciout:
198062306a36Sopenharmony_ci	if (cmd)
198162306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	kfree(ping_data);
198462306a36Sopenharmony_ci	return ret;
198562306a36Sopenharmony_ci}
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_cistatic enum tcm_tmreq_table iscsit_convert_tmf(u8 iscsi_tmf)
198862306a36Sopenharmony_ci{
198962306a36Sopenharmony_ci	switch (iscsi_tmf) {
199062306a36Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK:
199162306a36Sopenharmony_ci		return TMR_ABORT_TASK;
199262306a36Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK_SET:
199362306a36Sopenharmony_ci		return TMR_ABORT_TASK_SET;
199462306a36Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_ACA:
199562306a36Sopenharmony_ci		return TMR_CLEAR_ACA;
199662306a36Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
199762306a36Sopenharmony_ci		return TMR_CLEAR_TASK_SET;
199862306a36Sopenharmony_ci	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
199962306a36Sopenharmony_ci		return TMR_LUN_RESET;
200062306a36Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
200162306a36Sopenharmony_ci		return TMR_TARGET_WARM_RESET;
200262306a36Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
200362306a36Sopenharmony_ci		return TMR_TARGET_COLD_RESET;
200462306a36Sopenharmony_ci	default:
200562306a36Sopenharmony_ci		return TMR_UNKNOWN;
200662306a36Sopenharmony_ci	}
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_ciint
201062306a36Sopenharmony_ciiscsit_handle_task_mgt_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
201162306a36Sopenharmony_ci			   unsigned char *buf)
201262306a36Sopenharmony_ci{
201362306a36Sopenharmony_ci	struct se_tmr_req *se_tmr;
201462306a36Sopenharmony_ci	struct iscsi_tmr_req *tmr_req;
201562306a36Sopenharmony_ci	struct iscsi_tm *hdr;
201662306a36Sopenharmony_ci	int out_of_order_cmdsn = 0, ret;
201762306a36Sopenharmony_ci	u8 function, tcm_function = TMR_UNKNOWN;
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	hdr			= (struct iscsi_tm *) buf;
202062306a36Sopenharmony_ci	hdr->flags &= ~ISCSI_FLAG_CMD_FINAL;
202162306a36Sopenharmony_ci	function = hdr->flags;
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	pr_debug("Got Task Management Request ITT: 0x%08x, CmdSN:"
202462306a36Sopenharmony_ci		" 0x%08x, Function: 0x%02x, RefTaskTag: 0x%08x, RefCmdSN:"
202562306a36Sopenharmony_ci		" 0x%08x, CID: %hu\n", hdr->itt, hdr->cmdsn, function,
202662306a36Sopenharmony_ci		hdr->rtt, hdr->refcmdsn, conn->cid);
202762306a36Sopenharmony_ci
202862306a36Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
202962306a36Sopenharmony_ci	    ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
203062306a36Sopenharmony_ci	     hdr->rtt != RESERVED_ITT)) {
203162306a36Sopenharmony_ci		pr_err("RefTaskTag should be set to 0xFFFFFFFF.\n");
203262306a36Sopenharmony_ci		hdr->rtt = RESERVED_ITT;
203362306a36Sopenharmony_ci	}
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci	if ((function == ISCSI_TM_FUNC_TASK_REASSIGN) &&
203662306a36Sopenharmony_ci			!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
203762306a36Sopenharmony_ci		pr_err("Task Management Request TASK_REASSIGN not"
203862306a36Sopenharmony_ci			" issued as immediate command, bad iSCSI Initiator"
203962306a36Sopenharmony_ci				"implementation\n");
204062306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
204162306a36Sopenharmony_ci					     ISCSI_REASON_PROTOCOL_ERROR, buf);
204262306a36Sopenharmony_ci	}
204362306a36Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_ABORT_TASK) &&
204462306a36Sopenharmony_ci	    be32_to_cpu(hdr->refcmdsn) != ISCSI_RESERVED_TAG)
204562306a36Sopenharmony_ci		hdr->refcmdsn = cpu_to_be32(ISCSI_RESERVED_TAG);
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	cmd->data_direction = DMA_NONE;
204862306a36Sopenharmony_ci	cmd->tmr_req = kzalloc(sizeof(*cmd->tmr_req), GFP_KERNEL);
204962306a36Sopenharmony_ci	if (!cmd->tmr_req) {
205062306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
205162306a36Sopenharmony_ci					     ISCSI_REASON_BOOKMARK_NO_RESOURCES,
205262306a36Sopenharmony_ci					     buf);
205362306a36Sopenharmony_ci	}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci	__target_init_cmd(&cmd->se_cmd, &iscsi_ops,
205662306a36Sopenharmony_ci			  conn->sess->se_sess, 0, DMA_NONE,
205762306a36Sopenharmony_ci			  TCM_SIMPLE_TAG, cmd->sense_buffer + 2,
205862306a36Sopenharmony_ci			  scsilun_to_int(&hdr->lun),
205962306a36Sopenharmony_ci			  conn->cmd_cnt);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	target_get_sess_cmd(&cmd->se_cmd, true);
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci	/*
206462306a36Sopenharmony_ci	 * TASK_REASSIGN for ERL=2 / connection stays inside of
206562306a36Sopenharmony_ci	 * LIO-Target $FABRIC_MOD
206662306a36Sopenharmony_ci	 */
206762306a36Sopenharmony_ci	if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
206862306a36Sopenharmony_ci		tcm_function = iscsit_convert_tmf(function);
206962306a36Sopenharmony_ci		if (tcm_function == TMR_UNKNOWN) {
207062306a36Sopenharmony_ci			pr_err("Unknown iSCSI TMR Function:"
207162306a36Sopenharmony_ci			       " 0x%02x\n", function);
207262306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
207362306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
207462306a36Sopenharmony_ci		}
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci	ret = core_tmr_alloc_req(&cmd->se_cmd, cmd->tmr_req, tcm_function,
207762306a36Sopenharmony_ci				 GFP_KERNEL);
207862306a36Sopenharmony_ci	if (ret < 0)
207962306a36Sopenharmony_ci		return iscsit_add_reject_cmd(cmd,
208062306a36Sopenharmony_ci				ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	cmd->tmr_req->se_tmr_req = cmd->se_cmd.se_tmr_req;
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_SCSI_TMFUNC;
208562306a36Sopenharmony_ci	cmd->i_state		= ISTATE_SEND_TASKMGTRSP;
208662306a36Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
208762306a36Sopenharmony_ci	cmd->init_task_tag	= hdr->itt;
208862306a36Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
208962306a36Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
209062306a36Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
209162306a36Sopenharmony_ci	se_tmr			= cmd->se_cmd.se_tmr_req;
209262306a36Sopenharmony_ci	tmr_req			= cmd->tmr_req;
209362306a36Sopenharmony_ci	/*
209462306a36Sopenharmony_ci	 * Locate the struct se_lun for all TMRs not related to ERL=2 TASK_REASSIGN
209562306a36Sopenharmony_ci	 */
209662306a36Sopenharmony_ci	if (function != ISCSI_TM_FUNC_TASK_REASSIGN) {
209762306a36Sopenharmony_ci		ret = transport_lookup_tmr_lun(&cmd->se_cmd);
209862306a36Sopenharmony_ci		if (ret < 0) {
209962306a36Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_NO_LUN;
210062306a36Sopenharmony_ci			goto attach;
210162306a36Sopenharmony_ci		}
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci	switch (function) {
210562306a36Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK:
210662306a36Sopenharmony_ci		se_tmr->response = iscsit_tmr_abort_task(cmd, buf);
210762306a36Sopenharmony_ci		if (se_tmr->response)
210862306a36Sopenharmony_ci			goto attach;
210962306a36Sopenharmony_ci		break;
211062306a36Sopenharmony_ci	case ISCSI_TM_FUNC_ABORT_TASK_SET:
211162306a36Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_ACA:
211262306a36Sopenharmony_ci	case ISCSI_TM_FUNC_CLEAR_TASK_SET:
211362306a36Sopenharmony_ci	case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET:
211462306a36Sopenharmony_ci		break;
211562306a36Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_WARM_RESET:
211662306a36Sopenharmony_ci		if (iscsit_tmr_task_warm_reset(conn, tmr_req, buf) < 0) {
211762306a36Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
211862306a36Sopenharmony_ci			goto attach;
211962306a36Sopenharmony_ci		}
212062306a36Sopenharmony_ci		break;
212162306a36Sopenharmony_ci	case ISCSI_TM_FUNC_TARGET_COLD_RESET:
212262306a36Sopenharmony_ci		if (iscsit_tmr_task_cold_reset(conn, tmr_req, buf) < 0) {
212362306a36Sopenharmony_ci			se_tmr->response = ISCSI_TMF_RSP_AUTH_FAILED;
212462306a36Sopenharmony_ci			goto attach;
212562306a36Sopenharmony_ci		}
212662306a36Sopenharmony_ci		break;
212762306a36Sopenharmony_ci	case ISCSI_TM_FUNC_TASK_REASSIGN:
212862306a36Sopenharmony_ci		se_tmr->response = iscsit_tmr_task_reassign(cmd, buf);
212962306a36Sopenharmony_ci		/*
213062306a36Sopenharmony_ci		 * Perform sanity checks on the ExpDataSN only if the
213162306a36Sopenharmony_ci		 * TASK_REASSIGN was successful.
213262306a36Sopenharmony_ci		 */
213362306a36Sopenharmony_ci		if (se_tmr->response)
213462306a36Sopenharmony_ci			break;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci		if (iscsit_check_task_reassign_expdatasn(tmr_req, conn) < 0)
213762306a36Sopenharmony_ci			return iscsit_add_reject_cmd(cmd,
213862306a36Sopenharmony_ci					ISCSI_REASON_BOOKMARK_INVALID, buf);
213962306a36Sopenharmony_ci		break;
214062306a36Sopenharmony_ci	default:
214162306a36Sopenharmony_ci		pr_err("Unknown TMR function: 0x%02x, protocol"
214262306a36Sopenharmony_ci			" error.\n", function);
214362306a36Sopenharmony_ci		se_tmr->response = ISCSI_TMF_RSP_NOT_SUPPORTED;
214462306a36Sopenharmony_ci		goto attach;
214562306a36Sopenharmony_ci	}
214662306a36Sopenharmony_ci
214762306a36Sopenharmony_ci	if ((function != ISCSI_TM_FUNC_TASK_REASSIGN) &&
214862306a36Sopenharmony_ci	    (se_tmr->response == ISCSI_TMF_RSP_COMPLETE))
214962306a36Sopenharmony_ci		se_tmr->call_transport = 1;
215062306a36Sopenharmony_ciattach:
215162306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
215262306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
215362306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_ci	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
215662306a36Sopenharmony_ci		int cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
215762306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_HIGHER_THAN_EXP) {
215862306a36Sopenharmony_ci			out_of_order_cmdsn = 1;
215962306a36Sopenharmony_ci		} else if (cmdsn_ret == CMDSN_LOWER_THAN_EXP) {
216062306a36Sopenharmony_ci			target_put_sess_cmd(&cmd->se_cmd);
216162306a36Sopenharmony_ci			return 0;
216262306a36Sopenharmony_ci		} else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER) {
216362306a36Sopenharmony_ci			return -1;
216462306a36Sopenharmony_ci		}
216562306a36Sopenharmony_ci	}
216662306a36Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	if (out_of_order_cmdsn || !(hdr->opcode & ISCSI_OP_IMMEDIATE))
216962306a36Sopenharmony_ci		return 0;
217062306a36Sopenharmony_ci	/*
217162306a36Sopenharmony_ci	 * Found the referenced task, send to transport for processing.
217262306a36Sopenharmony_ci	 */
217362306a36Sopenharmony_ci	if (se_tmr->call_transport)
217462306a36Sopenharmony_ci		return transport_generic_handle_tmr(&cmd->se_cmd);
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci	/*
217762306a36Sopenharmony_ci	 * Could not find the referenced LUN, task, or Task Management
217862306a36Sopenharmony_ci	 * command not authorized or supported.  Change state and
217962306a36Sopenharmony_ci	 * let the tx_thread send the response.
218062306a36Sopenharmony_ci	 *
218162306a36Sopenharmony_ci	 * For connection recovery, this is also the default action for
218262306a36Sopenharmony_ci	 * TMR TASK_REASSIGN.
218362306a36Sopenharmony_ci	 */
218462306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
218562306a36Sopenharmony_ci	target_put_sess_cmd(&cmd->se_cmd);
218662306a36Sopenharmony_ci	return 0;
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_task_mgt_cmd);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci/* #warning FIXME: Support Text Command parameters besides SendTargets */
219162306a36Sopenharmony_ciint
219262306a36Sopenharmony_ciiscsit_setup_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
219362306a36Sopenharmony_ci		      struct iscsi_text *hdr)
219462306a36Sopenharmony_ci{
219562306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci	if (payload_length > conn->conn_ops->MaxXmitDataSegmentLength) {
219862306a36Sopenharmony_ci		pr_err("Unable to accept text parameter length: %u"
219962306a36Sopenharmony_ci			"greater than MaxXmitDataSegmentLength %u.\n",
220062306a36Sopenharmony_ci		       payload_length, conn->conn_ops->MaxXmitDataSegmentLength);
220162306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
220262306a36Sopenharmony_ci					 (unsigned char *)hdr);
220362306a36Sopenharmony_ci	}
220462306a36Sopenharmony_ci
220562306a36Sopenharmony_ci	if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL) ||
220662306a36Sopenharmony_ci	     (hdr->flags & ISCSI_FLAG_TEXT_CONTINUE)) {
220762306a36Sopenharmony_ci		pr_err("Multi sequence text commands currently not supported\n");
220862306a36Sopenharmony_ci		return iscsit_reject_cmd(cmd, ISCSI_REASON_CMD_NOT_SUPPORTED,
220962306a36Sopenharmony_ci					(unsigned char *)hdr);
221062306a36Sopenharmony_ci	}
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci	pr_debug("Got Text Request: ITT: 0x%08x, CmdSN: 0x%08x,"
221362306a36Sopenharmony_ci		" ExpStatSN: 0x%08x, Length: %u\n", hdr->itt, hdr->cmdsn,
221462306a36Sopenharmony_ci		hdr->exp_statsn, payload_length);
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	cmd->iscsi_opcode	= ISCSI_OP_TEXT;
221762306a36Sopenharmony_ci	cmd->i_state		= ISTATE_SEND_TEXTRSP;
221862306a36Sopenharmony_ci	cmd->immediate_cmd	= ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
221962306a36Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag  = hdr->itt;
222062306a36Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
222162306a36Sopenharmony_ci	cmd->cmd_sn		= be32_to_cpu(hdr->cmdsn);
222262306a36Sopenharmony_ci	cmd->exp_stat_sn	= be32_to_cpu(hdr->exp_statsn);
222362306a36Sopenharmony_ci	cmd->data_direction	= DMA_NONE;
222462306a36Sopenharmony_ci	kfree(cmd->text_in_ptr);
222562306a36Sopenharmony_ci	cmd->text_in_ptr	= NULL;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	return 0;
222862306a36Sopenharmony_ci}
222962306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_setup_text_cmd);
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ciint
223262306a36Sopenharmony_ciiscsit_process_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
223362306a36Sopenharmony_ci			struct iscsi_text *hdr)
223462306a36Sopenharmony_ci{
223562306a36Sopenharmony_ci	unsigned char *text_in = cmd->text_in_ptr, *text_ptr;
223662306a36Sopenharmony_ci	int cmdsn_ret;
223762306a36Sopenharmony_ci
223862306a36Sopenharmony_ci	if (!text_in) {
223962306a36Sopenharmony_ci		cmd->targ_xfer_tag = be32_to_cpu(hdr->ttt);
224062306a36Sopenharmony_ci		if (cmd->targ_xfer_tag == 0xFFFFFFFF) {
224162306a36Sopenharmony_ci			pr_err("Unable to locate text_in buffer for sendtargets"
224262306a36Sopenharmony_ci			       " discovery\n");
224362306a36Sopenharmony_ci			goto reject;
224462306a36Sopenharmony_ci		}
224562306a36Sopenharmony_ci		goto empty_sendtargets;
224662306a36Sopenharmony_ci	}
224762306a36Sopenharmony_ci	if (strncmp("SendTargets=", text_in, 12) != 0) {
224862306a36Sopenharmony_ci		pr_err("Received Text Data that is not"
224962306a36Sopenharmony_ci			" SendTargets, cannot continue.\n");
225062306a36Sopenharmony_ci		goto reject;
225162306a36Sopenharmony_ci	}
225262306a36Sopenharmony_ci	/* '=' confirmed in strncmp */
225362306a36Sopenharmony_ci	text_ptr = strchr(text_in, '=');
225462306a36Sopenharmony_ci	BUG_ON(!text_ptr);
225562306a36Sopenharmony_ci	if (!strncmp("=All", text_ptr, 5)) {
225662306a36Sopenharmony_ci		cmd->cmd_flags |= ICF_SENDTARGETS_ALL;
225762306a36Sopenharmony_ci	} else if (!strncmp("=iqn.", text_ptr, 5) ||
225862306a36Sopenharmony_ci		   !strncmp("=eui.", text_ptr, 5)) {
225962306a36Sopenharmony_ci		cmd->cmd_flags |= ICF_SENDTARGETS_SINGLE;
226062306a36Sopenharmony_ci	} else {
226162306a36Sopenharmony_ci		pr_err("Unable to locate valid SendTargets%s value\n",
226262306a36Sopenharmony_ci		       text_ptr);
226362306a36Sopenharmony_ci		goto reject;
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
226762306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
226862306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ciempty_sendtargets:
227162306a36Sopenharmony_ci	iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	if (!(hdr->opcode & ISCSI_OP_IMMEDIATE)) {
227462306a36Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd,
227562306a36Sopenharmony_ci				(unsigned char *)hdr, hdr->cmdsn);
227662306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
227762306a36Sopenharmony_ci			return -1;
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_ci		return 0;
228062306a36Sopenharmony_ci	}
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	return iscsit_execute_cmd(cmd, 0);
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_cireject:
228562306a36Sopenharmony_ci	return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR,
228662306a36Sopenharmony_ci				 (unsigned char *)hdr);
228762306a36Sopenharmony_ci}
228862306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_process_text_cmd);
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_cistatic int
229162306a36Sopenharmony_ciiscsit_handle_text_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
229262306a36Sopenharmony_ci		       unsigned char *buf)
229362306a36Sopenharmony_ci{
229462306a36Sopenharmony_ci	struct iscsi_text *hdr = (struct iscsi_text *)buf;
229562306a36Sopenharmony_ci	char *text_in = NULL;
229662306a36Sopenharmony_ci	u32 payload_length = ntoh24(hdr->dlength);
229762306a36Sopenharmony_ci	int rx_size, rc;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	rc = iscsit_setup_text_cmd(conn, cmd, hdr);
230062306a36Sopenharmony_ci	if (rc < 0)
230162306a36Sopenharmony_ci		return 0;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	rx_size = payload_length;
230462306a36Sopenharmony_ci	if (payload_length) {
230562306a36Sopenharmony_ci		u32 checksum = 0, data_crc = 0;
230662306a36Sopenharmony_ci		u32 padding = 0;
230762306a36Sopenharmony_ci		int niov = 0, rx_got;
230862306a36Sopenharmony_ci		struct kvec iov[2];
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci		rx_size = ALIGN(payload_length, 4);
231162306a36Sopenharmony_ci		text_in = kzalloc(rx_size, GFP_KERNEL);
231262306a36Sopenharmony_ci		if (!text_in)
231362306a36Sopenharmony_ci			goto reject;
231462306a36Sopenharmony_ci
231562306a36Sopenharmony_ci		cmd->text_in_ptr = text_in;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci		memset(iov, 0, sizeof(iov));
231862306a36Sopenharmony_ci		iov[niov].iov_base	= text_in;
231962306a36Sopenharmony_ci		iov[niov++].iov_len	= rx_size;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci		padding = rx_size - payload_length;
232262306a36Sopenharmony_ci		if (padding)
232362306a36Sopenharmony_ci			pr_debug("Receiving %u additional bytes"
232462306a36Sopenharmony_ci					" for padding.\n", padding);
232562306a36Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
232662306a36Sopenharmony_ci			iov[niov].iov_base	= &checksum;
232762306a36Sopenharmony_ci			iov[niov++].iov_len	= ISCSI_CRC_LEN;
232862306a36Sopenharmony_ci			rx_size += ISCSI_CRC_LEN;
232962306a36Sopenharmony_ci		}
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci		WARN_ON_ONCE(niov > ARRAY_SIZE(iov));
233262306a36Sopenharmony_ci		rx_got = rx_data(conn, &iov[0], niov, rx_size);
233362306a36Sopenharmony_ci		if (rx_got != rx_size)
233462306a36Sopenharmony_ci			goto reject;
233562306a36Sopenharmony_ci
233662306a36Sopenharmony_ci		if (conn->conn_ops->DataDigest) {
233762306a36Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash,
233862306a36Sopenharmony_ci						  text_in, rx_size, 0, NULL,
233962306a36Sopenharmony_ci						  &data_crc);
234062306a36Sopenharmony_ci
234162306a36Sopenharmony_ci			if (checksum != data_crc) {
234262306a36Sopenharmony_ci				pr_err("Text data CRC32C DataDigest"
234362306a36Sopenharmony_ci					" 0x%08x does not match computed"
234462306a36Sopenharmony_ci					" 0x%08x\n", checksum, data_crc);
234562306a36Sopenharmony_ci				if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
234662306a36Sopenharmony_ci					pr_err("Unable to recover from"
234762306a36Sopenharmony_ci					" Text Data digest failure while in"
234862306a36Sopenharmony_ci						" ERL=0.\n");
234962306a36Sopenharmony_ci					goto reject;
235062306a36Sopenharmony_ci				} else {
235162306a36Sopenharmony_ci					/*
235262306a36Sopenharmony_ci					 * Silently drop this PDU and let the
235362306a36Sopenharmony_ci					 * initiator plug the CmdSN gap.
235462306a36Sopenharmony_ci					 */
235562306a36Sopenharmony_ci					pr_debug("Dropping Text"
235662306a36Sopenharmony_ci					" Command CmdSN: 0x%08x due to"
235762306a36Sopenharmony_ci					" DataCRC error.\n", hdr->cmdsn);
235862306a36Sopenharmony_ci					kfree(text_in);
235962306a36Sopenharmony_ci					return 0;
236062306a36Sopenharmony_ci				}
236162306a36Sopenharmony_ci			} else {
236262306a36Sopenharmony_ci				pr_debug("Got CRC32C DataDigest"
236362306a36Sopenharmony_ci					" 0x%08x for %u bytes of text data.\n",
236462306a36Sopenharmony_ci						checksum, payload_length);
236562306a36Sopenharmony_ci			}
236662306a36Sopenharmony_ci		}
236762306a36Sopenharmony_ci		text_in[payload_length - 1] = '\0';
236862306a36Sopenharmony_ci		pr_debug("Successfully read %d bytes of text"
236962306a36Sopenharmony_ci				" data.\n", payload_length);
237062306a36Sopenharmony_ci	}
237162306a36Sopenharmony_ci
237262306a36Sopenharmony_ci	return iscsit_process_text_cmd(conn, cmd, hdr);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_cireject:
237562306a36Sopenharmony_ci	kfree(cmd->text_in_ptr);
237662306a36Sopenharmony_ci	cmd->text_in_ptr = NULL;
237762306a36Sopenharmony_ci	return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, buf);
237862306a36Sopenharmony_ci}
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ciint iscsit_logout_closesession(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
238162306a36Sopenharmony_ci{
238262306a36Sopenharmony_ci	struct iscsit_conn *conn_p;
238362306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	pr_debug("Received logout request CLOSESESSION on CID: %hu"
238662306a36Sopenharmony_ci		" for SID: %u.\n", conn->cid, conn->sess->sid);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	atomic_set(&sess->session_logout, 1);
238962306a36Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 1);
239062306a36Sopenharmony_ci	conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_SESSION;
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	iscsit_inc_conn_usage_count(conn);
239362306a36Sopenharmony_ci	iscsit_inc_session_usage_count(sess);
239462306a36Sopenharmony_ci
239562306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
239662306a36Sopenharmony_ci	list_for_each_entry(conn_p, &sess->sess_conn_list, conn_list) {
239762306a36Sopenharmony_ci		if (conn_p->conn_state != TARG_CONN_STATE_LOGGED_IN)
239862306a36Sopenharmony_ci			continue;
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
240162306a36Sopenharmony_ci		conn_p->conn_state = TARG_CONN_STATE_IN_LOGOUT;
240262306a36Sopenharmony_ci	}
240362306a36Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
240662306a36Sopenharmony_ci
240762306a36Sopenharmony_ci	return 0;
240862306a36Sopenharmony_ci}
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ciint iscsit_logout_closeconnection(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
241162306a36Sopenharmony_ci{
241262306a36Sopenharmony_ci	struct iscsit_conn *l_conn;
241362306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci	pr_debug("Received logout request CLOSECONNECTION for CID:"
241662306a36Sopenharmony_ci		" %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci	/*
241962306a36Sopenharmony_ci	 * A Logout Request with a CLOSECONNECTION reason code for a CID
242062306a36Sopenharmony_ci	 * can arrive on a connection with a differing CID.
242162306a36Sopenharmony_ci	 */
242262306a36Sopenharmony_ci	if (conn->cid == cmd->logout_cid) {
242362306a36Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
242462306a36Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
242562306a36Sopenharmony_ci		conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
242662306a36Sopenharmony_ci
242762306a36Sopenharmony_ci		atomic_set(&conn->conn_logout_remove, 1);
242862306a36Sopenharmony_ci		conn->conn_logout_reason = ISCSI_LOGOUT_REASON_CLOSE_CONNECTION;
242962306a36Sopenharmony_ci		iscsit_inc_conn_usage_count(conn);
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
243262306a36Sopenharmony_ci	} else {
243362306a36Sopenharmony_ci		/*
243462306a36Sopenharmony_ci		 * Handle all different cid CLOSECONNECTION requests in
243562306a36Sopenharmony_ci		 * iscsit_logout_post_handler_diffcid() as to give enough
243662306a36Sopenharmony_ci		 * time for any non immediate command's CmdSN to be
243762306a36Sopenharmony_ci		 * acknowledged on the connection in question.
243862306a36Sopenharmony_ci		 *
243962306a36Sopenharmony_ci		 * Here we simply make sure the CID is still around.
244062306a36Sopenharmony_ci		 */
244162306a36Sopenharmony_ci		l_conn = iscsit_get_conn_from_cid(sess,
244262306a36Sopenharmony_ci				cmd->logout_cid);
244362306a36Sopenharmony_ci		if (!l_conn) {
244462306a36Sopenharmony_ci			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
244562306a36Sopenharmony_ci			iscsit_add_cmd_to_response_queue(cmd, conn,
244662306a36Sopenharmony_ci					cmd->i_state);
244762306a36Sopenharmony_ci			return 0;
244862306a36Sopenharmony_ci		}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci		iscsit_dec_conn_usage_count(l_conn);
245162306a36Sopenharmony_ci	}
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
245462306a36Sopenharmony_ci
245562306a36Sopenharmony_ci	return 0;
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ciint iscsit_logout_removeconnforrecovery(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
245962306a36Sopenharmony_ci{
246062306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
246162306a36Sopenharmony_ci
246262306a36Sopenharmony_ci	pr_debug("Received explicit REMOVECONNFORRECOVERY logout for"
246362306a36Sopenharmony_ci		" CID: %hu on CID: %hu.\n", cmd->logout_cid, conn->cid);
246462306a36Sopenharmony_ci
246562306a36Sopenharmony_ci	if (sess->sess_ops->ErrorRecoveryLevel != 2) {
246662306a36Sopenharmony_ci		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
246762306a36Sopenharmony_ci			" while ERL!=2.\n");
246862306a36Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_RECOVERY_UNSUPPORTED;
246962306a36Sopenharmony_ci		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
247062306a36Sopenharmony_ci		return 0;
247162306a36Sopenharmony_ci	}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	if (conn->cid == cmd->logout_cid) {
247462306a36Sopenharmony_ci		pr_err("Received Logout Request REMOVECONNFORRECOVERY"
247562306a36Sopenharmony_ci			" with CID: %hu on CID: %hu, implementation error.\n",
247662306a36Sopenharmony_ci				cmd->logout_cid, conn->cid);
247762306a36Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_CLEANUP_FAILED;
247862306a36Sopenharmony_ci		iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
247962306a36Sopenharmony_ci		return 0;
248062306a36Sopenharmony_ci	}
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn, cmd->i_state);
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	return 0;
248562306a36Sopenharmony_ci}
248662306a36Sopenharmony_ci
248762306a36Sopenharmony_ciint
248862306a36Sopenharmony_ciiscsit_handle_logout_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd,
248962306a36Sopenharmony_ci			unsigned char *buf)
249062306a36Sopenharmony_ci{
249162306a36Sopenharmony_ci	int cmdsn_ret, logout_remove = 0;
249262306a36Sopenharmony_ci	u8 reason_code = 0;
249362306a36Sopenharmony_ci	struct iscsi_logout *hdr;
249462306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn = iscsit_snmp_get_tiqn(conn);
249562306a36Sopenharmony_ci
249662306a36Sopenharmony_ci	hdr			= (struct iscsi_logout *) buf;
249762306a36Sopenharmony_ci	reason_code		= (hdr->flags & 0x7f);
249862306a36Sopenharmony_ci
249962306a36Sopenharmony_ci	if (tiqn) {
250062306a36Sopenharmony_ci		spin_lock(&tiqn->logout_stats.lock);
250162306a36Sopenharmony_ci		if (reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION)
250262306a36Sopenharmony_ci			tiqn->logout_stats.normal_logouts++;
250362306a36Sopenharmony_ci		else
250462306a36Sopenharmony_ci			tiqn->logout_stats.abnormal_logouts++;
250562306a36Sopenharmony_ci		spin_unlock(&tiqn->logout_stats.lock);
250662306a36Sopenharmony_ci	}
250762306a36Sopenharmony_ci
250862306a36Sopenharmony_ci	pr_debug("Got Logout Request ITT: 0x%08x CmdSN: 0x%08x"
250962306a36Sopenharmony_ci		" ExpStatSN: 0x%08x Reason: 0x%02x CID: %hu on CID: %hu\n",
251062306a36Sopenharmony_ci		hdr->itt, hdr->cmdsn, hdr->exp_statsn, reason_code,
251162306a36Sopenharmony_ci		hdr->cid, conn->cid);
251262306a36Sopenharmony_ci
251362306a36Sopenharmony_ci	if (conn->conn_state != TARG_CONN_STATE_LOGGED_IN) {
251462306a36Sopenharmony_ci		pr_err("Received logout request on connection that"
251562306a36Sopenharmony_ci			" is not in logged in state, ignoring request.\n");
251662306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
251762306a36Sopenharmony_ci		return 0;
251862306a36Sopenharmony_ci	}
251962306a36Sopenharmony_ci
252062306a36Sopenharmony_ci	cmd->iscsi_opcode       = ISCSI_OP_LOGOUT;
252162306a36Sopenharmony_ci	cmd->i_state            = ISTATE_SEND_LOGOUTRSP;
252262306a36Sopenharmony_ci	cmd->immediate_cmd      = ((hdr->opcode & ISCSI_OP_IMMEDIATE) ? 1 : 0);
252362306a36Sopenharmony_ci	conn->sess->init_task_tag = cmd->init_task_tag  = hdr->itt;
252462306a36Sopenharmony_ci	cmd->targ_xfer_tag      = 0xFFFFFFFF;
252562306a36Sopenharmony_ci	cmd->cmd_sn             = be32_to_cpu(hdr->cmdsn);
252662306a36Sopenharmony_ci	cmd->exp_stat_sn        = be32_to_cpu(hdr->exp_statsn);
252762306a36Sopenharmony_ci	cmd->logout_cid         = be16_to_cpu(hdr->cid);
252862306a36Sopenharmony_ci	cmd->logout_reason      = reason_code;
252962306a36Sopenharmony_ci	cmd->data_direction     = DMA_NONE;
253062306a36Sopenharmony_ci
253162306a36Sopenharmony_ci	/*
253262306a36Sopenharmony_ci	 * We need to sleep in these cases (by returning 1) until the Logout
253362306a36Sopenharmony_ci	 * Response gets sent in the tx thread.
253462306a36Sopenharmony_ci	 */
253562306a36Sopenharmony_ci	if ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_SESSION) ||
253662306a36Sopenharmony_ci	   ((reason_code == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION) &&
253762306a36Sopenharmony_ci	    be16_to_cpu(hdr->cid) == conn->cid))
253862306a36Sopenharmony_ci		logout_remove = 1;
253962306a36Sopenharmony_ci
254062306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
254162306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn->conn_cmd_list);
254262306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	if (reason_code != ISCSI_LOGOUT_REASON_RECOVERY)
254562306a36Sopenharmony_ci		iscsit_ack_from_expstatsn(conn, be32_to_cpu(hdr->exp_statsn));
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	/*
254862306a36Sopenharmony_ci	 * Immediate commands are executed, well, immediately.
254962306a36Sopenharmony_ci	 * Non-Immediate Logout Commands are executed in CmdSN order.
255062306a36Sopenharmony_ci	 */
255162306a36Sopenharmony_ci	if (cmd->immediate_cmd) {
255262306a36Sopenharmony_ci		int ret = iscsit_execute_cmd(cmd, 0);
255362306a36Sopenharmony_ci
255462306a36Sopenharmony_ci		if (ret < 0)
255562306a36Sopenharmony_ci			return ret;
255662306a36Sopenharmony_ci	} else {
255762306a36Sopenharmony_ci		cmdsn_ret = iscsit_sequence_cmd(conn, cmd, buf, hdr->cmdsn);
255862306a36Sopenharmony_ci		if (cmdsn_ret == CMDSN_LOWER_THAN_EXP)
255962306a36Sopenharmony_ci			logout_remove = 0;
256062306a36Sopenharmony_ci		else if (cmdsn_ret == CMDSN_ERROR_CANNOT_RECOVER)
256162306a36Sopenharmony_ci			return -1;
256262306a36Sopenharmony_ci	}
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	return logout_remove;
256562306a36Sopenharmony_ci}
256662306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_logout_cmd);
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ciint iscsit_handle_snack(
256962306a36Sopenharmony_ci	struct iscsit_conn *conn,
257062306a36Sopenharmony_ci	unsigned char *buf)
257162306a36Sopenharmony_ci{
257262306a36Sopenharmony_ci	struct iscsi_snack *hdr;
257362306a36Sopenharmony_ci
257462306a36Sopenharmony_ci	hdr			= (struct iscsi_snack *) buf;
257562306a36Sopenharmony_ci	hdr->flags		&= ~ISCSI_FLAG_CMD_FINAL;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	pr_debug("Got ISCSI_INIT_SNACK, ITT: 0x%08x, ExpStatSN:"
257862306a36Sopenharmony_ci		" 0x%08x, Type: 0x%02x, BegRun: 0x%08x, RunLength: 0x%08x,"
257962306a36Sopenharmony_ci		" CID: %hu\n", hdr->itt, hdr->exp_statsn, hdr->flags,
258062306a36Sopenharmony_ci			hdr->begrun, hdr->runlength, conn->cid);
258162306a36Sopenharmony_ci
258262306a36Sopenharmony_ci	if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
258362306a36Sopenharmony_ci		pr_err("Initiator sent SNACK request while in"
258462306a36Sopenharmony_ci			" ErrorRecoveryLevel=0.\n");
258562306a36Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
258662306a36Sopenharmony_ci					 buf);
258762306a36Sopenharmony_ci	}
258862306a36Sopenharmony_ci	/*
258962306a36Sopenharmony_ci	 * SNACK_DATA and SNACK_R2T are both 0,  so check which function to
259062306a36Sopenharmony_ci	 * call from inside iscsi_send_recovery_datain_or_r2t().
259162306a36Sopenharmony_ci	 */
259262306a36Sopenharmony_ci	switch (hdr->flags & ISCSI_FLAG_SNACK_TYPE_MASK) {
259362306a36Sopenharmony_ci	case 0:
259462306a36Sopenharmony_ci		return iscsit_handle_recovery_datain_or_r2t(conn, buf,
259562306a36Sopenharmony_ci			hdr->itt,
259662306a36Sopenharmony_ci			be32_to_cpu(hdr->ttt),
259762306a36Sopenharmony_ci			be32_to_cpu(hdr->begrun),
259862306a36Sopenharmony_ci			be32_to_cpu(hdr->runlength));
259962306a36Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_STATUS:
260062306a36Sopenharmony_ci		return iscsit_handle_status_snack(conn, hdr->itt,
260162306a36Sopenharmony_ci			be32_to_cpu(hdr->ttt),
260262306a36Sopenharmony_ci			be32_to_cpu(hdr->begrun), be32_to_cpu(hdr->runlength));
260362306a36Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_DATA_ACK:
260462306a36Sopenharmony_ci		return iscsit_handle_data_ack(conn, be32_to_cpu(hdr->ttt),
260562306a36Sopenharmony_ci			be32_to_cpu(hdr->begrun),
260662306a36Sopenharmony_ci			be32_to_cpu(hdr->runlength));
260762306a36Sopenharmony_ci	case ISCSI_FLAG_SNACK_TYPE_RDATA:
260862306a36Sopenharmony_ci		/* FIXME: Support R-Data SNACK */
260962306a36Sopenharmony_ci		pr_err("R-Data SNACK Not Supported.\n");
261062306a36Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
261162306a36Sopenharmony_ci					 buf);
261262306a36Sopenharmony_ci	default:
261362306a36Sopenharmony_ci		pr_err("Unknown SNACK type 0x%02x, protocol"
261462306a36Sopenharmony_ci			" error.\n", hdr->flags & 0x0f);
261562306a36Sopenharmony_ci		return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
261662306a36Sopenharmony_ci					 buf);
261762306a36Sopenharmony_ci	}
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_ci	return 0;
262062306a36Sopenharmony_ci}
262162306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_handle_snack);
262262306a36Sopenharmony_ci
262362306a36Sopenharmony_cistatic void iscsit_rx_thread_wait_for_tcp(struct iscsit_conn *conn)
262462306a36Sopenharmony_ci{
262562306a36Sopenharmony_ci	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
262662306a36Sopenharmony_ci	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
262762306a36Sopenharmony_ci		wait_for_completion_interruptible_timeout(
262862306a36Sopenharmony_ci					&conn->rx_half_close_comp,
262962306a36Sopenharmony_ci					ISCSI_RX_THREAD_TCP_TIMEOUT * HZ);
263062306a36Sopenharmony_ci	}
263162306a36Sopenharmony_ci}
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_cistatic int iscsit_handle_immediate_data(
263462306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
263562306a36Sopenharmony_ci	struct iscsi_scsi_req *hdr,
263662306a36Sopenharmony_ci	u32 length)
263762306a36Sopenharmony_ci{
263862306a36Sopenharmony_ci	int iov_ret, rx_got = 0, rx_size = 0;
263962306a36Sopenharmony_ci	u32 checksum, iov_count = 0, padding = 0;
264062306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
264162306a36Sopenharmony_ci	struct kvec *iov;
264262306a36Sopenharmony_ci	void *overflow_buf = NULL;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	BUG_ON(cmd->write_data_done > cmd->se_cmd.data_length);
264562306a36Sopenharmony_ci	rx_size = min(cmd->se_cmd.data_length - cmd->write_data_done, length);
264662306a36Sopenharmony_ci	iov_ret = iscsit_map_iovec(cmd, cmd->iov_data,
264762306a36Sopenharmony_ci				   cmd->orig_iov_data_count - 2,
264862306a36Sopenharmony_ci				   cmd->write_data_done, rx_size);
264962306a36Sopenharmony_ci	if (iov_ret < 0)
265062306a36Sopenharmony_ci		return IMMEDIATE_DATA_CANNOT_RECOVER;
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	iov_count = iov_ret;
265362306a36Sopenharmony_ci	iov = &cmd->iov_data[0];
265462306a36Sopenharmony_ci	if (rx_size < length) {
265562306a36Sopenharmony_ci		/*
265662306a36Sopenharmony_ci		 * Special case: length of immediate data exceeds the data
265762306a36Sopenharmony_ci		 * buffer size derived from the CDB.
265862306a36Sopenharmony_ci		 */
265962306a36Sopenharmony_ci		overflow_buf = kmalloc(length - rx_size, GFP_KERNEL);
266062306a36Sopenharmony_ci		if (!overflow_buf) {
266162306a36Sopenharmony_ci			iscsit_unmap_iovec(cmd);
266262306a36Sopenharmony_ci			return IMMEDIATE_DATA_CANNOT_RECOVER;
266362306a36Sopenharmony_ci		}
266462306a36Sopenharmony_ci		cmd->overflow_buf = overflow_buf;
266562306a36Sopenharmony_ci		iov[iov_count].iov_base = overflow_buf;
266662306a36Sopenharmony_ci		iov[iov_count].iov_len = length - rx_size;
266762306a36Sopenharmony_ci		iov_count++;
266862306a36Sopenharmony_ci		rx_size = length;
266962306a36Sopenharmony_ci	}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	padding = ((-length) & 3);
267262306a36Sopenharmony_ci	if (padding != 0) {
267362306a36Sopenharmony_ci		iov[iov_count].iov_base	= cmd->pad_bytes;
267462306a36Sopenharmony_ci		iov[iov_count++].iov_len = padding;
267562306a36Sopenharmony_ci		rx_size += padding;
267662306a36Sopenharmony_ci	}
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
267962306a36Sopenharmony_ci		iov[iov_count].iov_base		= &checksum;
268062306a36Sopenharmony_ci		iov[iov_count++].iov_len	= ISCSI_CRC_LEN;
268162306a36Sopenharmony_ci		rx_size += ISCSI_CRC_LEN;
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	WARN_ON_ONCE(iov_count > cmd->orig_iov_data_count);
268562306a36Sopenharmony_ci	rx_got = rx_data(conn, &cmd->iov_data[0], iov_count, rx_size);
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	iscsit_unmap_iovec(cmd);
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	if (rx_got != rx_size) {
269062306a36Sopenharmony_ci		iscsit_rx_thread_wait_for_tcp(conn);
269162306a36Sopenharmony_ci		return IMMEDIATE_DATA_CANNOT_RECOVER;
269262306a36Sopenharmony_ci	}
269362306a36Sopenharmony_ci
269462306a36Sopenharmony_ci	if (conn->conn_ops->DataDigest) {
269562306a36Sopenharmony_ci		u32 data_crc;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_ci		data_crc = iscsit_do_crypto_hash_sg(conn->conn_rx_hash, cmd,
269862306a36Sopenharmony_ci						    cmd->write_data_done, length, padding,
269962306a36Sopenharmony_ci						    cmd->pad_bytes);
270062306a36Sopenharmony_ci
270162306a36Sopenharmony_ci		if (checksum != data_crc) {
270262306a36Sopenharmony_ci			pr_err("ImmediateData CRC32C DataDigest 0x%08x"
270362306a36Sopenharmony_ci				" does not match computed 0x%08x\n", checksum,
270462306a36Sopenharmony_ci				data_crc);
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci			if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
270762306a36Sopenharmony_ci				pr_err("Unable to recover from"
270862306a36Sopenharmony_ci					" Immediate Data digest failure while"
270962306a36Sopenharmony_ci					" in ERL=0.\n");
271062306a36Sopenharmony_ci				iscsit_reject_cmd(cmd,
271162306a36Sopenharmony_ci						ISCSI_REASON_DATA_DIGEST_ERROR,
271262306a36Sopenharmony_ci						(unsigned char *)hdr);
271362306a36Sopenharmony_ci				return IMMEDIATE_DATA_CANNOT_RECOVER;
271462306a36Sopenharmony_ci			} else {
271562306a36Sopenharmony_ci				iscsit_reject_cmd(cmd,
271662306a36Sopenharmony_ci						ISCSI_REASON_DATA_DIGEST_ERROR,
271762306a36Sopenharmony_ci						(unsigned char *)hdr);
271862306a36Sopenharmony_ci				return IMMEDIATE_DATA_ERL1_CRC_FAILURE;
271962306a36Sopenharmony_ci			}
272062306a36Sopenharmony_ci		} else {
272162306a36Sopenharmony_ci			pr_debug("Got CRC32C DataDigest 0x%08x for"
272262306a36Sopenharmony_ci				" %u bytes of Immediate Data\n", checksum,
272362306a36Sopenharmony_ci				length);
272462306a36Sopenharmony_ci		}
272562306a36Sopenharmony_ci	}
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	cmd->write_data_done += length;
272862306a36Sopenharmony_ci
272962306a36Sopenharmony_ci	if (cmd->write_data_done == cmd->se_cmd.data_length) {
273062306a36Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
273162306a36Sopenharmony_ci		cmd->cmd_flags |= ICF_GOT_LAST_DATAOUT;
273262306a36Sopenharmony_ci		cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT;
273362306a36Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
273462306a36Sopenharmony_ci	}
273562306a36Sopenharmony_ci
273662306a36Sopenharmony_ci	return IMMEDIATE_DATA_NORMAL_OPERATION;
273762306a36Sopenharmony_ci}
273862306a36Sopenharmony_ci
273962306a36Sopenharmony_ci/* #warning iscsi_build_conn_drop_async_message() only sends out on connections
274062306a36Sopenharmony_ci	with active network interface */
274162306a36Sopenharmony_cistatic void iscsit_build_conn_drop_async_message(struct iscsit_conn *conn)
274262306a36Sopenharmony_ci{
274362306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
274462306a36Sopenharmony_ci	struct iscsit_conn *conn_p;
274562306a36Sopenharmony_ci	bool found = false;
274662306a36Sopenharmony_ci
274762306a36Sopenharmony_ci	lockdep_assert_held(&conn->sess->conn_lock);
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	/*
275062306a36Sopenharmony_ci	 * Only send a Asynchronous Message on connections whos network
275162306a36Sopenharmony_ci	 * interface is still functional.
275262306a36Sopenharmony_ci	 */
275362306a36Sopenharmony_ci	list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) {
275462306a36Sopenharmony_ci		if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) {
275562306a36Sopenharmony_ci			iscsit_inc_conn_usage_count(conn_p);
275662306a36Sopenharmony_ci			found = true;
275762306a36Sopenharmony_ci			break;
275862306a36Sopenharmony_ci		}
275962306a36Sopenharmony_ci	}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	if (!found)
276262306a36Sopenharmony_ci		return;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	cmd = iscsit_allocate_cmd(conn_p, TASK_RUNNING);
276562306a36Sopenharmony_ci	if (!cmd) {
276662306a36Sopenharmony_ci		iscsit_dec_conn_usage_count(conn_p);
276762306a36Sopenharmony_ci		return;
276862306a36Sopenharmony_ci	}
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	cmd->logout_cid = conn->cid;
277162306a36Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
277262306a36Sopenharmony_ci	cmd->i_state = ISTATE_SEND_ASYNCMSG;
277362306a36Sopenharmony_ci
277462306a36Sopenharmony_ci	spin_lock_bh(&conn_p->cmd_lock);
277562306a36Sopenharmony_ci	list_add_tail(&cmd->i_conn_node, &conn_p->conn_cmd_list);
277662306a36Sopenharmony_ci	spin_unlock_bh(&conn_p->cmd_lock);
277762306a36Sopenharmony_ci
277862306a36Sopenharmony_ci	iscsit_add_cmd_to_response_queue(cmd, conn_p, cmd->i_state);
277962306a36Sopenharmony_ci	iscsit_dec_conn_usage_count(conn_p);
278062306a36Sopenharmony_ci}
278162306a36Sopenharmony_ci
278262306a36Sopenharmony_cistatic int iscsit_send_conn_drop_async_message(
278362306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
278462306a36Sopenharmony_ci	struct iscsit_conn *conn)
278562306a36Sopenharmony_ci{
278662306a36Sopenharmony_ci	struct iscsi_async *hdr;
278762306a36Sopenharmony_ci
278862306a36Sopenharmony_ci	cmd->iscsi_opcode = ISCSI_OP_ASYNC_EVENT;
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	hdr			= (struct iscsi_async *) cmd->pdu;
279162306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_ASYNC_EVENT;
279262306a36Sopenharmony_ci	hdr->flags		= ISCSI_FLAG_CMD_FINAL;
279362306a36Sopenharmony_ci	cmd->init_task_tag	= RESERVED_ITT;
279462306a36Sopenharmony_ci	cmd->targ_xfer_tag	= 0xFFFFFFFF;
279562306a36Sopenharmony_ci	put_unaligned_be64(0xFFFFFFFFFFFFFFFFULL, &hdr->rsvd4[0]);
279662306a36Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
279762306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
279862306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
279962306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
280062306a36Sopenharmony_ci	hdr->async_event	= ISCSI_ASYNC_MSG_DROPPING_CONNECTION;
280162306a36Sopenharmony_ci	hdr->param1		= cpu_to_be16(cmd->logout_cid);
280262306a36Sopenharmony_ci	hdr->param2		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Wait);
280362306a36Sopenharmony_ci	hdr->param3		= cpu_to_be16(conn->sess->sess_ops->DefaultTime2Retain);
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	pr_debug("Sending Connection Dropped Async Message StatSN:"
280662306a36Sopenharmony_ci		" 0x%08x, for CID: %hu on CID: %hu\n", cmd->stat_sn,
280762306a36Sopenharmony_ci			cmd->logout_cid, conn->cid);
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
281062306a36Sopenharmony_ci}
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_cistatic void iscsit_tx_thread_wait_for_tcp(struct iscsit_conn *conn)
281362306a36Sopenharmony_ci{
281462306a36Sopenharmony_ci	if ((conn->sock->sk->sk_shutdown & SEND_SHUTDOWN) ||
281562306a36Sopenharmony_ci	    (conn->sock->sk->sk_shutdown & RCV_SHUTDOWN)) {
281662306a36Sopenharmony_ci		wait_for_completion_interruptible_timeout(
281762306a36Sopenharmony_ci					&conn->tx_half_close_comp,
281862306a36Sopenharmony_ci					ISCSI_TX_THREAD_TCP_TIMEOUT * HZ);
281962306a36Sopenharmony_ci	}
282062306a36Sopenharmony_ci}
282162306a36Sopenharmony_ci
282262306a36Sopenharmony_civoid
282362306a36Sopenharmony_ciiscsit_build_datain_pdu(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
282462306a36Sopenharmony_ci			struct iscsi_datain *datain, struct iscsi_data_rsp *hdr,
282562306a36Sopenharmony_ci			bool set_statsn)
282662306a36Sopenharmony_ci{
282762306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_DATA_IN;
282862306a36Sopenharmony_ci	hdr->flags		= datain->flags;
282962306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_STATUS) {
283062306a36Sopenharmony_ci		if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
283162306a36Sopenharmony_ci			hdr->flags |= ISCSI_FLAG_DATA_OVERFLOW;
283262306a36Sopenharmony_ci			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
283362306a36Sopenharmony_ci		} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
283462306a36Sopenharmony_ci			hdr->flags |= ISCSI_FLAG_DATA_UNDERFLOW;
283562306a36Sopenharmony_ci			hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
283662306a36Sopenharmony_ci		}
283762306a36Sopenharmony_ci	}
283862306a36Sopenharmony_ci	hton24(hdr->dlength, datain->length);
283962306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_ACK)
284062306a36Sopenharmony_ci		int_to_scsilun(cmd->se_cmd.orig_fe_lun,
284162306a36Sopenharmony_ci				(struct scsi_lun *)&hdr->lun);
284262306a36Sopenharmony_ci	else
284362306a36Sopenharmony_ci		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
284462306a36Sopenharmony_ci
284562306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_DATA_ACK)
284862306a36Sopenharmony_ci		hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
284962306a36Sopenharmony_ci	else
285062306a36Sopenharmony_ci		hdr->ttt		= cpu_to_be32(0xFFFFFFFF);
285162306a36Sopenharmony_ci	if (set_statsn)
285262306a36Sopenharmony_ci		hdr->statsn		= cpu_to_be32(cmd->stat_sn);
285362306a36Sopenharmony_ci	else
285462306a36Sopenharmony_ci		hdr->statsn		= cpu_to_be32(0xFFFFFFFF);
285562306a36Sopenharmony_ci
285662306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
285762306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
285862306a36Sopenharmony_ci	hdr->datasn		= cpu_to_be32(datain->data_sn);
285962306a36Sopenharmony_ci	hdr->offset		= cpu_to_be32(datain->offset);
286062306a36Sopenharmony_ci
286162306a36Sopenharmony_ci	pr_debug("Built DataIN ITT: 0x%08x, StatSN: 0x%08x,"
286262306a36Sopenharmony_ci		" DataSN: 0x%08x, Offset: %u, Length: %u, CID: %hu\n",
286362306a36Sopenharmony_ci		cmd->init_task_tag, ntohl(hdr->statsn), ntohl(hdr->datasn),
286462306a36Sopenharmony_ci		ntohl(hdr->offset), datain->length, conn->cid);
286562306a36Sopenharmony_ci}
286662306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_datain_pdu);
286762306a36Sopenharmony_ci
286862306a36Sopenharmony_cistatic int iscsit_send_datain(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
286962306a36Sopenharmony_ci{
287062306a36Sopenharmony_ci	struct iscsi_data_rsp *hdr = (struct iscsi_data_rsp *)&cmd->pdu[0];
287162306a36Sopenharmony_ci	struct iscsi_datain datain;
287262306a36Sopenharmony_ci	struct iscsi_datain_req *dr;
287362306a36Sopenharmony_ci	int eodr = 0, ret;
287462306a36Sopenharmony_ci	bool set_statsn = false;
287562306a36Sopenharmony_ci
287662306a36Sopenharmony_ci	memset(&datain, 0, sizeof(struct iscsi_datain));
287762306a36Sopenharmony_ci	dr = iscsit_get_datain_values(cmd, &datain);
287862306a36Sopenharmony_ci	if (!dr) {
287962306a36Sopenharmony_ci		pr_err("iscsit_get_datain_values failed for ITT: 0x%08x\n",
288062306a36Sopenharmony_ci				cmd->init_task_tag);
288162306a36Sopenharmony_ci		return -1;
288262306a36Sopenharmony_ci	}
288362306a36Sopenharmony_ci	/*
288462306a36Sopenharmony_ci	 * Be paranoid and double check the logic for now.
288562306a36Sopenharmony_ci	 */
288662306a36Sopenharmony_ci	if ((datain.offset + datain.length) > cmd->se_cmd.data_length) {
288762306a36Sopenharmony_ci		pr_err("Command ITT: 0x%08x, datain.offset: %u and"
288862306a36Sopenharmony_ci			" datain.length: %u exceeds cmd->data_length: %u\n",
288962306a36Sopenharmony_ci			cmd->init_task_tag, datain.offset, datain.length,
289062306a36Sopenharmony_ci			cmd->se_cmd.data_length);
289162306a36Sopenharmony_ci		return -1;
289262306a36Sopenharmony_ci	}
289362306a36Sopenharmony_ci
289462306a36Sopenharmony_ci	atomic_long_add(datain.length, &conn->sess->tx_data_octets);
289562306a36Sopenharmony_ci	/*
289662306a36Sopenharmony_ci	 * Special case for successfully execution w/ both DATAIN
289762306a36Sopenharmony_ci	 * and Sense Data.
289862306a36Sopenharmony_ci	 */
289962306a36Sopenharmony_ci	if ((datain.flags & ISCSI_FLAG_DATA_STATUS) &&
290062306a36Sopenharmony_ci	    (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE))
290162306a36Sopenharmony_ci		datain.flags &= ~ISCSI_FLAG_DATA_STATUS;
290262306a36Sopenharmony_ci	else {
290362306a36Sopenharmony_ci		if ((dr->dr_complete == DATAIN_COMPLETE_NORMAL) ||
290462306a36Sopenharmony_ci		    (dr->dr_complete == DATAIN_COMPLETE_CONNECTION_RECOVERY)) {
290562306a36Sopenharmony_ci			iscsit_increment_maxcmdsn(cmd, conn->sess);
290662306a36Sopenharmony_ci			cmd->stat_sn = conn->stat_sn++;
290762306a36Sopenharmony_ci			set_statsn = true;
290862306a36Sopenharmony_ci		} else if (dr->dr_complete ==
290962306a36Sopenharmony_ci			   DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY)
291062306a36Sopenharmony_ci			set_statsn = true;
291162306a36Sopenharmony_ci	}
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_ci	iscsit_build_datain_pdu(cmd, conn, &datain, hdr, set_statsn);
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, dr, &datain, 0);
291662306a36Sopenharmony_ci	if (ret < 0)
291762306a36Sopenharmony_ci		return ret;
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci	if (dr->dr_complete) {
292062306a36Sopenharmony_ci		eodr = (cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ?
292162306a36Sopenharmony_ci				2 : 1;
292262306a36Sopenharmony_ci		iscsit_free_datain_req(cmd, dr);
292362306a36Sopenharmony_ci	}
292462306a36Sopenharmony_ci
292562306a36Sopenharmony_ci	return eodr;
292662306a36Sopenharmony_ci}
292762306a36Sopenharmony_ci
292862306a36Sopenharmony_ciint
292962306a36Sopenharmony_ciiscsit_build_logout_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
293062306a36Sopenharmony_ci			struct iscsi_logout_rsp *hdr)
293162306a36Sopenharmony_ci{
293262306a36Sopenharmony_ci	struct iscsit_conn *logout_conn = NULL;
293362306a36Sopenharmony_ci	struct iscsi_conn_recovery *cr = NULL;
293462306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
293562306a36Sopenharmony_ci	/*
293662306a36Sopenharmony_ci	 * The actual shutting down of Sessions and/or Connections
293762306a36Sopenharmony_ci	 * for CLOSESESSION and CLOSECONNECTION Logout Requests
293862306a36Sopenharmony_ci	 * is done in scsi_logout_post_handler().
293962306a36Sopenharmony_ci	 */
294062306a36Sopenharmony_ci	switch (cmd->logout_reason) {
294162306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
294262306a36Sopenharmony_ci		pr_debug("iSCSI session logout successful, setting"
294362306a36Sopenharmony_ci			" logout response to ISCSI_LOGOUT_SUCCESS.\n");
294462306a36Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
294562306a36Sopenharmony_ci		break;
294662306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
294762306a36Sopenharmony_ci		if (cmd->logout_response == ISCSI_LOGOUT_CID_NOT_FOUND)
294862306a36Sopenharmony_ci			break;
294962306a36Sopenharmony_ci		/*
295062306a36Sopenharmony_ci		 * For CLOSECONNECTION logout requests carrying
295162306a36Sopenharmony_ci		 * a matching logout CID -> local CID, the reference
295262306a36Sopenharmony_ci		 * for the local CID will have been incremented in
295362306a36Sopenharmony_ci		 * iscsi_logout_closeconnection().
295462306a36Sopenharmony_ci		 *
295562306a36Sopenharmony_ci		 * For CLOSECONNECTION logout requests carrying
295662306a36Sopenharmony_ci		 * a different CID than the connection it arrived
295762306a36Sopenharmony_ci		 * on, the connection responding to cmd->logout_cid
295862306a36Sopenharmony_ci		 * is stopped in iscsit_logout_post_handler_diffcid().
295962306a36Sopenharmony_ci		 */
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci		pr_debug("iSCSI CID: %hu logout on CID: %hu"
296262306a36Sopenharmony_ci			" successful.\n", cmd->logout_cid, conn->cid);
296362306a36Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
296462306a36Sopenharmony_ci		break;
296562306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_RECOVERY:
296662306a36Sopenharmony_ci		if ((cmd->logout_response == ISCSI_LOGOUT_RECOVERY_UNSUPPORTED) ||
296762306a36Sopenharmony_ci		    (cmd->logout_response == ISCSI_LOGOUT_CLEANUP_FAILED))
296862306a36Sopenharmony_ci			break;
296962306a36Sopenharmony_ci		/*
297062306a36Sopenharmony_ci		 * If the connection is still active from our point of view
297162306a36Sopenharmony_ci		 * force connection recovery to occur.
297262306a36Sopenharmony_ci		 */
297362306a36Sopenharmony_ci		logout_conn = iscsit_get_conn_from_cid_rcfr(sess,
297462306a36Sopenharmony_ci				cmd->logout_cid);
297562306a36Sopenharmony_ci		if (logout_conn) {
297662306a36Sopenharmony_ci			iscsit_connection_reinstatement_rcfr(logout_conn);
297762306a36Sopenharmony_ci			iscsit_dec_conn_usage_count(logout_conn);
297862306a36Sopenharmony_ci		}
297962306a36Sopenharmony_ci
298062306a36Sopenharmony_ci		cr = iscsit_get_inactive_connection_recovery_entry(
298162306a36Sopenharmony_ci				conn->sess, cmd->logout_cid);
298262306a36Sopenharmony_ci		if (!cr) {
298362306a36Sopenharmony_ci			pr_err("Unable to locate CID: %hu for"
298462306a36Sopenharmony_ci			" REMOVECONNFORRECOVERY Logout Request.\n",
298562306a36Sopenharmony_ci				cmd->logout_cid);
298662306a36Sopenharmony_ci			cmd->logout_response = ISCSI_LOGOUT_CID_NOT_FOUND;
298762306a36Sopenharmony_ci			break;
298862306a36Sopenharmony_ci		}
298962306a36Sopenharmony_ci
299062306a36Sopenharmony_ci		iscsit_discard_cr_cmds_by_expstatsn(cr, cmd->exp_stat_sn);
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_ci		pr_debug("iSCSI REMOVECONNFORRECOVERY logout"
299362306a36Sopenharmony_ci			" for recovery for CID: %hu on CID: %hu successful.\n",
299462306a36Sopenharmony_ci				cmd->logout_cid, conn->cid);
299562306a36Sopenharmony_ci		cmd->logout_response = ISCSI_LOGOUT_SUCCESS;
299662306a36Sopenharmony_ci		break;
299762306a36Sopenharmony_ci	default:
299862306a36Sopenharmony_ci		pr_err("Unknown cmd->logout_reason: 0x%02x\n",
299962306a36Sopenharmony_ci				cmd->logout_reason);
300062306a36Sopenharmony_ci		return -1;
300162306a36Sopenharmony_ci	}
300262306a36Sopenharmony_ci
300362306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_LOGOUT_RSP;
300462306a36Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
300562306a36Sopenharmony_ci	hdr->response		= cmd->logout_response;
300662306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
300762306a36Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
300862306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
300962306a36Sopenharmony_ci
301062306a36Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
301162306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
301262306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_ci	pr_debug("Built Logout Response ITT: 0x%08x StatSN:"
301562306a36Sopenharmony_ci		" 0x%08x Response: 0x%02x CID: %hu on CID: %hu\n",
301662306a36Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, hdr->response,
301762306a36Sopenharmony_ci		cmd->logout_cid, conn->cid);
301862306a36Sopenharmony_ci
301962306a36Sopenharmony_ci	return 0;
302062306a36Sopenharmony_ci}
302162306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_logout_rsp);
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_cistatic int
302462306a36Sopenharmony_ciiscsit_send_logout(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
302562306a36Sopenharmony_ci{
302662306a36Sopenharmony_ci	int rc;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci	rc = iscsit_build_logout_rsp(cmd, conn,
302962306a36Sopenharmony_ci			(struct iscsi_logout_rsp *)&cmd->pdu[0]);
303062306a36Sopenharmony_ci	if (rc < 0)
303162306a36Sopenharmony_ci		return rc;
303262306a36Sopenharmony_ci
303362306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
303462306a36Sopenharmony_ci}
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_civoid
303762306a36Sopenharmony_ciiscsit_build_nopin_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
303862306a36Sopenharmony_ci		       struct iscsi_nopin *hdr, bool nopout_response)
303962306a36Sopenharmony_ci{
304062306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_NOOP_IN;
304162306a36Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
304262306a36Sopenharmony_ci        hton24(hdr->dlength, cmd->buf_ptr_size);
304362306a36Sopenharmony_ci	if (nopout_response)
304462306a36Sopenharmony_ci		put_unaligned_le64(0xFFFFFFFFFFFFFFFFULL, &hdr->lun);
304562306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
304662306a36Sopenharmony_ci	hdr->ttt		= cpu_to_be32(cmd->targ_xfer_tag);
304762306a36Sopenharmony_ci	cmd->stat_sn		= (nopout_response) ? conn->stat_sn++ :
304862306a36Sopenharmony_ci				  conn->stat_sn;
304962306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
305062306a36Sopenharmony_ci
305162306a36Sopenharmony_ci	if (nopout_response)
305262306a36Sopenharmony_ci		iscsit_increment_maxcmdsn(cmd, conn->sess);
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
305562306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
305662306a36Sopenharmony_ci
305762306a36Sopenharmony_ci	pr_debug("Built NOPIN %s Response ITT: 0x%08x, TTT: 0x%08x,"
305862306a36Sopenharmony_ci		" StatSN: 0x%08x, Length %u\n", (nopout_response) ?
305962306a36Sopenharmony_ci		"Solicited" : "Unsolicited", cmd->init_task_tag,
306062306a36Sopenharmony_ci		cmd->targ_xfer_tag, cmd->stat_sn, cmd->buf_ptr_size);
306162306a36Sopenharmony_ci}
306262306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_nopin_rsp);
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci/*
306562306a36Sopenharmony_ci *	Unsolicited NOPIN, either requesting a response or not.
306662306a36Sopenharmony_ci */
306762306a36Sopenharmony_cistatic int iscsit_send_unsolicited_nopin(
306862306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
306962306a36Sopenharmony_ci	struct iscsit_conn *conn,
307062306a36Sopenharmony_ci	int want_response)
307162306a36Sopenharmony_ci{
307262306a36Sopenharmony_ci	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
307362306a36Sopenharmony_ci	int ret;
307462306a36Sopenharmony_ci
307562306a36Sopenharmony_ci	iscsit_build_nopin_rsp(cmd, conn, hdr, false);
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	pr_debug("Sending Unsolicited NOPIN TTT: 0x%08x StatSN:"
307862306a36Sopenharmony_ci		" 0x%08x CID: %hu\n", hdr->ttt, cmd->stat_sn, conn->cid);
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
308162306a36Sopenharmony_ci	if (ret < 0)
308262306a36Sopenharmony_ci		return ret;
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	spin_lock_bh(&cmd->istate_lock);
308562306a36Sopenharmony_ci	cmd->i_state = want_response ?
308662306a36Sopenharmony_ci		ISTATE_SENT_NOPIN_WANT_RESPONSE : ISTATE_SENT_STATUS;
308762306a36Sopenharmony_ci	spin_unlock_bh(&cmd->istate_lock);
308862306a36Sopenharmony_ci
308962306a36Sopenharmony_ci	return 0;
309062306a36Sopenharmony_ci}
309162306a36Sopenharmony_ci
309262306a36Sopenharmony_cistatic int
309362306a36Sopenharmony_ciiscsit_send_nopin(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
309462306a36Sopenharmony_ci{
309562306a36Sopenharmony_ci	struct iscsi_nopin *hdr = (struct iscsi_nopin *)&cmd->pdu[0];
309662306a36Sopenharmony_ci
309762306a36Sopenharmony_ci	iscsit_build_nopin_rsp(cmd, conn, hdr, true);
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci	/*
310062306a36Sopenharmony_ci	 * NOPOUT Ping Data is attached to struct iscsit_cmd->buf_ptr.
310162306a36Sopenharmony_ci	 * NOPOUT DataSegmentLength is at struct iscsit_cmd->buf_ptr_size.
310262306a36Sopenharmony_ci	 */
310362306a36Sopenharmony_ci	pr_debug("Echoing back %u bytes of ping data.\n", cmd->buf_ptr_size);
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
310662306a36Sopenharmony_ci						     cmd->buf_ptr,
310762306a36Sopenharmony_ci						     cmd->buf_ptr_size);
310862306a36Sopenharmony_ci}
310962306a36Sopenharmony_ci
311062306a36Sopenharmony_cistatic int iscsit_send_r2t(
311162306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
311262306a36Sopenharmony_ci	struct iscsit_conn *conn)
311362306a36Sopenharmony_ci{
311462306a36Sopenharmony_ci	struct iscsi_r2t *r2t;
311562306a36Sopenharmony_ci	struct iscsi_r2t_rsp *hdr;
311662306a36Sopenharmony_ci	int ret;
311762306a36Sopenharmony_ci
311862306a36Sopenharmony_ci	r2t = iscsit_get_r2t_from_list(cmd);
311962306a36Sopenharmony_ci	if (!r2t)
312062306a36Sopenharmony_ci		return -1;
312162306a36Sopenharmony_ci
312262306a36Sopenharmony_ci	hdr			= (struct iscsi_r2t_rsp *) cmd->pdu;
312362306a36Sopenharmony_ci	memset(hdr, 0, ISCSI_HDR_LEN);
312462306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_R2T;
312562306a36Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
312662306a36Sopenharmony_ci	int_to_scsilun(cmd->se_cmd.orig_fe_lun,
312762306a36Sopenharmony_ci			(struct scsi_lun *)&hdr->lun);
312862306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
312962306a36Sopenharmony_ci	if (conn->conn_transport->iscsit_get_r2t_ttt)
313062306a36Sopenharmony_ci		conn->conn_transport->iscsit_get_r2t_ttt(conn, cmd, r2t);
313162306a36Sopenharmony_ci	else
313262306a36Sopenharmony_ci		r2t->targ_xfer_tag = session_get_next_ttt(conn->sess);
313362306a36Sopenharmony_ci	hdr->ttt		= cpu_to_be32(r2t->targ_xfer_tag);
313462306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(conn->stat_sn);
313562306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
313662306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
313762306a36Sopenharmony_ci	hdr->r2tsn		= cpu_to_be32(r2t->r2t_sn);
313862306a36Sopenharmony_ci	hdr->data_offset	= cpu_to_be32(r2t->offset);
313962306a36Sopenharmony_ci	hdr->data_length	= cpu_to_be32(r2t->xfer_len);
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_ci	pr_debug("Built %sR2T, ITT: 0x%08x, TTT: 0x%08x, StatSN:"
314262306a36Sopenharmony_ci		" 0x%08x, R2TSN: 0x%08x, Offset: %u, DDTL: %u, CID: %hu\n",
314362306a36Sopenharmony_ci		(!r2t->recovery_r2t) ? "" : "Recovery ", cmd->init_task_tag,
314462306a36Sopenharmony_ci		r2t->targ_xfer_tag, ntohl(hdr->statsn), r2t->r2t_sn,
314562306a36Sopenharmony_ci			r2t->offset, r2t->xfer_len, conn->cid);
314662306a36Sopenharmony_ci
314762306a36Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
314862306a36Sopenharmony_ci	r2t->sent_r2t = 1;
314962306a36Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	ret = conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
315262306a36Sopenharmony_ci	if (ret < 0) {
315362306a36Sopenharmony_ci		return ret;
315462306a36Sopenharmony_ci	}
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	spin_lock_bh(&cmd->dataout_timeout_lock);
315762306a36Sopenharmony_ci	iscsit_start_dataout_timer(cmd, conn);
315862306a36Sopenharmony_ci	spin_unlock_bh(&cmd->dataout_timeout_lock);
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	return 0;
316162306a36Sopenharmony_ci}
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci/*
316462306a36Sopenharmony_ci *	@recovery: If called from iscsi_task_reassign_complete_write() for
316562306a36Sopenharmony_ci *		connection recovery.
316662306a36Sopenharmony_ci */
316762306a36Sopenharmony_ciint iscsit_build_r2ts_for_cmd(
316862306a36Sopenharmony_ci	struct iscsit_conn *conn,
316962306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
317062306a36Sopenharmony_ci	bool recovery)
317162306a36Sopenharmony_ci{
317262306a36Sopenharmony_ci	int first_r2t = 1;
317362306a36Sopenharmony_ci	u32 offset = 0, xfer_len = 0;
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci	spin_lock_bh(&cmd->r2t_lock);
317662306a36Sopenharmony_ci	if (cmd->cmd_flags & ICF_SENT_LAST_R2T) {
317762306a36Sopenharmony_ci		spin_unlock_bh(&cmd->r2t_lock);
317862306a36Sopenharmony_ci		return 0;
317962306a36Sopenharmony_ci	}
318062306a36Sopenharmony_ci
318162306a36Sopenharmony_ci	if (conn->sess->sess_ops->DataSequenceInOrder &&
318262306a36Sopenharmony_ci	    !recovery)
318362306a36Sopenharmony_ci		cmd->r2t_offset = max(cmd->r2t_offset, cmd->write_data_done);
318462306a36Sopenharmony_ci
318562306a36Sopenharmony_ci	while (cmd->outstanding_r2ts < conn->sess->sess_ops->MaxOutstandingR2T) {
318662306a36Sopenharmony_ci		if (conn->sess->sess_ops->DataSequenceInOrder) {
318762306a36Sopenharmony_ci			offset = cmd->r2t_offset;
318862306a36Sopenharmony_ci
318962306a36Sopenharmony_ci			if (first_r2t && recovery) {
319062306a36Sopenharmony_ci				int new_data_end = offset +
319162306a36Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength -
319262306a36Sopenharmony_ci					cmd->next_burst_len;
319362306a36Sopenharmony_ci
319462306a36Sopenharmony_ci				if (new_data_end > cmd->se_cmd.data_length)
319562306a36Sopenharmony_ci					xfer_len = cmd->se_cmd.data_length - offset;
319662306a36Sopenharmony_ci				else
319762306a36Sopenharmony_ci					xfer_len =
319862306a36Sopenharmony_ci						conn->sess->sess_ops->MaxBurstLength -
319962306a36Sopenharmony_ci						cmd->next_burst_len;
320062306a36Sopenharmony_ci			} else {
320162306a36Sopenharmony_ci				int new_data_end = offset +
320262306a36Sopenharmony_ci					conn->sess->sess_ops->MaxBurstLength;
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci				if (new_data_end > cmd->se_cmd.data_length)
320562306a36Sopenharmony_ci					xfer_len = cmd->se_cmd.data_length - offset;
320662306a36Sopenharmony_ci				else
320762306a36Sopenharmony_ci					xfer_len = conn->sess->sess_ops->MaxBurstLength;
320862306a36Sopenharmony_ci			}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci			if ((s32)xfer_len < 0) {
321162306a36Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
321262306a36Sopenharmony_ci				break;
321362306a36Sopenharmony_ci			}
321462306a36Sopenharmony_ci
321562306a36Sopenharmony_ci			cmd->r2t_offset += xfer_len;
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_ci			if (cmd->r2t_offset == cmd->se_cmd.data_length)
321862306a36Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
321962306a36Sopenharmony_ci		} else {
322062306a36Sopenharmony_ci			struct iscsi_seq *seq;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci			seq = iscsit_get_seq_holder_for_r2t(cmd);
322362306a36Sopenharmony_ci			if (!seq) {
322462306a36Sopenharmony_ci				spin_unlock_bh(&cmd->r2t_lock);
322562306a36Sopenharmony_ci				return -1;
322662306a36Sopenharmony_ci			}
322762306a36Sopenharmony_ci
322862306a36Sopenharmony_ci			offset = seq->offset;
322962306a36Sopenharmony_ci			xfer_len = seq->xfer_len;
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci			if (cmd->seq_send_order == cmd->seq_count)
323262306a36Sopenharmony_ci				cmd->cmd_flags |= ICF_SENT_LAST_R2T;
323362306a36Sopenharmony_ci		}
323462306a36Sopenharmony_ci		cmd->outstanding_r2ts++;
323562306a36Sopenharmony_ci		first_r2t = 0;
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci		if (iscsit_add_r2t_to_list(cmd, offset, xfer_len, 0, 0) < 0) {
323862306a36Sopenharmony_ci			spin_unlock_bh(&cmd->r2t_lock);
323962306a36Sopenharmony_ci			return -1;
324062306a36Sopenharmony_ci		}
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci		if (cmd->cmd_flags & ICF_SENT_LAST_R2T)
324362306a36Sopenharmony_ci			break;
324462306a36Sopenharmony_ci	}
324562306a36Sopenharmony_ci	spin_unlock_bh(&cmd->r2t_lock);
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	return 0;
324862306a36Sopenharmony_ci}
324962306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_r2ts_for_cmd);
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_civoid iscsit_build_rsp_pdu(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
325262306a36Sopenharmony_ci			bool inc_stat_sn, struct iscsi_scsi_rsp *hdr)
325362306a36Sopenharmony_ci{
325462306a36Sopenharmony_ci	if (inc_stat_sn)
325562306a36Sopenharmony_ci		cmd->stat_sn = conn->stat_sn++;
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	atomic_long_inc(&conn->sess->rsp_pdus);
325862306a36Sopenharmony_ci
325962306a36Sopenharmony_ci	memset(hdr, 0, ISCSI_HDR_LEN);
326062306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_CMD_RSP;
326162306a36Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
326262306a36Sopenharmony_ci	if (cmd->se_cmd.se_cmd_flags & SCF_OVERFLOW_BIT) {
326362306a36Sopenharmony_ci		hdr->flags |= ISCSI_FLAG_CMD_OVERFLOW;
326462306a36Sopenharmony_ci		hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
326562306a36Sopenharmony_ci	} else if (cmd->se_cmd.se_cmd_flags & SCF_UNDERFLOW_BIT) {
326662306a36Sopenharmony_ci		hdr->flags |= ISCSI_FLAG_CMD_UNDERFLOW;
326762306a36Sopenharmony_ci		hdr->residual_count = cpu_to_be32(cmd->se_cmd.residual_count);
326862306a36Sopenharmony_ci	}
326962306a36Sopenharmony_ci	hdr->response		= cmd->iscsi_response;
327062306a36Sopenharmony_ci	hdr->cmd_status		= cmd->se_cmd.scsi_status;
327162306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
327262306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
327362306a36Sopenharmony_ci
327462306a36Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
327562306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
327662306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ci	pr_debug("Built SCSI Response, ITT: 0x%08x, StatSN: 0x%08x,"
327962306a36Sopenharmony_ci		" Response: 0x%02x, SAM Status: 0x%02x, CID: %hu\n",
328062306a36Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, cmd->se_cmd.scsi_status,
328162306a36Sopenharmony_ci		cmd->se_cmd.scsi_status, conn->cid);
328262306a36Sopenharmony_ci}
328362306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_rsp_pdu);
328462306a36Sopenharmony_ci
328562306a36Sopenharmony_cistatic int iscsit_send_response(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
328662306a36Sopenharmony_ci{
328762306a36Sopenharmony_ci	struct iscsi_scsi_rsp *hdr = (struct iscsi_scsi_rsp *)&cmd->pdu[0];
328862306a36Sopenharmony_ci	bool inc_stat_sn = (cmd->i_state == ISTATE_SEND_STATUS);
328962306a36Sopenharmony_ci	void *data_buf = NULL;
329062306a36Sopenharmony_ci	u32 padding = 0, data_buf_len = 0;
329162306a36Sopenharmony_ci
329262306a36Sopenharmony_ci	iscsit_build_rsp_pdu(cmd, conn, inc_stat_sn, hdr);
329362306a36Sopenharmony_ci
329462306a36Sopenharmony_ci	/*
329562306a36Sopenharmony_ci	 * Attach SENSE DATA payload to iSCSI Response PDU
329662306a36Sopenharmony_ci	 */
329762306a36Sopenharmony_ci	if (cmd->se_cmd.sense_buffer &&
329862306a36Sopenharmony_ci	   ((cmd->se_cmd.se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
329962306a36Sopenharmony_ci	    (cmd->se_cmd.se_cmd_flags & SCF_EMULATED_TASK_SENSE))) {
330062306a36Sopenharmony_ci		put_unaligned_be16(cmd->se_cmd.scsi_sense_length, cmd->sense_buffer);
330162306a36Sopenharmony_ci		cmd->se_cmd.scsi_sense_length += sizeof (__be16);
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci		padding		= -(cmd->se_cmd.scsi_sense_length) & 3;
330462306a36Sopenharmony_ci		hton24(hdr->dlength, (u32)cmd->se_cmd.scsi_sense_length);
330562306a36Sopenharmony_ci		data_buf = cmd->sense_buffer;
330662306a36Sopenharmony_ci		data_buf_len = cmd->se_cmd.scsi_sense_length + padding;
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci		if (padding) {
330962306a36Sopenharmony_ci			memset(cmd->sense_buffer +
331062306a36Sopenharmony_ci				cmd->se_cmd.scsi_sense_length, 0, padding);
331162306a36Sopenharmony_ci			pr_debug("Adding %u bytes of padding to"
331262306a36Sopenharmony_ci				" SENSE.\n", padding);
331362306a36Sopenharmony_ci		}
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci		pr_debug("Attaching SENSE DATA: %u bytes to iSCSI"
331662306a36Sopenharmony_ci				" Response PDU\n",
331762306a36Sopenharmony_ci				cmd->se_cmd.scsi_sense_length);
331862306a36Sopenharmony_ci	}
331962306a36Sopenharmony_ci
332062306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, data_buf,
332162306a36Sopenharmony_ci						     data_buf_len);
332262306a36Sopenharmony_ci}
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_cistatic u8 iscsit_convert_tcm_tmr_rsp(struct se_tmr_req *se_tmr)
332562306a36Sopenharmony_ci{
332662306a36Sopenharmony_ci	switch (se_tmr->response) {
332762306a36Sopenharmony_ci	case TMR_FUNCTION_COMPLETE:
332862306a36Sopenharmony_ci		return ISCSI_TMF_RSP_COMPLETE;
332962306a36Sopenharmony_ci	case TMR_TASK_DOES_NOT_EXIST:
333062306a36Sopenharmony_ci		return ISCSI_TMF_RSP_NO_TASK;
333162306a36Sopenharmony_ci	case TMR_LUN_DOES_NOT_EXIST:
333262306a36Sopenharmony_ci		return ISCSI_TMF_RSP_NO_LUN;
333362306a36Sopenharmony_ci	case TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED:
333462306a36Sopenharmony_ci		return ISCSI_TMF_RSP_NOT_SUPPORTED;
333562306a36Sopenharmony_ci	case TMR_FUNCTION_REJECTED:
333662306a36Sopenharmony_ci	default:
333762306a36Sopenharmony_ci		return ISCSI_TMF_RSP_REJECTED;
333862306a36Sopenharmony_ci	}
333962306a36Sopenharmony_ci}
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_civoid
334262306a36Sopenharmony_ciiscsit_build_task_mgt_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
334362306a36Sopenharmony_ci			  struct iscsi_tm_rsp *hdr)
334462306a36Sopenharmony_ci{
334562306a36Sopenharmony_ci	struct se_tmr_req *se_tmr = cmd->se_cmd.se_tmr_req;
334662306a36Sopenharmony_ci
334762306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_SCSI_TMFUNC_RSP;
334862306a36Sopenharmony_ci	hdr->flags		= ISCSI_FLAG_CMD_FINAL;
334962306a36Sopenharmony_ci	hdr->response		= iscsit_convert_tcm_tmr_rsp(se_tmr);
335062306a36Sopenharmony_ci	hdr->itt		= cmd->init_task_tag;
335162306a36Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
335262306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
335562306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
335662306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
335762306a36Sopenharmony_ci
335862306a36Sopenharmony_ci	pr_debug("Built Task Management Response ITT: 0x%08x,"
335962306a36Sopenharmony_ci		" StatSN: 0x%08x, Response: 0x%02x, CID: %hu\n",
336062306a36Sopenharmony_ci		cmd->init_task_tag, cmd->stat_sn, hdr->response, conn->cid);
336162306a36Sopenharmony_ci}
336262306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_task_mgt_rsp);
336362306a36Sopenharmony_ci
336462306a36Sopenharmony_cistatic int
336562306a36Sopenharmony_ciiscsit_send_task_mgt_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn)
336662306a36Sopenharmony_ci{
336762306a36Sopenharmony_ci	struct iscsi_tm_rsp *hdr = (struct iscsi_tm_rsp *)&cmd->pdu[0];
336862306a36Sopenharmony_ci
336962306a36Sopenharmony_ci	iscsit_build_task_mgt_rsp(cmd, conn, hdr);
337062306a36Sopenharmony_ci
337162306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL, NULL, 0);
337262306a36Sopenharmony_ci}
337362306a36Sopenharmony_ci
337462306a36Sopenharmony_ci#define SENDTARGETS_BUF_LIMIT 32768U
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_cistatic int
337762306a36Sopenharmony_ciiscsit_build_sendtargets_response(struct iscsit_cmd *cmd,
337862306a36Sopenharmony_ci				  enum iscsit_transport_type network_transport,
337962306a36Sopenharmony_ci				  int skip_bytes, bool *completed)
338062306a36Sopenharmony_ci{
338162306a36Sopenharmony_ci	char *payload = NULL;
338262306a36Sopenharmony_ci	struct iscsit_conn *conn = cmd->conn;
338362306a36Sopenharmony_ci	struct iscsi_portal_group *tpg;
338462306a36Sopenharmony_ci	struct iscsi_tiqn *tiqn;
338562306a36Sopenharmony_ci	struct iscsi_tpg_np *tpg_np;
338662306a36Sopenharmony_ci	int buffer_len, end_of_buf = 0, len = 0, payload_len = 0;
338762306a36Sopenharmony_ci	int target_name_printed;
338862306a36Sopenharmony_ci	unsigned char buf[ISCSI_IQN_LEN+12]; /* iqn + "TargetName=" + \0 */
338962306a36Sopenharmony_ci	unsigned char *text_in = cmd->text_in_ptr, *text_ptr = NULL;
339062306a36Sopenharmony_ci	bool active;
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	buffer_len = min(conn->conn_ops->MaxRecvDataSegmentLength,
339362306a36Sopenharmony_ci			 SENDTARGETS_BUF_LIMIT);
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	payload = kzalloc(buffer_len, GFP_KERNEL);
339662306a36Sopenharmony_ci	if (!payload)
339762306a36Sopenharmony_ci		return -ENOMEM;
339862306a36Sopenharmony_ci
339962306a36Sopenharmony_ci	/*
340062306a36Sopenharmony_ci	 * Locate pointer to iqn./eui. string for ICF_SENDTARGETS_SINGLE
340162306a36Sopenharmony_ci	 * explicit case..
340262306a36Sopenharmony_ci	 */
340362306a36Sopenharmony_ci	if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) {
340462306a36Sopenharmony_ci		text_ptr = strchr(text_in, '=');
340562306a36Sopenharmony_ci		if (!text_ptr) {
340662306a36Sopenharmony_ci			pr_err("Unable to locate '=' string in text_in:"
340762306a36Sopenharmony_ci			       " %s\n", text_in);
340862306a36Sopenharmony_ci			kfree(payload);
340962306a36Sopenharmony_ci			return -EINVAL;
341062306a36Sopenharmony_ci		}
341162306a36Sopenharmony_ci		/*
341262306a36Sopenharmony_ci		 * Skip over '=' character..
341362306a36Sopenharmony_ci		 */
341462306a36Sopenharmony_ci		text_ptr += 1;
341562306a36Sopenharmony_ci	}
341662306a36Sopenharmony_ci
341762306a36Sopenharmony_ci	spin_lock(&tiqn_lock);
341862306a36Sopenharmony_ci	list_for_each_entry(tiqn, &g_tiqn_list, tiqn_list) {
341962306a36Sopenharmony_ci		if ((cmd->cmd_flags & ICF_SENDTARGETS_SINGLE) &&
342062306a36Sopenharmony_ci		     strcmp(tiqn->tiqn, text_ptr)) {
342162306a36Sopenharmony_ci			continue;
342262306a36Sopenharmony_ci		}
342362306a36Sopenharmony_ci
342462306a36Sopenharmony_ci		target_name_printed = 0;
342562306a36Sopenharmony_ci
342662306a36Sopenharmony_ci		spin_lock(&tiqn->tiqn_tpg_lock);
342762306a36Sopenharmony_ci		list_for_each_entry(tpg, &tiqn->tiqn_tpg_list, tpg_list) {
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci			/* If demo_mode_discovery=0 and generate_node_acls=0
343062306a36Sopenharmony_ci			 * (demo mode dislabed) do not return
343162306a36Sopenharmony_ci			 * TargetName+TargetAddress unless a NodeACL exists.
343262306a36Sopenharmony_ci			 */
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_ci			if ((tpg->tpg_attrib.generate_node_acls == 0) &&
343562306a36Sopenharmony_ci			    (tpg->tpg_attrib.demo_mode_discovery == 0) &&
343662306a36Sopenharmony_ci			    (!target_tpg_has_node_acl(&tpg->tpg_se_tpg,
343762306a36Sopenharmony_ci				cmd->conn->sess->sess_ops->InitiatorName))) {
343862306a36Sopenharmony_ci				continue;
343962306a36Sopenharmony_ci			}
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_ci			spin_lock(&tpg->tpg_state_lock);
344262306a36Sopenharmony_ci			active = (tpg->tpg_state == TPG_STATE_ACTIVE);
344362306a36Sopenharmony_ci			spin_unlock(&tpg->tpg_state_lock);
344462306a36Sopenharmony_ci
344562306a36Sopenharmony_ci			if (!active && tpg->tpg_attrib.tpg_enabled_sendtargets)
344662306a36Sopenharmony_ci				continue;
344762306a36Sopenharmony_ci
344862306a36Sopenharmony_ci			spin_lock(&tpg->tpg_np_lock);
344962306a36Sopenharmony_ci			list_for_each_entry(tpg_np, &tpg->tpg_gnp_list,
345062306a36Sopenharmony_ci						tpg_np_list) {
345162306a36Sopenharmony_ci				struct iscsi_np *np = tpg_np->tpg_np;
345262306a36Sopenharmony_ci				struct sockaddr_storage *sockaddr;
345362306a36Sopenharmony_ci
345462306a36Sopenharmony_ci				if (np->np_network_transport != network_transport)
345562306a36Sopenharmony_ci					continue;
345662306a36Sopenharmony_ci
345762306a36Sopenharmony_ci				if (!target_name_printed) {
345862306a36Sopenharmony_ci					len = sprintf(buf, "TargetName=%s",
345962306a36Sopenharmony_ci						      tiqn->tiqn);
346062306a36Sopenharmony_ci					len += 1;
346162306a36Sopenharmony_ci
346262306a36Sopenharmony_ci					if ((len + payload_len) > buffer_len) {
346362306a36Sopenharmony_ci						spin_unlock(&tpg->tpg_np_lock);
346462306a36Sopenharmony_ci						spin_unlock(&tiqn->tiqn_tpg_lock);
346562306a36Sopenharmony_ci						end_of_buf = 1;
346662306a36Sopenharmony_ci						goto eob;
346762306a36Sopenharmony_ci					}
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci					if (skip_bytes && len <= skip_bytes) {
347062306a36Sopenharmony_ci						skip_bytes -= len;
347162306a36Sopenharmony_ci					} else {
347262306a36Sopenharmony_ci						memcpy(payload + payload_len, buf, len);
347362306a36Sopenharmony_ci						payload_len += len;
347462306a36Sopenharmony_ci						target_name_printed = 1;
347562306a36Sopenharmony_ci						if (len > skip_bytes)
347662306a36Sopenharmony_ci							skip_bytes = 0;
347762306a36Sopenharmony_ci					}
347862306a36Sopenharmony_ci				}
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci				if (inet_addr_is_any((struct sockaddr *)&np->np_sockaddr))
348162306a36Sopenharmony_ci					sockaddr = &conn->local_sockaddr;
348262306a36Sopenharmony_ci				else
348362306a36Sopenharmony_ci					sockaddr = &np->np_sockaddr;
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci				len = sprintf(buf, "TargetAddress="
348662306a36Sopenharmony_ci					      "%pISpc,%hu",
348762306a36Sopenharmony_ci					      sockaddr,
348862306a36Sopenharmony_ci					      tpg->tpgt);
348962306a36Sopenharmony_ci				len += 1;
349062306a36Sopenharmony_ci
349162306a36Sopenharmony_ci				if ((len + payload_len) > buffer_len) {
349262306a36Sopenharmony_ci					spin_unlock(&tpg->tpg_np_lock);
349362306a36Sopenharmony_ci					spin_unlock(&tiqn->tiqn_tpg_lock);
349462306a36Sopenharmony_ci					end_of_buf = 1;
349562306a36Sopenharmony_ci					goto eob;
349662306a36Sopenharmony_ci				}
349762306a36Sopenharmony_ci
349862306a36Sopenharmony_ci				if (skip_bytes && len <= skip_bytes) {
349962306a36Sopenharmony_ci					skip_bytes -= len;
350062306a36Sopenharmony_ci				} else {
350162306a36Sopenharmony_ci					memcpy(payload + payload_len, buf, len);
350262306a36Sopenharmony_ci					payload_len += len;
350362306a36Sopenharmony_ci					if (len > skip_bytes)
350462306a36Sopenharmony_ci						skip_bytes = 0;
350562306a36Sopenharmony_ci				}
350662306a36Sopenharmony_ci			}
350762306a36Sopenharmony_ci			spin_unlock(&tpg->tpg_np_lock);
350862306a36Sopenharmony_ci		}
350962306a36Sopenharmony_ci		spin_unlock(&tiqn->tiqn_tpg_lock);
351062306a36Sopenharmony_cieob:
351162306a36Sopenharmony_ci		if (end_of_buf) {
351262306a36Sopenharmony_ci			*completed = false;
351362306a36Sopenharmony_ci			break;
351462306a36Sopenharmony_ci		}
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci		if (cmd->cmd_flags & ICF_SENDTARGETS_SINGLE)
351762306a36Sopenharmony_ci			break;
351862306a36Sopenharmony_ci	}
351962306a36Sopenharmony_ci	spin_unlock(&tiqn_lock);
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_ci	cmd->buf_ptr = payload;
352262306a36Sopenharmony_ci
352362306a36Sopenharmony_ci	return payload_len;
352462306a36Sopenharmony_ci}
352562306a36Sopenharmony_ci
352662306a36Sopenharmony_ciint
352762306a36Sopenharmony_ciiscsit_build_text_rsp(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
352862306a36Sopenharmony_ci		      struct iscsi_text_rsp *hdr,
352962306a36Sopenharmony_ci		      enum iscsit_transport_type network_transport)
353062306a36Sopenharmony_ci{
353162306a36Sopenharmony_ci	int text_length, padding;
353262306a36Sopenharmony_ci	bool completed = true;
353362306a36Sopenharmony_ci
353462306a36Sopenharmony_ci	text_length = iscsit_build_sendtargets_response(cmd, network_transport,
353562306a36Sopenharmony_ci							cmd->read_data_done,
353662306a36Sopenharmony_ci							&completed);
353762306a36Sopenharmony_ci	if (text_length < 0)
353862306a36Sopenharmony_ci		return text_length;
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	if (completed) {
354162306a36Sopenharmony_ci		hdr->flags = ISCSI_FLAG_CMD_FINAL;
354262306a36Sopenharmony_ci	} else {
354362306a36Sopenharmony_ci		hdr->flags = ISCSI_FLAG_TEXT_CONTINUE;
354462306a36Sopenharmony_ci		cmd->read_data_done += text_length;
354562306a36Sopenharmony_ci		if (cmd->targ_xfer_tag == 0xFFFFFFFF)
354662306a36Sopenharmony_ci			cmd->targ_xfer_tag = session_get_next_ttt(conn->sess);
354762306a36Sopenharmony_ci	}
354862306a36Sopenharmony_ci	hdr->opcode = ISCSI_OP_TEXT_RSP;
354962306a36Sopenharmony_ci	padding = ((-text_length) & 3);
355062306a36Sopenharmony_ci	hton24(hdr->dlength, text_length);
355162306a36Sopenharmony_ci	hdr->itt = cmd->init_task_tag;
355262306a36Sopenharmony_ci	hdr->ttt = cpu_to_be32(cmd->targ_xfer_tag);
355362306a36Sopenharmony_ci	cmd->stat_sn = conn->stat_sn++;
355462306a36Sopenharmony_ci	hdr->statsn = cpu_to_be32(cmd->stat_sn);
355562306a36Sopenharmony_ci
355662306a36Sopenharmony_ci	iscsit_increment_maxcmdsn(cmd, conn->sess);
355762306a36Sopenharmony_ci	/*
355862306a36Sopenharmony_ci	 * Reset maxcmdsn_inc in multi-part text payload exchanges to
355962306a36Sopenharmony_ci	 * correctly increment MaxCmdSN for each response answering a
356062306a36Sopenharmony_ci	 * non immediate text request with a valid CmdSN.
356162306a36Sopenharmony_ci	 */
356262306a36Sopenharmony_ci	cmd->maxcmdsn_inc = 0;
356362306a36Sopenharmony_ci	hdr->exp_cmdsn = cpu_to_be32(conn->sess->exp_cmd_sn);
356462306a36Sopenharmony_ci	hdr->max_cmdsn = cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
356562306a36Sopenharmony_ci
356662306a36Sopenharmony_ci	pr_debug("Built Text Response: ITT: 0x%08x, TTT: 0x%08x, StatSN: 0x%08x,"
356762306a36Sopenharmony_ci		" Length: %u, CID: %hu F: %d C: %d\n", cmd->init_task_tag,
356862306a36Sopenharmony_ci		cmd->targ_xfer_tag, cmd->stat_sn, text_length, conn->cid,
356962306a36Sopenharmony_ci		!!(hdr->flags & ISCSI_FLAG_CMD_FINAL),
357062306a36Sopenharmony_ci		!!(hdr->flags & ISCSI_FLAG_TEXT_CONTINUE));
357162306a36Sopenharmony_ci
357262306a36Sopenharmony_ci	return text_length + padding;
357362306a36Sopenharmony_ci}
357462306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_text_rsp);
357562306a36Sopenharmony_ci
357662306a36Sopenharmony_cistatic int iscsit_send_text_rsp(
357762306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
357862306a36Sopenharmony_ci	struct iscsit_conn *conn)
357962306a36Sopenharmony_ci{
358062306a36Sopenharmony_ci	struct iscsi_text_rsp *hdr = (struct iscsi_text_rsp *)cmd->pdu;
358162306a36Sopenharmony_ci	int text_length;
358262306a36Sopenharmony_ci
358362306a36Sopenharmony_ci	text_length = iscsit_build_text_rsp(cmd, conn, hdr,
358462306a36Sopenharmony_ci				conn->conn_transport->transport_type);
358562306a36Sopenharmony_ci	if (text_length < 0)
358662306a36Sopenharmony_ci		return text_length;
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
358962306a36Sopenharmony_ci						     cmd->buf_ptr,
359062306a36Sopenharmony_ci						     text_length);
359162306a36Sopenharmony_ci}
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_civoid
359462306a36Sopenharmony_ciiscsit_build_reject(struct iscsit_cmd *cmd, struct iscsit_conn *conn,
359562306a36Sopenharmony_ci		    struct iscsi_reject *hdr)
359662306a36Sopenharmony_ci{
359762306a36Sopenharmony_ci	hdr->opcode		= ISCSI_OP_REJECT;
359862306a36Sopenharmony_ci	hdr->reason		= cmd->reject_reason;
359962306a36Sopenharmony_ci	hdr->flags		|= ISCSI_FLAG_CMD_FINAL;
360062306a36Sopenharmony_ci	hton24(hdr->dlength, ISCSI_HDR_LEN);
360162306a36Sopenharmony_ci	hdr->ffffffff		= cpu_to_be32(0xffffffff);
360262306a36Sopenharmony_ci	cmd->stat_sn		= conn->stat_sn++;
360362306a36Sopenharmony_ci	hdr->statsn		= cpu_to_be32(cmd->stat_sn);
360462306a36Sopenharmony_ci	hdr->exp_cmdsn		= cpu_to_be32(conn->sess->exp_cmd_sn);
360562306a36Sopenharmony_ci	hdr->max_cmdsn		= cpu_to_be32((u32) atomic_read(&conn->sess->max_cmd_sn));
360662306a36Sopenharmony_ci
360762306a36Sopenharmony_ci}
360862306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_build_reject);
360962306a36Sopenharmony_ci
361062306a36Sopenharmony_cistatic int iscsit_send_reject(
361162306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
361262306a36Sopenharmony_ci	struct iscsit_conn *conn)
361362306a36Sopenharmony_ci{
361462306a36Sopenharmony_ci	struct iscsi_reject *hdr = (struct iscsi_reject *)&cmd->pdu[0];
361562306a36Sopenharmony_ci
361662306a36Sopenharmony_ci	iscsit_build_reject(cmd, conn, hdr);
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	pr_debug("Built Reject PDU StatSN: 0x%08x, Reason: 0x%02x,"
361962306a36Sopenharmony_ci		" CID: %hu\n", ntohl(hdr->statsn), hdr->reason, conn->cid);
362062306a36Sopenharmony_ci
362162306a36Sopenharmony_ci	return conn->conn_transport->iscsit_xmit_pdu(conn, cmd, NULL,
362262306a36Sopenharmony_ci						     cmd->buf_ptr,
362362306a36Sopenharmony_ci						     ISCSI_HDR_LEN);
362462306a36Sopenharmony_ci}
362562306a36Sopenharmony_ci
362662306a36Sopenharmony_civoid iscsit_thread_get_cpumask(struct iscsit_conn *conn)
362762306a36Sopenharmony_ci{
362862306a36Sopenharmony_ci	int ord, cpu;
362962306a36Sopenharmony_ci	cpumask_var_t conn_allowed_cpumask;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	/*
363262306a36Sopenharmony_ci	 * bitmap_id is assigned from iscsit_global->ts_bitmap from
363362306a36Sopenharmony_ci	 * within iscsit_start_kthreads()
363462306a36Sopenharmony_ci	 *
363562306a36Sopenharmony_ci	 * Here we use bitmap_id to determine which CPU that this
363662306a36Sopenharmony_ci	 * iSCSI connection's RX/TX threads will be scheduled to
363762306a36Sopenharmony_ci	 * execute upon.
363862306a36Sopenharmony_ci	 */
363962306a36Sopenharmony_ci	if (!zalloc_cpumask_var(&conn_allowed_cpumask, GFP_KERNEL)) {
364062306a36Sopenharmony_ci		ord = conn->bitmap_id % cpumask_weight(cpu_online_mask);
364162306a36Sopenharmony_ci		for_each_online_cpu(cpu) {
364262306a36Sopenharmony_ci			if (ord-- == 0) {
364362306a36Sopenharmony_ci				cpumask_set_cpu(cpu, conn->conn_cpumask);
364462306a36Sopenharmony_ci				return;
364562306a36Sopenharmony_ci			}
364662306a36Sopenharmony_ci		}
364762306a36Sopenharmony_ci	} else {
364862306a36Sopenharmony_ci		cpumask_and(conn_allowed_cpumask, iscsit_global->allowed_cpumask,
364962306a36Sopenharmony_ci			cpu_online_mask);
365062306a36Sopenharmony_ci
365162306a36Sopenharmony_ci		cpumask_clear(conn->conn_cpumask);
365262306a36Sopenharmony_ci		ord = conn->bitmap_id % cpumask_weight(conn_allowed_cpumask);
365362306a36Sopenharmony_ci		for_each_cpu(cpu, conn_allowed_cpumask) {
365462306a36Sopenharmony_ci			if (ord-- == 0) {
365562306a36Sopenharmony_ci				cpumask_set_cpu(cpu, conn->conn_cpumask);
365662306a36Sopenharmony_ci				free_cpumask_var(conn_allowed_cpumask);
365762306a36Sopenharmony_ci				return;
365862306a36Sopenharmony_ci			}
365962306a36Sopenharmony_ci		}
366062306a36Sopenharmony_ci		free_cpumask_var(conn_allowed_cpumask);
366162306a36Sopenharmony_ci	}
366262306a36Sopenharmony_ci	/*
366362306a36Sopenharmony_ci	 * This should never be reached..
366462306a36Sopenharmony_ci	 */
366562306a36Sopenharmony_ci	dump_stack();
366662306a36Sopenharmony_ci	cpumask_setall(conn->conn_cpumask);
366762306a36Sopenharmony_ci}
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_cistatic void iscsit_thread_reschedule(struct iscsit_conn *conn)
367062306a36Sopenharmony_ci{
367162306a36Sopenharmony_ci	/*
367262306a36Sopenharmony_ci	 * If iscsit_global->allowed_cpumask modified, reschedule iSCSI
367362306a36Sopenharmony_ci	 * connection's RX/TX threads update conn->allowed_cpumask.
367462306a36Sopenharmony_ci	 */
367562306a36Sopenharmony_ci	if (!cpumask_equal(iscsit_global->allowed_cpumask,
367662306a36Sopenharmony_ci			   conn->allowed_cpumask)) {
367762306a36Sopenharmony_ci		iscsit_thread_get_cpumask(conn);
367862306a36Sopenharmony_ci		conn->conn_tx_reset_cpumask = 1;
367962306a36Sopenharmony_ci		conn->conn_rx_reset_cpumask = 1;
368062306a36Sopenharmony_ci		cpumask_copy(conn->allowed_cpumask,
368162306a36Sopenharmony_ci			     iscsit_global->allowed_cpumask);
368262306a36Sopenharmony_ci	}
368362306a36Sopenharmony_ci}
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_civoid iscsit_thread_check_cpumask(
368662306a36Sopenharmony_ci	struct iscsit_conn *conn,
368762306a36Sopenharmony_ci	struct task_struct *p,
368862306a36Sopenharmony_ci	int mode)
368962306a36Sopenharmony_ci{
369062306a36Sopenharmony_ci	/*
369162306a36Sopenharmony_ci	 * The TX and RX threads maybe call iscsit_thread_check_cpumask()
369262306a36Sopenharmony_ci	 * at the same time. The RX thread might be faster and return from
369362306a36Sopenharmony_ci	 * iscsit_thread_reschedule() with conn_rx_reset_cpumask set to 0.
369462306a36Sopenharmony_ci	 * Then the TX thread sets it back to 1.
369562306a36Sopenharmony_ci	 * The next time the RX thread loops, it sees conn_rx_reset_cpumask
369662306a36Sopenharmony_ci	 * set to 1 and calls set_cpus_allowed_ptr() again and set it to 0.
369762306a36Sopenharmony_ci	 */
369862306a36Sopenharmony_ci	iscsit_thread_reschedule(conn);
369962306a36Sopenharmony_ci
370062306a36Sopenharmony_ci	/*
370162306a36Sopenharmony_ci	 * mode == 1 signals iscsi_target_tx_thread() usage.
370262306a36Sopenharmony_ci	 * mode == 0 signals iscsi_target_rx_thread() usage.
370362306a36Sopenharmony_ci	 */
370462306a36Sopenharmony_ci	if (mode == 1) {
370562306a36Sopenharmony_ci		if (!conn->conn_tx_reset_cpumask)
370662306a36Sopenharmony_ci			return;
370762306a36Sopenharmony_ci	} else {
370862306a36Sopenharmony_ci		if (!conn->conn_rx_reset_cpumask)
370962306a36Sopenharmony_ci			return;
371062306a36Sopenharmony_ci	}
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	/*
371362306a36Sopenharmony_ci	 * Update the CPU mask for this single kthread so that
371462306a36Sopenharmony_ci	 * both TX and RX kthreads are scheduled to run on the
371562306a36Sopenharmony_ci	 * same CPU.
371662306a36Sopenharmony_ci	 */
371762306a36Sopenharmony_ci	set_cpus_allowed_ptr(p, conn->conn_cpumask);
371862306a36Sopenharmony_ci	if (mode == 1)
371962306a36Sopenharmony_ci		conn->conn_tx_reset_cpumask = 0;
372062306a36Sopenharmony_ci	else
372162306a36Sopenharmony_ci		conn->conn_rx_reset_cpumask = 0;
372262306a36Sopenharmony_ci}
372362306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_thread_check_cpumask);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ciint
372662306a36Sopenharmony_ciiscsit_immediate_queue(struct iscsit_conn *conn, struct iscsit_cmd *cmd, int state)
372762306a36Sopenharmony_ci{
372862306a36Sopenharmony_ci	int ret;
372962306a36Sopenharmony_ci
373062306a36Sopenharmony_ci	switch (state) {
373162306a36Sopenharmony_ci	case ISTATE_SEND_R2T:
373262306a36Sopenharmony_ci		ret = iscsit_send_r2t(cmd, conn);
373362306a36Sopenharmony_ci		if (ret < 0)
373462306a36Sopenharmony_ci			goto err;
373562306a36Sopenharmony_ci		break;
373662306a36Sopenharmony_ci	case ISTATE_REMOVE:
373762306a36Sopenharmony_ci		spin_lock_bh(&conn->cmd_lock);
373862306a36Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
373962306a36Sopenharmony_ci		spin_unlock_bh(&conn->cmd_lock);
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci		iscsit_free_cmd(cmd, false);
374262306a36Sopenharmony_ci		break;
374362306a36Sopenharmony_ci	case ISTATE_SEND_NOPIN_WANT_RESPONSE:
374462306a36Sopenharmony_ci		iscsit_mod_nopin_response_timer(conn);
374562306a36Sopenharmony_ci		ret = iscsit_send_unsolicited_nopin(cmd, conn, 1);
374662306a36Sopenharmony_ci		if (ret < 0)
374762306a36Sopenharmony_ci			goto err;
374862306a36Sopenharmony_ci		break;
374962306a36Sopenharmony_ci	case ISTATE_SEND_NOPIN_NO_RESPONSE:
375062306a36Sopenharmony_ci		ret = iscsit_send_unsolicited_nopin(cmd, conn, 0);
375162306a36Sopenharmony_ci		if (ret < 0)
375262306a36Sopenharmony_ci			goto err;
375362306a36Sopenharmony_ci		break;
375462306a36Sopenharmony_ci	default:
375562306a36Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
375662306a36Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
375762306a36Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag, state,
375862306a36Sopenharmony_ci		       conn->cid);
375962306a36Sopenharmony_ci		goto err;
376062306a36Sopenharmony_ci	}
376162306a36Sopenharmony_ci
376262306a36Sopenharmony_ci	return 0;
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_cierr:
376562306a36Sopenharmony_ci	return -1;
376662306a36Sopenharmony_ci}
376762306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_immediate_queue);
376862306a36Sopenharmony_ci
376962306a36Sopenharmony_cistatic int
377062306a36Sopenharmony_ciiscsit_handle_immediate_queue(struct iscsit_conn *conn)
377162306a36Sopenharmony_ci{
377262306a36Sopenharmony_ci	struct iscsit_transport *t = conn->conn_transport;
377362306a36Sopenharmony_ci	struct iscsi_queue_req *qr;
377462306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
377562306a36Sopenharmony_ci	u8 state;
377662306a36Sopenharmony_ci	int ret;
377762306a36Sopenharmony_ci
377862306a36Sopenharmony_ci	while ((qr = iscsit_get_cmd_from_immediate_queue(conn))) {
377962306a36Sopenharmony_ci		atomic_set(&conn->check_immediate_queue, 0);
378062306a36Sopenharmony_ci		cmd = qr->cmd;
378162306a36Sopenharmony_ci		state = qr->state;
378262306a36Sopenharmony_ci		kmem_cache_free(lio_qr_cache, qr);
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci		ret = t->iscsit_immediate_queue(conn, cmd, state);
378562306a36Sopenharmony_ci		if (ret < 0)
378662306a36Sopenharmony_ci			return ret;
378762306a36Sopenharmony_ci	}
378862306a36Sopenharmony_ci
378962306a36Sopenharmony_ci	return 0;
379062306a36Sopenharmony_ci}
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ciint
379362306a36Sopenharmony_ciiscsit_response_queue(struct iscsit_conn *conn, struct iscsit_cmd *cmd, int state)
379462306a36Sopenharmony_ci{
379562306a36Sopenharmony_ci	int ret;
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_cicheck_rsp_state:
379862306a36Sopenharmony_ci	switch (state) {
379962306a36Sopenharmony_ci	case ISTATE_SEND_DATAIN:
380062306a36Sopenharmony_ci		ret = iscsit_send_datain(cmd, conn);
380162306a36Sopenharmony_ci		if (ret < 0)
380262306a36Sopenharmony_ci			goto err;
380362306a36Sopenharmony_ci		else if (!ret)
380462306a36Sopenharmony_ci			/* more drs */
380562306a36Sopenharmony_ci			goto check_rsp_state;
380662306a36Sopenharmony_ci		else if (ret == 1) {
380762306a36Sopenharmony_ci			/* all done */
380862306a36Sopenharmony_ci			spin_lock_bh(&cmd->istate_lock);
380962306a36Sopenharmony_ci			cmd->i_state = ISTATE_SENT_STATUS;
381062306a36Sopenharmony_ci			spin_unlock_bh(&cmd->istate_lock);
381162306a36Sopenharmony_ci
381262306a36Sopenharmony_ci			if (atomic_read(&conn->check_immediate_queue))
381362306a36Sopenharmony_ci				return 1;
381462306a36Sopenharmony_ci
381562306a36Sopenharmony_ci			return 0;
381662306a36Sopenharmony_ci		} else if (ret == 2) {
381762306a36Sopenharmony_ci			/* Still must send status,
381862306a36Sopenharmony_ci			   SCF_TRANSPORT_TASK_SENSE was set */
381962306a36Sopenharmony_ci			spin_lock_bh(&cmd->istate_lock);
382062306a36Sopenharmony_ci			cmd->i_state = ISTATE_SEND_STATUS;
382162306a36Sopenharmony_ci			spin_unlock_bh(&cmd->istate_lock);
382262306a36Sopenharmony_ci			state = ISTATE_SEND_STATUS;
382362306a36Sopenharmony_ci			goto check_rsp_state;
382462306a36Sopenharmony_ci		}
382562306a36Sopenharmony_ci
382662306a36Sopenharmony_ci		break;
382762306a36Sopenharmony_ci	case ISTATE_SEND_STATUS:
382862306a36Sopenharmony_ci	case ISTATE_SEND_STATUS_RECOVERY:
382962306a36Sopenharmony_ci		ret = iscsit_send_response(cmd, conn);
383062306a36Sopenharmony_ci		break;
383162306a36Sopenharmony_ci	case ISTATE_SEND_LOGOUTRSP:
383262306a36Sopenharmony_ci		ret = iscsit_send_logout(cmd, conn);
383362306a36Sopenharmony_ci		break;
383462306a36Sopenharmony_ci	case ISTATE_SEND_ASYNCMSG:
383562306a36Sopenharmony_ci		ret = iscsit_send_conn_drop_async_message(
383662306a36Sopenharmony_ci			cmd, conn);
383762306a36Sopenharmony_ci		break;
383862306a36Sopenharmony_ci	case ISTATE_SEND_NOPIN:
383962306a36Sopenharmony_ci		ret = iscsit_send_nopin(cmd, conn);
384062306a36Sopenharmony_ci		break;
384162306a36Sopenharmony_ci	case ISTATE_SEND_REJECT:
384262306a36Sopenharmony_ci		ret = iscsit_send_reject(cmd, conn);
384362306a36Sopenharmony_ci		break;
384462306a36Sopenharmony_ci	case ISTATE_SEND_TASKMGTRSP:
384562306a36Sopenharmony_ci		ret = iscsit_send_task_mgt_rsp(cmd, conn);
384662306a36Sopenharmony_ci		if (ret != 0)
384762306a36Sopenharmony_ci			break;
384862306a36Sopenharmony_ci		ret = iscsit_tmr_post_handler(cmd, conn);
384962306a36Sopenharmony_ci		if (ret != 0)
385062306a36Sopenharmony_ci			iscsit_fall_back_to_erl0(conn->sess);
385162306a36Sopenharmony_ci		break;
385262306a36Sopenharmony_ci	case ISTATE_SEND_TEXTRSP:
385362306a36Sopenharmony_ci		ret = iscsit_send_text_rsp(cmd, conn);
385462306a36Sopenharmony_ci		break;
385562306a36Sopenharmony_ci	default:
385662306a36Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
385762306a36Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
385862306a36Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag,
385962306a36Sopenharmony_ci		       state, conn->cid);
386062306a36Sopenharmony_ci		goto err;
386162306a36Sopenharmony_ci	}
386262306a36Sopenharmony_ci	if (ret < 0)
386362306a36Sopenharmony_ci		goto err;
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	switch (state) {
386662306a36Sopenharmony_ci	case ISTATE_SEND_LOGOUTRSP:
386762306a36Sopenharmony_ci		if (!iscsit_logout_post_handler(cmd, conn))
386862306a36Sopenharmony_ci			return -ECONNRESET;
386962306a36Sopenharmony_ci		fallthrough;
387062306a36Sopenharmony_ci	case ISTATE_SEND_STATUS:
387162306a36Sopenharmony_ci	case ISTATE_SEND_ASYNCMSG:
387262306a36Sopenharmony_ci	case ISTATE_SEND_NOPIN:
387362306a36Sopenharmony_ci	case ISTATE_SEND_STATUS_RECOVERY:
387462306a36Sopenharmony_ci	case ISTATE_SEND_TEXTRSP:
387562306a36Sopenharmony_ci	case ISTATE_SEND_TASKMGTRSP:
387662306a36Sopenharmony_ci	case ISTATE_SEND_REJECT:
387762306a36Sopenharmony_ci		spin_lock_bh(&cmd->istate_lock);
387862306a36Sopenharmony_ci		cmd->i_state = ISTATE_SENT_STATUS;
387962306a36Sopenharmony_ci		spin_unlock_bh(&cmd->istate_lock);
388062306a36Sopenharmony_ci		break;
388162306a36Sopenharmony_ci	default:
388262306a36Sopenharmony_ci		pr_err("Unknown Opcode: 0x%02x ITT:"
388362306a36Sopenharmony_ci		       " 0x%08x, i_state: %d on CID: %hu\n",
388462306a36Sopenharmony_ci		       cmd->iscsi_opcode, cmd->init_task_tag,
388562306a36Sopenharmony_ci		       cmd->i_state, conn->cid);
388662306a36Sopenharmony_ci		goto err;
388762306a36Sopenharmony_ci	}
388862306a36Sopenharmony_ci
388962306a36Sopenharmony_ci	if (atomic_read(&conn->check_immediate_queue))
389062306a36Sopenharmony_ci		return 1;
389162306a36Sopenharmony_ci
389262306a36Sopenharmony_ci	return 0;
389362306a36Sopenharmony_ci
389462306a36Sopenharmony_cierr:
389562306a36Sopenharmony_ci	return -1;
389662306a36Sopenharmony_ci}
389762306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_response_queue);
389862306a36Sopenharmony_ci
389962306a36Sopenharmony_cistatic int iscsit_handle_response_queue(struct iscsit_conn *conn)
390062306a36Sopenharmony_ci{
390162306a36Sopenharmony_ci	struct iscsit_transport *t = conn->conn_transport;
390262306a36Sopenharmony_ci	struct iscsi_queue_req *qr;
390362306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
390462306a36Sopenharmony_ci	u8 state;
390562306a36Sopenharmony_ci	int ret;
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci	while ((qr = iscsit_get_cmd_from_response_queue(conn))) {
390862306a36Sopenharmony_ci		cmd = qr->cmd;
390962306a36Sopenharmony_ci		state = qr->state;
391062306a36Sopenharmony_ci		kmem_cache_free(lio_qr_cache, qr);
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci		ret = t->iscsit_response_queue(conn, cmd, state);
391362306a36Sopenharmony_ci		if (ret == 1 || ret < 0)
391462306a36Sopenharmony_ci			return ret;
391562306a36Sopenharmony_ci	}
391662306a36Sopenharmony_ci
391762306a36Sopenharmony_ci	return 0;
391862306a36Sopenharmony_ci}
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ciint iscsi_target_tx_thread(void *arg)
392162306a36Sopenharmony_ci{
392262306a36Sopenharmony_ci	int ret = 0;
392362306a36Sopenharmony_ci	struct iscsit_conn *conn = arg;
392462306a36Sopenharmony_ci	bool conn_freed = false;
392562306a36Sopenharmony_ci
392662306a36Sopenharmony_ci	/*
392762306a36Sopenharmony_ci	 * Allow ourselves to be interrupted by SIGINT so that a
392862306a36Sopenharmony_ci	 * connection recovery / failure event can be triggered externally.
392962306a36Sopenharmony_ci	 */
393062306a36Sopenharmony_ci	allow_signal(SIGINT);
393162306a36Sopenharmony_ci
393262306a36Sopenharmony_ci	while (!kthread_should_stop()) {
393362306a36Sopenharmony_ci		/*
393462306a36Sopenharmony_ci		 * Ensure that both TX and RX per connection kthreads
393562306a36Sopenharmony_ci		 * are scheduled to run on the same CPU.
393662306a36Sopenharmony_ci		 */
393762306a36Sopenharmony_ci		iscsit_thread_check_cpumask(conn, current, 1);
393862306a36Sopenharmony_ci
393962306a36Sopenharmony_ci		wait_event_interruptible(conn->queues_wq,
394062306a36Sopenharmony_ci					 !iscsit_conn_all_queues_empty(conn));
394162306a36Sopenharmony_ci
394262306a36Sopenharmony_ci		if (signal_pending(current))
394362306a36Sopenharmony_ci			goto transport_err;
394462306a36Sopenharmony_ci
394562306a36Sopenharmony_ciget_immediate:
394662306a36Sopenharmony_ci		ret = iscsit_handle_immediate_queue(conn);
394762306a36Sopenharmony_ci		if (ret < 0)
394862306a36Sopenharmony_ci			goto transport_err;
394962306a36Sopenharmony_ci
395062306a36Sopenharmony_ci		ret = iscsit_handle_response_queue(conn);
395162306a36Sopenharmony_ci		if (ret == 1) {
395262306a36Sopenharmony_ci			goto get_immediate;
395362306a36Sopenharmony_ci		} else if (ret == -ECONNRESET) {
395462306a36Sopenharmony_ci			conn_freed = true;
395562306a36Sopenharmony_ci			goto out;
395662306a36Sopenharmony_ci		} else if (ret < 0) {
395762306a36Sopenharmony_ci			goto transport_err;
395862306a36Sopenharmony_ci		}
395962306a36Sopenharmony_ci	}
396062306a36Sopenharmony_ci
396162306a36Sopenharmony_citransport_err:
396262306a36Sopenharmony_ci	/*
396362306a36Sopenharmony_ci	 * Avoid the normal connection failure code-path if this connection
396462306a36Sopenharmony_ci	 * is still within LOGIN mode, and iscsi_np process context is
396562306a36Sopenharmony_ci	 * responsible for cleaning up the early connection failure.
396662306a36Sopenharmony_ci	 */
396762306a36Sopenharmony_ci	if (conn->conn_state != TARG_CONN_STATE_IN_LOGIN)
396862306a36Sopenharmony_ci		iscsit_take_action_for_connection_exit(conn, &conn_freed);
396962306a36Sopenharmony_ciout:
397062306a36Sopenharmony_ci	if (!conn_freed) {
397162306a36Sopenharmony_ci		while (!kthread_should_stop()) {
397262306a36Sopenharmony_ci			msleep(100);
397362306a36Sopenharmony_ci		}
397462306a36Sopenharmony_ci	}
397562306a36Sopenharmony_ci	return 0;
397662306a36Sopenharmony_ci}
397762306a36Sopenharmony_ci
397862306a36Sopenharmony_cistatic int iscsi_target_rx_opcode(struct iscsit_conn *conn, unsigned char *buf)
397962306a36Sopenharmony_ci{
398062306a36Sopenharmony_ci	struct iscsi_hdr *hdr = (struct iscsi_hdr *)buf;
398162306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
398262306a36Sopenharmony_ci	int ret = 0;
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	switch (hdr->opcode & ISCSI_OPCODE_MASK) {
398562306a36Sopenharmony_ci	case ISCSI_OP_SCSI_CMD:
398662306a36Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
398762306a36Sopenharmony_ci		if (!cmd)
398862306a36Sopenharmony_ci			goto reject;
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci		ret = iscsit_handle_scsi_cmd(conn, cmd, buf);
399162306a36Sopenharmony_ci		break;
399262306a36Sopenharmony_ci	case ISCSI_OP_SCSI_DATA_OUT:
399362306a36Sopenharmony_ci		ret = iscsit_handle_data_out(conn, buf);
399462306a36Sopenharmony_ci		break;
399562306a36Sopenharmony_ci	case ISCSI_OP_NOOP_OUT:
399662306a36Sopenharmony_ci		cmd = NULL;
399762306a36Sopenharmony_ci		if (hdr->ttt == cpu_to_be32(0xFFFFFFFF)) {
399862306a36Sopenharmony_ci			cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
399962306a36Sopenharmony_ci			if (!cmd)
400062306a36Sopenharmony_ci				goto reject;
400162306a36Sopenharmony_ci		}
400262306a36Sopenharmony_ci		ret = iscsit_handle_nop_out(conn, cmd, buf);
400362306a36Sopenharmony_ci		break;
400462306a36Sopenharmony_ci	case ISCSI_OP_SCSI_TMFUNC:
400562306a36Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
400662306a36Sopenharmony_ci		if (!cmd)
400762306a36Sopenharmony_ci			goto reject;
400862306a36Sopenharmony_ci
400962306a36Sopenharmony_ci		ret = iscsit_handle_task_mgt_cmd(conn, cmd, buf);
401062306a36Sopenharmony_ci		break;
401162306a36Sopenharmony_ci	case ISCSI_OP_TEXT:
401262306a36Sopenharmony_ci		if (hdr->ttt != cpu_to_be32(0xFFFFFFFF)) {
401362306a36Sopenharmony_ci			cmd = iscsit_find_cmd_from_itt(conn, hdr->itt);
401462306a36Sopenharmony_ci			if (!cmd)
401562306a36Sopenharmony_ci				goto reject;
401662306a36Sopenharmony_ci		} else {
401762306a36Sopenharmony_ci			cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
401862306a36Sopenharmony_ci			if (!cmd)
401962306a36Sopenharmony_ci				goto reject;
402062306a36Sopenharmony_ci		}
402162306a36Sopenharmony_ci
402262306a36Sopenharmony_ci		ret = iscsit_handle_text_cmd(conn, cmd, buf);
402362306a36Sopenharmony_ci		break;
402462306a36Sopenharmony_ci	case ISCSI_OP_LOGOUT:
402562306a36Sopenharmony_ci		cmd = iscsit_allocate_cmd(conn, TASK_INTERRUPTIBLE);
402662306a36Sopenharmony_ci		if (!cmd)
402762306a36Sopenharmony_ci			goto reject;
402862306a36Sopenharmony_ci
402962306a36Sopenharmony_ci		ret = iscsit_handle_logout_cmd(conn, cmd, buf);
403062306a36Sopenharmony_ci		if (ret > 0)
403162306a36Sopenharmony_ci			wait_for_completion_timeout(&conn->conn_logout_comp,
403262306a36Sopenharmony_ci					SECONDS_FOR_LOGOUT_COMP * HZ);
403362306a36Sopenharmony_ci		break;
403462306a36Sopenharmony_ci	case ISCSI_OP_SNACK:
403562306a36Sopenharmony_ci		ret = iscsit_handle_snack(conn, buf);
403662306a36Sopenharmony_ci		break;
403762306a36Sopenharmony_ci	default:
403862306a36Sopenharmony_ci		pr_err("Got unknown iSCSI OpCode: 0x%02x\n", hdr->opcode);
403962306a36Sopenharmony_ci		if (!conn->sess->sess_ops->ErrorRecoveryLevel) {
404062306a36Sopenharmony_ci			pr_err("Cannot recover from unknown"
404162306a36Sopenharmony_ci			" opcode while ERL=0, closing iSCSI connection.\n");
404262306a36Sopenharmony_ci			return -1;
404362306a36Sopenharmony_ci		}
404462306a36Sopenharmony_ci		pr_err("Unable to recover from unknown opcode while OFMarker=No,"
404562306a36Sopenharmony_ci		       " closing iSCSI connection.\n");
404662306a36Sopenharmony_ci		ret = -1;
404762306a36Sopenharmony_ci		break;
404862306a36Sopenharmony_ci	}
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci	return ret;
405162306a36Sopenharmony_cireject:
405262306a36Sopenharmony_ci	return iscsit_add_reject(conn, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf);
405362306a36Sopenharmony_ci}
405462306a36Sopenharmony_ci
405562306a36Sopenharmony_cistatic bool iscsi_target_check_conn_state(struct iscsit_conn *conn)
405662306a36Sopenharmony_ci{
405762306a36Sopenharmony_ci	bool ret;
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
406062306a36Sopenharmony_ci	ret = (conn->conn_state != TARG_CONN_STATE_LOGGED_IN);
406162306a36Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
406262306a36Sopenharmony_ci
406362306a36Sopenharmony_ci	return ret;
406462306a36Sopenharmony_ci}
406562306a36Sopenharmony_ci
406662306a36Sopenharmony_cistatic void iscsit_get_rx_pdu(struct iscsit_conn *conn)
406762306a36Sopenharmony_ci{
406862306a36Sopenharmony_ci	int ret;
406962306a36Sopenharmony_ci	u8 *buffer, *tmp_buf, opcode;
407062306a36Sopenharmony_ci	u32 checksum = 0, digest = 0;
407162306a36Sopenharmony_ci	struct iscsi_hdr *hdr;
407262306a36Sopenharmony_ci	struct kvec iov;
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL);
407562306a36Sopenharmony_ci	if (!buffer)
407662306a36Sopenharmony_ci		return;
407762306a36Sopenharmony_ci
407862306a36Sopenharmony_ci	while (!kthread_should_stop()) {
407962306a36Sopenharmony_ci		/*
408062306a36Sopenharmony_ci		 * Ensure that both TX and RX per connection kthreads
408162306a36Sopenharmony_ci		 * are scheduled to run on the same CPU.
408262306a36Sopenharmony_ci		 */
408362306a36Sopenharmony_ci		iscsit_thread_check_cpumask(conn, current, 0);
408462306a36Sopenharmony_ci
408562306a36Sopenharmony_ci		memset(&iov, 0, sizeof(struct kvec));
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci		iov.iov_base	= buffer;
408862306a36Sopenharmony_ci		iov.iov_len	= ISCSI_HDR_LEN;
408962306a36Sopenharmony_ci
409062306a36Sopenharmony_ci		ret = rx_data(conn, &iov, 1, ISCSI_HDR_LEN);
409162306a36Sopenharmony_ci		if (ret != ISCSI_HDR_LEN) {
409262306a36Sopenharmony_ci			iscsit_rx_thread_wait_for_tcp(conn);
409362306a36Sopenharmony_ci			break;
409462306a36Sopenharmony_ci		}
409562306a36Sopenharmony_ci
409662306a36Sopenharmony_ci		hdr = (struct iscsi_hdr *) buffer;
409762306a36Sopenharmony_ci		if (hdr->hlength) {
409862306a36Sopenharmony_ci			iov.iov_len = hdr->hlength * 4;
409962306a36Sopenharmony_ci			tmp_buf = krealloc(buffer,
410062306a36Sopenharmony_ci					  ISCSI_HDR_LEN + iov.iov_len,
410162306a36Sopenharmony_ci					  GFP_KERNEL);
410262306a36Sopenharmony_ci			if (!tmp_buf)
410362306a36Sopenharmony_ci				break;
410462306a36Sopenharmony_ci
410562306a36Sopenharmony_ci			buffer = tmp_buf;
410662306a36Sopenharmony_ci			iov.iov_base = &buffer[ISCSI_HDR_LEN];
410762306a36Sopenharmony_ci
410862306a36Sopenharmony_ci			ret = rx_data(conn, &iov, 1, iov.iov_len);
410962306a36Sopenharmony_ci			if (ret != iov.iov_len) {
411062306a36Sopenharmony_ci				iscsit_rx_thread_wait_for_tcp(conn);
411162306a36Sopenharmony_ci				break;
411262306a36Sopenharmony_ci			}
411362306a36Sopenharmony_ci		}
411462306a36Sopenharmony_ci
411562306a36Sopenharmony_ci		if (conn->conn_ops->HeaderDigest) {
411662306a36Sopenharmony_ci			iov.iov_base	= &digest;
411762306a36Sopenharmony_ci			iov.iov_len	= ISCSI_CRC_LEN;
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ci			ret = rx_data(conn, &iov, 1, ISCSI_CRC_LEN);
412062306a36Sopenharmony_ci			if (ret != ISCSI_CRC_LEN) {
412162306a36Sopenharmony_ci				iscsit_rx_thread_wait_for_tcp(conn);
412262306a36Sopenharmony_ci				break;
412362306a36Sopenharmony_ci			}
412462306a36Sopenharmony_ci
412562306a36Sopenharmony_ci			iscsit_do_crypto_hash_buf(conn->conn_rx_hash, buffer,
412662306a36Sopenharmony_ci						  ISCSI_HDR_LEN, 0, NULL,
412762306a36Sopenharmony_ci						  &checksum);
412862306a36Sopenharmony_ci
412962306a36Sopenharmony_ci			if (digest != checksum) {
413062306a36Sopenharmony_ci				pr_err("HeaderDigest CRC32C failed,"
413162306a36Sopenharmony_ci					" received 0x%08x, computed 0x%08x\n",
413262306a36Sopenharmony_ci					digest, checksum);
413362306a36Sopenharmony_ci				/*
413462306a36Sopenharmony_ci				 * Set the PDU to 0xff so it will intentionally
413562306a36Sopenharmony_ci				 * hit default in the switch below.
413662306a36Sopenharmony_ci				 */
413762306a36Sopenharmony_ci				memset(buffer, 0xff, ISCSI_HDR_LEN);
413862306a36Sopenharmony_ci				atomic_long_inc(&conn->sess->conn_digest_errors);
413962306a36Sopenharmony_ci			} else {
414062306a36Sopenharmony_ci				pr_debug("Got HeaderDigest CRC32C"
414162306a36Sopenharmony_ci						" 0x%08x\n", checksum);
414262306a36Sopenharmony_ci			}
414362306a36Sopenharmony_ci		}
414462306a36Sopenharmony_ci
414562306a36Sopenharmony_ci		if (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT)
414662306a36Sopenharmony_ci			break;
414762306a36Sopenharmony_ci
414862306a36Sopenharmony_ci		opcode = buffer[0] & ISCSI_OPCODE_MASK;
414962306a36Sopenharmony_ci
415062306a36Sopenharmony_ci		if (conn->sess->sess_ops->SessionType &&
415162306a36Sopenharmony_ci		   ((!(opcode & ISCSI_OP_TEXT)) ||
415262306a36Sopenharmony_ci		    (!(opcode & ISCSI_OP_LOGOUT)))) {
415362306a36Sopenharmony_ci			pr_err("Received illegal iSCSI Opcode: 0x%02x"
415462306a36Sopenharmony_ci			" while in Discovery Session, rejecting.\n", opcode);
415562306a36Sopenharmony_ci			iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR,
415662306a36Sopenharmony_ci					  buffer);
415762306a36Sopenharmony_ci			break;
415862306a36Sopenharmony_ci		}
415962306a36Sopenharmony_ci
416062306a36Sopenharmony_ci		ret = iscsi_target_rx_opcode(conn, buffer);
416162306a36Sopenharmony_ci		if (ret < 0)
416262306a36Sopenharmony_ci			break;
416362306a36Sopenharmony_ci	}
416462306a36Sopenharmony_ci
416562306a36Sopenharmony_ci	kfree(buffer);
416662306a36Sopenharmony_ci}
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ciint iscsi_target_rx_thread(void *arg)
416962306a36Sopenharmony_ci{
417062306a36Sopenharmony_ci	int rc;
417162306a36Sopenharmony_ci	struct iscsit_conn *conn = arg;
417262306a36Sopenharmony_ci	bool conn_freed = false;
417362306a36Sopenharmony_ci
417462306a36Sopenharmony_ci	/*
417562306a36Sopenharmony_ci	 * Allow ourselves to be interrupted by SIGINT so that a
417662306a36Sopenharmony_ci	 * connection recovery / failure event can be triggered externally.
417762306a36Sopenharmony_ci	 */
417862306a36Sopenharmony_ci	allow_signal(SIGINT);
417962306a36Sopenharmony_ci	/*
418062306a36Sopenharmony_ci	 * Wait for iscsi_post_login_handler() to complete before allowing
418162306a36Sopenharmony_ci	 * incoming iscsi/tcp socket I/O, and/or failing the connection.
418262306a36Sopenharmony_ci	 */
418362306a36Sopenharmony_ci	rc = wait_for_completion_interruptible(&conn->rx_login_comp);
418462306a36Sopenharmony_ci	if (rc < 0 || iscsi_target_check_conn_state(conn))
418562306a36Sopenharmony_ci		goto out;
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci	if (!conn->conn_transport->iscsit_get_rx_pdu)
418862306a36Sopenharmony_ci		return 0;
418962306a36Sopenharmony_ci
419062306a36Sopenharmony_ci	conn->conn_transport->iscsit_get_rx_pdu(conn);
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	if (!signal_pending(current))
419362306a36Sopenharmony_ci		atomic_set(&conn->transport_failed, 1);
419462306a36Sopenharmony_ci	iscsit_take_action_for_connection_exit(conn, &conn_freed);
419562306a36Sopenharmony_ci
419662306a36Sopenharmony_ciout:
419762306a36Sopenharmony_ci	if (!conn_freed) {
419862306a36Sopenharmony_ci		while (!kthread_should_stop()) {
419962306a36Sopenharmony_ci			msleep(100);
420062306a36Sopenharmony_ci		}
420162306a36Sopenharmony_ci	}
420262306a36Sopenharmony_ci
420362306a36Sopenharmony_ci	return 0;
420462306a36Sopenharmony_ci}
420562306a36Sopenharmony_ci
420662306a36Sopenharmony_cistatic void iscsit_release_commands_from_conn(struct iscsit_conn *conn)
420762306a36Sopenharmony_ci{
420862306a36Sopenharmony_ci	LIST_HEAD(tmp_list);
420962306a36Sopenharmony_ci	struct iscsit_cmd *cmd = NULL, *cmd_tmp = NULL;
421062306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
421162306a36Sopenharmony_ci	/*
421262306a36Sopenharmony_ci	 * We expect this function to only ever be called from either RX or TX
421362306a36Sopenharmony_ci	 * thread context via iscsit_close_connection() once the other context
421462306a36Sopenharmony_ci	 * has been reset -> returned sleeping pre-handler state.
421562306a36Sopenharmony_ci	 */
421662306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
421762306a36Sopenharmony_ci	list_splice_init(&conn->conn_cmd_list, &tmp_list);
421862306a36Sopenharmony_ci
421962306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
422062306a36Sopenharmony_ci		struct se_cmd *se_cmd = &cmd->se_cmd;
422162306a36Sopenharmony_ci
422262306a36Sopenharmony_ci		if (!se_cmd->se_tfo)
422362306a36Sopenharmony_ci			continue;
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci		spin_lock_irq(&se_cmd->t_state_lock);
422662306a36Sopenharmony_ci		if (se_cmd->transport_state & CMD_T_ABORTED) {
422762306a36Sopenharmony_ci			if (!(se_cmd->transport_state & CMD_T_TAS))
422862306a36Sopenharmony_ci				/*
422962306a36Sopenharmony_ci				 * LIO's abort path owns the cleanup for this,
423062306a36Sopenharmony_ci				 * so put it back on the list and let
423162306a36Sopenharmony_ci				 * aborted_task handle it.
423262306a36Sopenharmony_ci				 */
423362306a36Sopenharmony_ci				list_move_tail(&cmd->i_conn_node,
423462306a36Sopenharmony_ci					       &conn->conn_cmd_list);
423562306a36Sopenharmony_ci		} else {
423662306a36Sopenharmony_ci			se_cmd->transport_state |= CMD_T_FABRIC_STOP;
423762306a36Sopenharmony_ci		}
423862306a36Sopenharmony_ci
423962306a36Sopenharmony_ci		if (cmd->se_cmd.t_state == TRANSPORT_WRITE_PENDING) {
424062306a36Sopenharmony_ci			/*
424162306a36Sopenharmony_ci			 * We never submitted the cmd to LIO core, so we have
424262306a36Sopenharmony_ci			 * to tell LIO to perform the completion process.
424362306a36Sopenharmony_ci			 */
424462306a36Sopenharmony_ci			spin_unlock_irq(&se_cmd->t_state_lock);
424562306a36Sopenharmony_ci			target_complete_cmd(&cmd->se_cmd, SAM_STAT_TASK_ABORTED);
424662306a36Sopenharmony_ci			continue;
424762306a36Sopenharmony_ci		}
424862306a36Sopenharmony_ci		spin_unlock_irq(&se_cmd->t_state_lock);
424962306a36Sopenharmony_ci	}
425062306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci	list_for_each_entry_safe(cmd, cmd_tmp, &tmp_list, i_conn_node) {
425362306a36Sopenharmony_ci		list_del_init(&cmd->i_conn_node);
425462306a36Sopenharmony_ci
425562306a36Sopenharmony_ci		iscsit_increment_maxcmdsn(cmd, sess);
425662306a36Sopenharmony_ci		iscsit_free_cmd(cmd, true);
425762306a36Sopenharmony_ci
425862306a36Sopenharmony_ci	}
425962306a36Sopenharmony_ci
426062306a36Sopenharmony_ci	/*
426162306a36Sopenharmony_ci	 * Wait on commands that were cleaned up via the aborted_task path.
426262306a36Sopenharmony_ci	 * LLDs that implement iscsit_wait_conn will already have waited for
426362306a36Sopenharmony_ci	 * commands.
426462306a36Sopenharmony_ci	 */
426562306a36Sopenharmony_ci	if (!conn->conn_transport->iscsit_wait_conn) {
426662306a36Sopenharmony_ci		target_stop_cmd_counter(conn->cmd_cnt);
426762306a36Sopenharmony_ci		target_wait_for_cmds(conn->cmd_cnt);
426862306a36Sopenharmony_ci	}
426962306a36Sopenharmony_ci}
427062306a36Sopenharmony_ci
427162306a36Sopenharmony_cistatic void iscsit_stop_timers_for_cmds(
427262306a36Sopenharmony_ci	struct iscsit_conn *conn)
427362306a36Sopenharmony_ci{
427462306a36Sopenharmony_ci	struct iscsit_cmd *cmd;
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci	spin_lock_bh(&conn->cmd_lock);
427762306a36Sopenharmony_ci	list_for_each_entry(cmd, &conn->conn_cmd_list, i_conn_node) {
427862306a36Sopenharmony_ci		if (cmd->data_direction == DMA_TO_DEVICE)
427962306a36Sopenharmony_ci			iscsit_stop_dataout_timer(cmd);
428062306a36Sopenharmony_ci	}
428162306a36Sopenharmony_ci	spin_unlock_bh(&conn->cmd_lock);
428262306a36Sopenharmony_ci}
428362306a36Sopenharmony_ci
428462306a36Sopenharmony_ciint iscsit_close_connection(
428562306a36Sopenharmony_ci	struct iscsit_conn *conn)
428662306a36Sopenharmony_ci{
428762306a36Sopenharmony_ci	int conn_logout = (conn->conn_state == TARG_CONN_STATE_IN_LOGOUT);
428862306a36Sopenharmony_ci	struct iscsit_session	*sess = conn->sess;
428962306a36Sopenharmony_ci
429062306a36Sopenharmony_ci	pr_debug("Closing iSCSI connection CID %hu on SID:"
429162306a36Sopenharmony_ci		" %u\n", conn->cid, sess->sid);
429262306a36Sopenharmony_ci	/*
429362306a36Sopenharmony_ci	 * Always up conn_logout_comp for the traditional TCP and HW_OFFLOAD
429462306a36Sopenharmony_ci	 * case just in case the RX Thread in iscsi_target_rx_opcode() is
429562306a36Sopenharmony_ci	 * sleeping and the logout response never got sent because the
429662306a36Sopenharmony_ci	 * connection failed.
429762306a36Sopenharmony_ci	 *
429862306a36Sopenharmony_ci	 * However for iser-target, isert_wait4logout() is using conn_logout_comp
429962306a36Sopenharmony_ci	 * to signal logout response TX interrupt completion.  Go ahead and skip
430062306a36Sopenharmony_ci	 * this for iser since isert_rx_opcode() does not wait on logout failure,
430162306a36Sopenharmony_ci	 * and to avoid iscsit_conn pointer dereference in iser-target code.
430262306a36Sopenharmony_ci	 */
430362306a36Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown)
430462306a36Sopenharmony_ci		complete(&conn->conn_logout_comp);
430562306a36Sopenharmony_ci
430662306a36Sopenharmony_ci	if (!strcmp(current->comm, ISCSI_RX_THREAD_NAME)) {
430762306a36Sopenharmony_ci		if (conn->tx_thread &&
430862306a36Sopenharmony_ci		    cmpxchg(&conn->tx_thread_active, true, false)) {
430962306a36Sopenharmony_ci			send_sig(SIGINT, conn->tx_thread, 1);
431062306a36Sopenharmony_ci			kthread_stop(conn->tx_thread);
431162306a36Sopenharmony_ci		}
431262306a36Sopenharmony_ci	} else if (!strcmp(current->comm, ISCSI_TX_THREAD_NAME)) {
431362306a36Sopenharmony_ci		if (conn->rx_thread &&
431462306a36Sopenharmony_ci		    cmpxchg(&conn->rx_thread_active, true, false)) {
431562306a36Sopenharmony_ci			send_sig(SIGINT, conn->rx_thread, 1);
431662306a36Sopenharmony_ci			kthread_stop(conn->rx_thread);
431762306a36Sopenharmony_ci		}
431862306a36Sopenharmony_ci	}
431962306a36Sopenharmony_ci
432062306a36Sopenharmony_ci	spin_lock(&iscsit_global->ts_bitmap_lock);
432162306a36Sopenharmony_ci	bitmap_release_region(iscsit_global->ts_bitmap, conn->bitmap_id,
432262306a36Sopenharmony_ci			      get_order(1));
432362306a36Sopenharmony_ci	spin_unlock(&iscsit_global->ts_bitmap_lock);
432462306a36Sopenharmony_ci
432562306a36Sopenharmony_ci	iscsit_stop_timers_for_cmds(conn);
432662306a36Sopenharmony_ci	iscsit_stop_nopin_response_timer(conn);
432762306a36Sopenharmony_ci	iscsit_stop_nopin_timer(conn);
432862306a36Sopenharmony_ci
432962306a36Sopenharmony_ci	if (conn->conn_transport->iscsit_wait_conn)
433062306a36Sopenharmony_ci		conn->conn_transport->iscsit_wait_conn(conn);
433162306a36Sopenharmony_ci
433262306a36Sopenharmony_ci	/*
433362306a36Sopenharmony_ci	 * During Connection recovery drop unacknowledged out of order
433462306a36Sopenharmony_ci	 * commands for this connection, and prepare the other commands
433562306a36Sopenharmony_ci	 * for reallegiance.
433662306a36Sopenharmony_ci	 *
433762306a36Sopenharmony_ci	 * During normal operation clear the out of order commands (but
433862306a36Sopenharmony_ci	 * do not free the struct iscsi_ooo_cmdsn's) and release all
433962306a36Sopenharmony_ci	 * struct iscsit_cmds.
434062306a36Sopenharmony_ci	 */
434162306a36Sopenharmony_ci	if (atomic_read(&conn->connection_recovery)) {
434262306a36Sopenharmony_ci		iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(conn);
434362306a36Sopenharmony_ci		iscsit_prepare_cmds_for_reallegiance(conn);
434462306a36Sopenharmony_ci	} else {
434562306a36Sopenharmony_ci		iscsit_clear_ooo_cmdsns_for_conn(conn);
434662306a36Sopenharmony_ci		iscsit_release_commands_from_conn(conn);
434762306a36Sopenharmony_ci	}
434862306a36Sopenharmony_ci	iscsit_free_queue_reqs_for_conn(conn);
434962306a36Sopenharmony_ci
435062306a36Sopenharmony_ci	/*
435162306a36Sopenharmony_ci	 * Handle decrementing session or connection usage count if
435262306a36Sopenharmony_ci	 * a logout response was not able to be sent because the
435362306a36Sopenharmony_ci	 * connection failed.  Fall back to Session Recovery here.
435462306a36Sopenharmony_ci	 */
435562306a36Sopenharmony_ci	if (atomic_read(&conn->conn_logout_remove)) {
435662306a36Sopenharmony_ci		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_SESSION) {
435762306a36Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
435862306a36Sopenharmony_ci			iscsit_dec_session_usage_count(sess);
435962306a36Sopenharmony_ci		}
436062306a36Sopenharmony_ci		if (conn->conn_logout_reason == ISCSI_LOGOUT_REASON_CLOSE_CONNECTION)
436162306a36Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
436262306a36Sopenharmony_ci
436362306a36Sopenharmony_ci		atomic_set(&conn->conn_logout_remove, 0);
436462306a36Sopenharmony_ci		atomic_set(&sess->session_reinstatement, 0);
436562306a36Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
436662306a36Sopenharmony_ci	}
436762306a36Sopenharmony_ci
436862306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
436962306a36Sopenharmony_ci	list_del(&conn->conn_list);
437062306a36Sopenharmony_ci
437162306a36Sopenharmony_ci	/*
437262306a36Sopenharmony_ci	 * Attempt to let the Initiator know this connection failed by
437362306a36Sopenharmony_ci	 * sending an Connection Dropped Async Message on another
437462306a36Sopenharmony_ci	 * active connection.
437562306a36Sopenharmony_ci	 */
437662306a36Sopenharmony_ci	if (atomic_read(&conn->connection_recovery))
437762306a36Sopenharmony_ci		iscsit_build_conn_drop_async_message(conn);
437862306a36Sopenharmony_ci
437962306a36Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
438062306a36Sopenharmony_ci
438162306a36Sopenharmony_ci	/*
438262306a36Sopenharmony_ci	 * If connection reinstatement is being performed on this connection,
438362306a36Sopenharmony_ci	 * up the connection reinstatement semaphore that is being blocked on
438462306a36Sopenharmony_ci	 * in iscsit_cause_connection_reinstatement().
438562306a36Sopenharmony_ci	 */
438662306a36Sopenharmony_ci	spin_lock_bh(&conn->state_lock);
438762306a36Sopenharmony_ci	if (atomic_read(&conn->sleep_on_conn_wait_comp)) {
438862306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
438962306a36Sopenharmony_ci		complete(&conn->conn_wait_comp);
439062306a36Sopenharmony_ci		wait_for_completion(&conn->conn_post_wait_comp);
439162306a36Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
439262306a36Sopenharmony_ci	}
439362306a36Sopenharmony_ci
439462306a36Sopenharmony_ci	/*
439562306a36Sopenharmony_ci	 * If connection reinstatement is being performed on this connection
439662306a36Sopenharmony_ci	 * by receiving a REMOVECONNFORRECOVERY logout request, up the
439762306a36Sopenharmony_ci	 * connection wait rcfr semaphore that is being blocked on
439862306a36Sopenharmony_ci	 * an iscsit_connection_reinstatement_rcfr().
439962306a36Sopenharmony_ci	 */
440062306a36Sopenharmony_ci	if (atomic_read(&conn->connection_wait_rcfr)) {
440162306a36Sopenharmony_ci		spin_unlock_bh(&conn->state_lock);
440262306a36Sopenharmony_ci		complete(&conn->conn_wait_rcfr_comp);
440362306a36Sopenharmony_ci		wait_for_completion(&conn->conn_post_wait_comp);
440462306a36Sopenharmony_ci		spin_lock_bh(&conn->state_lock);
440562306a36Sopenharmony_ci	}
440662306a36Sopenharmony_ci	atomic_set(&conn->connection_reinstatement, 1);
440762306a36Sopenharmony_ci	spin_unlock_bh(&conn->state_lock);
440862306a36Sopenharmony_ci
440962306a36Sopenharmony_ci	/*
441062306a36Sopenharmony_ci	 * If any other processes are accessing this connection pointer we
441162306a36Sopenharmony_ci	 * must wait until they have completed.
441262306a36Sopenharmony_ci	 */
441362306a36Sopenharmony_ci	iscsit_check_conn_usage_count(conn);
441462306a36Sopenharmony_ci
441562306a36Sopenharmony_ci	ahash_request_free(conn->conn_tx_hash);
441662306a36Sopenharmony_ci	if (conn->conn_rx_hash) {
441762306a36Sopenharmony_ci		struct crypto_ahash *tfm;
441862306a36Sopenharmony_ci
441962306a36Sopenharmony_ci		tfm = crypto_ahash_reqtfm(conn->conn_rx_hash);
442062306a36Sopenharmony_ci		ahash_request_free(conn->conn_rx_hash);
442162306a36Sopenharmony_ci		crypto_free_ahash(tfm);
442262306a36Sopenharmony_ci	}
442362306a36Sopenharmony_ci
442462306a36Sopenharmony_ci	if (conn->sock)
442562306a36Sopenharmony_ci		sock_release(conn->sock);
442662306a36Sopenharmony_ci
442762306a36Sopenharmony_ci	if (conn->conn_transport->iscsit_free_conn)
442862306a36Sopenharmony_ci		conn->conn_transport->iscsit_free_conn(conn);
442962306a36Sopenharmony_ci
443062306a36Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_FREE.\n");
443162306a36Sopenharmony_ci	conn->conn_state = TARG_CONN_STATE_FREE;
443262306a36Sopenharmony_ci	iscsit_free_conn(conn);
443362306a36Sopenharmony_ci
443462306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
443562306a36Sopenharmony_ci	atomic_dec(&sess->nconn);
443662306a36Sopenharmony_ci	pr_debug("Decremented iSCSI connection count to %d from node:"
443762306a36Sopenharmony_ci		" %s\n", atomic_read(&sess->nconn),
443862306a36Sopenharmony_ci		sess->sess_ops->InitiatorName);
443962306a36Sopenharmony_ci	/*
444062306a36Sopenharmony_ci	 * Make sure that if one connection fails in an non ERL=2 iSCSI
444162306a36Sopenharmony_ci	 * Session that they all fail.
444262306a36Sopenharmony_ci	 */
444362306a36Sopenharmony_ci	if ((sess->sess_ops->ErrorRecoveryLevel != 2) && !conn_logout &&
444462306a36Sopenharmony_ci	     !atomic_read(&sess->session_logout))
444562306a36Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
444662306a36Sopenharmony_ci
444762306a36Sopenharmony_ci	/*
444862306a36Sopenharmony_ci	 * If this was not the last connection in the session, and we are
444962306a36Sopenharmony_ci	 * performing session reinstatement or falling back to ERL=0, call
445062306a36Sopenharmony_ci	 * iscsit_stop_session() without sleeping to shutdown the other
445162306a36Sopenharmony_ci	 * active connections.
445262306a36Sopenharmony_ci	 */
445362306a36Sopenharmony_ci	if (atomic_read(&sess->nconn)) {
445462306a36Sopenharmony_ci		if (!atomic_read(&sess->session_reinstatement) &&
445562306a36Sopenharmony_ci		    !atomic_read(&sess->session_fall_back_to_erl0)) {
445662306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
445762306a36Sopenharmony_ci			return 0;
445862306a36Sopenharmony_ci		}
445962306a36Sopenharmony_ci		if (!atomic_read(&sess->session_stop_active)) {
446062306a36Sopenharmony_ci			atomic_set(&sess->session_stop_active, 1);
446162306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
446262306a36Sopenharmony_ci			iscsit_stop_session(sess, 0, 0);
446362306a36Sopenharmony_ci			return 0;
446462306a36Sopenharmony_ci		}
446562306a36Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
446662306a36Sopenharmony_ci		return 0;
446762306a36Sopenharmony_ci	}
446862306a36Sopenharmony_ci
446962306a36Sopenharmony_ci	/*
447062306a36Sopenharmony_ci	 * If this was the last connection in the session and one of the
447162306a36Sopenharmony_ci	 * following is occurring:
447262306a36Sopenharmony_ci	 *
447362306a36Sopenharmony_ci	 * Session Reinstatement is not being performed, and are falling back
447462306a36Sopenharmony_ci	 * to ERL=0 call iscsit_close_session().
447562306a36Sopenharmony_ci	 *
447662306a36Sopenharmony_ci	 * Session Logout was requested.  iscsit_close_session() will be called
447762306a36Sopenharmony_ci	 * elsewhere.
447862306a36Sopenharmony_ci	 *
447962306a36Sopenharmony_ci	 * Session Continuation is not being performed, start the Time2Retain
448062306a36Sopenharmony_ci	 * handler and check if sleep_on_sess_wait_sem is active.
448162306a36Sopenharmony_ci	 */
448262306a36Sopenharmony_ci	if (!atomic_read(&sess->session_reinstatement) &&
448362306a36Sopenharmony_ci	     atomic_read(&sess->session_fall_back_to_erl0)) {
448462306a36Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
448562306a36Sopenharmony_ci		complete_all(&sess->session_wait_comp);
448662306a36Sopenharmony_ci		iscsit_close_session(sess, true);
448762306a36Sopenharmony_ci
448862306a36Sopenharmony_ci		return 0;
448962306a36Sopenharmony_ci	} else if (atomic_read(&sess->session_logout)) {
449062306a36Sopenharmony_ci		pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
449162306a36Sopenharmony_ci		sess->session_state = TARG_SESS_STATE_FREE;
449262306a36Sopenharmony_ci
449362306a36Sopenharmony_ci		if (atomic_read(&sess->session_close)) {
449462306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
449562306a36Sopenharmony_ci			complete_all(&sess->session_wait_comp);
449662306a36Sopenharmony_ci			iscsit_close_session(sess, true);
449762306a36Sopenharmony_ci		} else {
449862306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
449962306a36Sopenharmony_ci		}
450062306a36Sopenharmony_ci
450162306a36Sopenharmony_ci		return 0;
450262306a36Sopenharmony_ci	} else {
450362306a36Sopenharmony_ci		pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
450462306a36Sopenharmony_ci		sess->session_state = TARG_SESS_STATE_FAILED;
450562306a36Sopenharmony_ci
450662306a36Sopenharmony_ci		if (!atomic_read(&sess->session_continuation))
450762306a36Sopenharmony_ci			iscsit_start_time2retain_handler(sess);
450862306a36Sopenharmony_ci
450962306a36Sopenharmony_ci		if (atomic_read(&sess->session_close)) {
451062306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
451162306a36Sopenharmony_ci			complete_all(&sess->session_wait_comp);
451262306a36Sopenharmony_ci			iscsit_close_session(sess, true);
451362306a36Sopenharmony_ci		} else {
451462306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
451562306a36Sopenharmony_ci		}
451662306a36Sopenharmony_ci
451762306a36Sopenharmony_ci		return 0;
451862306a36Sopenharmony_ci	}
451962306a36Sopenharmony_ci}
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci/*
452262306a36Sopenharmony_ci * If the iSCSI Session for the iSCSI Initiator Node exists,
452362306a36Sopenharmony_ci * forcefully shutdown the iSCSI NEXUS.
452462306a36Sopenharmony_ci */
452562306a36Sopenharmony_ciint iscsit_close_session(struct iscsit_session *sess, bool can_sleep)
452662306a36Sopenharmony_ci{
452762306a36Sopenharmony_ci	struct iscsi_portal_group *tpg = sess->tpg;
452862306a36Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
452962306a36Sopenharmony_ci
453062306a36Sopenharmony_ci	if (atomic_read(&sess->nconn)) {
453162306a36Sopenharmony_ci		pr_err("%d connection(s) still exist for iSCSI session"
453262306a36Sopenharmony_ci			" to %s\n", atomic_read(&sess->nconn),
453362306a36Sopenharmony_ci			sess->sess_ops->InitiatorName);
453462306a36Sopenharmony_ci		BUG();
453562306a36Sopenharmony_ci	}
453662306a36Sopenharmony_ci
453762306a36Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
453862306a36Sopenharmony_ci	atomic_set(&sess->session_logout, 1);
453962306a36Sopenharmony_ci	atomic_set(&sess->session_reinstatement, 1);
454062306a36Sopenharmony_ci	iscsit_stop_time2retain_timer(sess);
454162306a36Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_ci	if (sess->sess_ops->ErrorRecoveryLevel == 2)
454462306a36Sopenharmony_ci		iscsit_free_connection_recovery_entries(sess);
454562306a36Sopenharmony_ci
454662306a36Sopenharmony_ci	/*
454762306a36Sopenharmony_ci	 * transport_deregister_session_configfs() will clear the
454862306a36Sopenharmony_ci	 * struct se_node_acl->nacl_sess pointer now as a iscsi_np process context
454962306a36Sopenharmony_ci	 * can be setting it again with __transport_register_session() in
455062306a36Sopenharmony_ci	 * iscsi_post_login_handler() again after the iscsit_stop_session()
455162306a36Sopenharmony_ci	 * completes in iscsi_np context.
455262306a36Sopenharmony_ci	 */
455362306a36Sopenharmony_ci	transport_deregister_session_configfs(sess->se_sess);
455462306a36Sopenharmony_ci
455562306a36Sopenharmony_ci	/*
455662306a36Sopenharmony_ci	 * If any other processes are accessing this session pointer we must
455762306a36Sopenharmony_ci	 * wait until they have completed.  If we are in an interrupt (the
455862306a36Sopenharmony_ci	 * time2retain handler) and contain and active session usage count we
455962306a36Sopenharmony_ci	 * restart the timer and exit.
456062306a36Sopenharmony_ci	 */
456162306a36Sopenharmony_ci	if (iscsit_check_session_usage_count(sess, can_sleep)) {
456262306a36Sopenharmony_ci		atomic_set(&sess->session_logout, 0);
456362306a36Sopenharmony_ci		iscsit_start_time2retain_handler(sess);
456462306a36Sopenharmony_ci		return 0;
456562306a36Sopenharmony_ci	}
456662306a36Sopenharmony_ci
456762306a36Sopenharmony_ci	transport_deregister_session(sess->se_sess);
456862306a36Sopenharmony_ci
456962306a36Sopenharmony_ci	iscsit_free_all_ooo_cmdsns(sess);
457062306a36Sopenharmony_ci
457162306a36Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
457262306a36Sopenharmony_ci	pr_debug("Moving to TARG_SESS_STATE_FREE.\n");
457362306a36Sopenharmony_ci	sess->session_state = TARG_SESS_STATE_FREE;
457462306a36Sopenharmony_ci	pr_debug("Released iSCSI session from node: %s\n",
457562306a36Sopenharmony_ci			sess->sess_ops->InitiatorName);
457662306a36Sopenharmony_ci	tpg->nsessions--;
457762306a36Sopenharmony_ci	if (tpg->tpg_tiqn)
457862306a36Sopenharmony_ci		tpg->tpg_tiqn->tiqn_nsessions--;
457962306a36Sopenharmony_ci
458062306a36Sopenharmony_ci	pr_debug("Decremented number of active iSCSI Sessions on"
458162306a36Sopenharmony_ci		" iSCSI TPG: %hu to %u\n", tpg->tpgt, tpg->nsessions);
458262306a36Sopenharmony_ci
458362306a36Sopenharmony_ci	ida_free(&sess_ida, sess->session_index);
458462306a36Sopenharmony_ci	kfree(sess->sess_ops);
458562306a36Sopenharmony_ci	sess->sess_ops = NULL;
458662306a36Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
458762306a36Sopenharmony_ci
458862306a36Sopenharmony_ci	kfree(sess);
458962306a36Sopenharmony_ci	return 0;
459062306a36Sopenharmony_ci}
459162306a36Sopenharmony_ci
459262306a36Sopenharmony_cistatic void iscsit_logout_post_handler_closesession(
459362306a36Sopenharmony_ci	struct iscsit_conn *conn)
459462306a36Sopenharmony_ci{
459562306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
459662306a36Sopenharmony_ci	int sleep = 1;
459762306a36Sopenharmony_ci	/*
459862306a36Sopenharmony_ci	 * Traditional iscsi/tcp will invoke this logic from TX thread
459962306a36Sopenharmony_ci	 * context during session logout, so clear tx_thread_active and
460062306a36Sopenharmony_ci	 * sleep if iscsit_close_connection() has not already occured.
460162306a36Sopenharmony_ci	 *
460262306a36Sopenharmony_ci	 * Since iser-target invokes this logic from it's own workqueue,
460362306a36Sopenharmony_ci	 * always sleep waiting for RX/TX thread shutdown to complete
460462306a36Sopenharmony_ci	 * within iscsit_close_connection().
460562306a36Sopenharmony_ci	 */
460662306a36Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown) {
460762306a36Sopenharmony_ci		sleep = cmpxchg(&conn->tx_thread_active, true, false);
460862306a36Sopenharmony_ci		if (!sleep)
460962306a36Sopenharmony_ci			return;
461062306a36Sopenharmony_ci	}
461162306a36Sopenharmony_ci
461262306a36Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 0);
461362306a36Sopenharmony_ci	complete(&conn->conn_logout_comp);
461462306a36Sopenharmony_ci
461562306a36Sopenharmony_ci	iscsit_dec_conn_usage_count(conn);
461662306a36Sopenharmony_ci	atomic_set(&sess->session_close, 1);
461762306a36Sopenharmony_ci	iscsit_stop_session(sess, sleep, sleep);
461862306a36Sopenharmony_ci	iscsit_dec_session_usage_count(sess);
461962306a36Sopenharmony_ci}
462062306a36Sopenharmony_ci
462162306a36Sopenharmony_cistatic void iscsit_logout_post_handler_samecid(
462262306a36Sopenharmony_ci	struct iscsit_conn *conn)
462362306a36Sopenharmony_ci{
462462306a36Sopenharmony_ci	int sleep = 1;
462562306a36Sopenharmony_ci
462662306a36Sopenharmony_ci	if (!conn->conn_transport->rdma_shutdown) {
462762306a36Sopenharmony_ci		sleep = cmpxchg(&conn->tx_thread_active, true, false);
462862306a36Sopenharmony_ci		if (!sleep)
462962306a36Sopenharmony_ci			return;
463062306a36Sopenharmony_ci	}
463162306a36Sopenharmony_ci
463262306a36Sopenharmony_ci	atomic_set(&conn->conn_logout_remove, 0);
463362306a36Sopenharmony_ci	complete(&conn->conn_logout_comp);
463462306a36Sopenharmony_ci
463562306a36Sopenharmony_ci	iscsit_cause_connection_reinstatement(conn, sleep);
463662306a36Sopenharmony_ci	iscsit_dec_conn_usage_count(conn);
463762306a36Sopenharmony_ci}
463862306a36Sopenharmony_ci
463962306a36Sopenharmony_cistatic void iscsit_logout_post_handler_diffcid(
464062306a36Sopenharmony_ci	struct iscsit_conn *conn,
464162306a36Sopenharmony_ci	u16 cid)
464262306a36Sopenharmony_ci{
464362306a36Sopenharmony_ci	struct iscsit_conn *l_conn;
464462306a36Sopenharmony_ci	struct iscsit_session *sess = conn->sess;
464562306a36Sopenharmony_ci	bool conn_found = false;
464662306a36Sopenharmony_ci
464762306a36Sopenharmony_ci	if (!sess)
464862306a36Sopenharmony_ci		return;
464962306a36Sopenharmony_ci
465062306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
465162306a36Sopenharmony_ci	list_for_each_entry(l_conn, &sess->sess_conn_list, conn_list) {
465262306a36Sopenharmony_ci		if (l_conn->cid == cid) {
465362306a36Sopenharmony_ci			iscsit_inc_conn_usage_count(l_conn);
465462306a36Sopenharmony_ci			conn_found = true;
465562306a36Sopenharmony_ci			break;
465662306a36Sopenharmony_ci		}
465762306a36Sopenharmony_ci	}
465862306a36Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
465962306a36Sopenharmony_ci
466062306a36Sopenharmony_ci	if (!conn_found)
466162306a36Sopenharmony_ci		return;
466262306a36Sopenharmony_ci
466362306a36Sopenharmony_ci	if (l_conn->sock)
466462306a36Sopenharmony_ci		l_conn->sock->ops->shutdown(l_conn->sock, RCV_SHUTDOWN);
466562306a36Sopenharmony_ci
466662306a36Sopenharmony_ci	spin_lock_bh(&l_conn->state_lock);
466762306a36Sopenharmony_ci	pr_debug("Moving to TARG_CONN_STATE_IN_LOGOUT.\n");
466862306a36Sopenharmony_ci	l_conn->conn_state = TARG_CONN_STATE_IN_LOGOUT;
466962306a36Sopenharmony_ci	spin_unlock_bh(&l_conn->state_lock);
467062306a36Sopenharmony_ci
467162306a36Sopenharmony_ci	iscsit_cause_connection_reinstatement(l_conn, 1);
467262306a36Sopenharmony_ci	iscsit_dec_conn_usage_count(l_conn);
467362306a36Sopenharmony_ci}
467462306a36Sopenharmony_ci
467562306a36Sopenharmony_ci/*
467662306a36Sopenharmony_ci *	Return of 0 causes the TX thread to restart.
467762306a36Sopenharmony_ci */
467862306a36Sopenharmony_ciint iscsit_logout_post_handler(
467962306a36Sopenharmony_ci	struct iscsit_cmd *cmd,
468062306a36Sopenharmony_ci	struct iscsit_conn *conn)
468162306a36Sopenharmony_ci{
468262306a36Sopenharmony_ci	int ret = 0;
468362306a36Sopenharmony_ci
468462306a36Sopenharmony_ci	switch (cmd->logout_reason) {
468562306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_SESSION:
468662306a36Sopenharmony_ci		switch (cmd->logout_response) {
468762306a36Sopenharmony_ci		case ISCSI_LOGOUT_SUCCESS:
468862306a36Sopenharmony_ci		case ISCSI_LOGOUT_CLEANUP_FAILED:
468962306a36Sopenharmony_ci		default:
469062306a36Sopenharmony_ci			iscsit_logout_post_handler_closesession(conn);
469162306a36Sopenharmony_ci			break;
469262306a36Sopenharmony_ci		}
469362306a36Sopenharmony_ci		break;
469462306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_CLOSE_CONNECTION:
469562306a36Sopenharmony_ci		if (conn->cid == cmd->logout_cid) {
469662306a36Sopenharmony_ci			switch (cmd->logout_response) {
469762306a36Sopenharmony_ci			case ISCSI_LOGOUT_SUCCESS:
469862306a36Sopenharmony_ci			case ISCSI_LOGOUT_CLEANUP_FAILED:
469962306a36Sopenharmony_ci			default:
470062306a36Sopenharmony_ci				iscsit_logout_post_handler_samecid(conn);
470162306a36Sopenharmony_ci				break;
470262306a36Sopenharmony_ci			}
470362306a36Sopenharmony_ci		} else {
470462306a36Sopenharmony_ci			switch (cmd->logout_response) {
470562306a36Sopenharmony_ci			case ISCSI_LOGOUT_SUCCESS:
470662306a36Sopenharmony_ci				iscsit_logout_post_handler_diffcid(conn,
470762306a36Sopenharmony_ci					cmd->logout_cid);
470862306a36Sopenharmony_ci				break;
470962306a36Sopenharmony_ci			case ISCSI_LOGOUT_CID_NOT_FOUND:
471062306a36Sopenharmony_ci			case ISCSI_LOGOUT_CLEANUP_FAILED:
471162306a36Sopenharmony_ci			default:
471262306a36Sopenharmony_ci				break;
471362306a36Sopenharmony_ci			}
471462306a36Sopenharmony_ci			ret = 1;
471562306a36Sopenharmony_ci		}
471662306a36Sopenharmony_ci		break;
471762306a36Sopenharmony_ci	case ISCSI_LOGOUT_REASON_RECOVERY:
471862306a36Sopenharmony_ci		switch (cmd->logout_response) {
471962306a36Sopenharmony_ci		case ISCSI_LOGOUT_SUCCESS:
472062306a36Sopenharmony_ci		case ISCSI_LOGOUT_CID_NOT_FOUND:
472162306a36Sopenharmony_ci		case ISCSI_LOGOUT_RECOVERY_UNSUPPORTED:
472262306a36Sopenharmony_ci		case ISCSI_LOGOUT_CLEANUP_FAILED:
472362306a36Sopenharmony_ci		default:
472462306a36Sopenharmony_ci			break;
472562306a36Sopenharmony_ci		}
472662306a36Sopenharmony_ci		ret = 1;
472762306a36Sopenharmony_ci		break;
472862306a36Sopenharmony_ci	default:
472962306a36Sopenharmony_ci		break;
473062306a36Sopenharmony_ci
473162306a36Sopenharmony_ci	}
473262306a36Sopenharmony_ci	return ret;
473362306a36Sopenharmony_ci}
473462306a36Sopenharmony_ciEXPORT_SYMBOL(iscsit_logout_post_handler);
473562306a36Sopenharmony_ci
473662306a36Sopenharmony_civoid iscsit_fail_session(struct iscsit_session *sess)
473762306a36Sopenharmony_ci{
473862306a36Sopenharmony_ci	struct iscsit_conn *conn;
473962306a36Sopenharmony_ci
474062306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
474162306a36Sopenharmony_ci	list_for_each_entry(conn, &sess->sess_conn_list, conn_list) {
474262306a36Sopenharmony_ci		pr_debug("Moving to TARG_CONN_STATE_CLEANUP_WAIT.\n");
474362306a36Sopenharmony_ci		conn->conn_state = TARG_CONN_STATE_CLEANUP_WAIT;
474462306a36Sopenharmony_ci	}
474562306a36Sopenharmony_ci	spin_unlock_bh(&sess->conn_lock);
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci	pr_debug("Moving to TARG_SESS_STATE_FAILED.\n");
474862306a36Sopenharmony_ci	sess->session_state = TARG_SESS_STATE_FAILED;
474962306a36Sopenharmony_ci}
475062306a36Sopenharmony_ci
475162306a36Sopenharmony_civoid iscsit_stop_session(
475262306a36Sopenharmony_ci	struct iscsit_session *sess,
475362306a36Sopenharmony_ci	int session_sleep,
475462306a36Sopenharmony_ci	int connection_sleep)
475562306a36Sopenharmony_ci{
475662306a36Sopenharmony_ci	u16 conn_count = atomic_read(&sess->nconn);
475762306a36Sopenharmony_ci	struct iscsit_conn *conn, *conn_tmp = NULL;
475862306a36Sopenharmony_ci	int is_last;
475962306a36Sopenharmony_ci
476062306a36Sopenharmony_ci	spin_lock_bh(&sess->conn_lock);
476162306a36Sopenharmony_ci
476262306a36Sopenharmony_ci	if (connection_sleep) {
476362306a36Sopenharmony_ci		list_for_each_entry_safe(conn, conn_tmp, &sess->sess_conn_list,
476462306a36Sopenharmony_ci				conn_list) {
476562306a36Sopenharmony_ci			if (conn_count == 0)
476662306a36Sopenharmony_ci				break;
476762306a36Sopenharmony_ci
476862306a36Sopenharmony_ci			if (list_is_last(&conn->conn_list, &sess->sess_conn_list)) {
476962306a36Sopenharmony_ci				is_last = 1;
477062306a36Sopenharmony_ci			} else {
477162306a36Sopenharmony_ci				iscsit_inc_conn_usage_count(conn_tmp);
477262306a36Sopenharmony_ci				is_last = 0;
477362306a36Sopenharmony_ci			}
477462306a36Sopenharmony_ci			iscsit_inc_conn_usage_count(conn);
477562306a36Sopenharmony_ci
477662306a36Sopenharmony_ci			spin_unlock_bh(&sess->conn_lock);
477762306a36Sopenharmony_ci			iscsit_cause_connection_reinstatement(conn, 1);
477862306a36Sopenharmony_ci			spin_lock_bh(&sess->conn_lock);
477962306a36Sopenharmony_ci
478062306a36Sopenharmony_ci			iscsit_dec_conn_usage_count(conn);
478162306a36Sopenharmony_ci			if (is_last == 0)
478262306a36Sopenharmony_ci				iscsit_dec_conn_usage_count(conn_tmp);
478362306a36Sopenharmony_ci			conn_count--;
478462306a36Sopenharmony_ci		}
478562306a36Sopenharmony_ci	} else {
478662306a36Sopenharmony_ci		list_for_each_entry(conn, &sess->sess_conn_list, conn_list)
478762306a36Sopenharmony_ci			iscsit_cause_connection_reinstatement(conn, 0);
478862306a36Sopenharmony_ci	}
478962306a36Sopenharmony_ci
479062306a36Sopenharmony_ci	if (session_sleep && atomic_read(&sess->nconn)) {
479162306a36Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
479262306a36Sopenharmony_ci		wait_for_completion(&sess->session_wait_comp);
479362306a36Sopenharmony_ci	} else
479462306a36Sopenharmony_ci		spin_unlock_bh(&sess->conn_lock);
479562306a36Sopenharmony_ci}
479662306a36Sopenharmony_ci
479762306a36Sopenharmony_ciint iscsit_release_sessions_for_tpg(struct iscsi_portal_group *tpg, int force)
479862306a36Sopenharmony_ci{
479962306a36Sopenharmony_ci	struct iscsit_session *sess;
480062306a36Sopenharmony_ci	struct se_portal_group *se_tpg = &tpg->tpg_se_tpg;
480162306a36Sopenharmony_ci	struct se_session *se_sess, *se_sess_tmp;
480262306a36Sopenharmony_ci	LIST_HEAD(free_list);
480362306a36Sopenharmony_ci	int session_count = 0;
480462306a36Sopenharmony_ci
480562306a36Sopenharmony_ci	spin_lock_bh(&se_tpg->session_lock);
480662306a36Sopenharmony_ci	if (tpg->nsessions && !force) {
480762306a36Sopenharmony_ci		spin_unlock_bh(&se_tpg->session_lock);
480862306a36Sopenharmony_ci		return -1;
480962306a36Sopenharmony_ci	}
481062306a36Sopenharmony_ci
481162306a36Sopenharmony_ci	list_for_each_entry_safe(se_sess, se_sess_tmp, &se_tpg->tpg_sess_list,
481262306a36Sopenharmony_ci			sess_list) {
481362306a36Sopenharmony_ci		sess = (struct iscsit_session *)se_sess->fabric_sess_ptr;
481462306a36Sopenharmony_ci
481562306a36Sopenharmony_ci		spin_lock(&sess->conn_lock);
481662306a36Sopenharmony_ci		if (atomic_read(&sess->session_fall_back_to_erl0) ||
481762306a36Sopenharmony_ci		    atomic_read(&sess->session_logout) ||
481862306a36Sopenharmony_ci		    atomic_read(&sess->session_close) ||
481962306a36Sopenharmony_ci		    (sess->time2retain_timer_flags & ISCSI_TF_EXPIRED)) {
482062306a36Sopenharmony_ci			spin_unlock(&sess->conn_lock);
482162306a36Sopenharmony_ci			continue;
482262306a36Sopenharmony_ci		}
482362306a36Sopenharmony_ci		iscsit_inc_session_usage_count(sess);
482462306a36Sopenharmony_ci		atomic_set(&sess->session_reinstatement, 1);
482562306a36Sopenharmony_ci		atomic_set(&sess->session_fall_back_to_erl0, 1);
482662306a36Sopenharmony_ci		atomic_set(&sess->session_close, 1);
482762306a36Sopenharmony_ci		spin_unlock(&sess->conn_lock);
482862306a36Sopenharmony_ci
482962306a36Sopenharmony_ci		list_move_tail(&se_sess->sess_list, &free_list);
483062306a36Sopenharmony_ci	}
483162306a36Sopenharmony_ci	spin_unlock_bh(&se_tpg->session_lock);
483262306a36Sopenharmony_ci
483362306a36Sopenharmony_ci	list_for_each_entry_safe(se_sess, se_sess_tmp, &free_list, sess_list) {
483462306a36Sopenharmony_ci		sess = (struct iscsit_session *)se_sess->fabric_sess_ptr;
483562306a36Sopenharmony_ci
483662306a36Sopenharmony_ci		list_del_init(&se_sess->sess_list);
483762306a36Sopenharmony_ci		iscsit_stop_session(sess, 1, 1);
483862306a36Sopenharmony_ci		iscsit_dec_session_usage_count(sess);
483962306a36Sopenharmony_ci		session_count++;
484062306a36Sopenharmony_ci	}
484162306a36Sopenharmony_ci
484262306a36Sopenharmony_ci	pr_debug("Released %d iSCSI Session(s) from Target Portal"
484362306a36Sopenharmony_ci			" Group: %hu\n", session_count, tpg->tpgt);
484462306a36Sopenharmony_ci	return 0;
484562306a36Sopenharmony_ci}
484662306a36Sopenharmony_ci
484762306a36Sopenharmony_ciMODULE_DESCRIPTION("iSCSI-Target Driver for mainline target infrastructure");
484862306a36Sopenharmony_ciMODULE_VERSION("4.1.x");
484962306a36Sopenharmony_ciMODULE_AUTHOR("nab@Linux-iSCSI.org");
485062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_cimodule_init(iscsi_target_init_module);
485362306a36Sopenharmony_cimodule_exit(iscsi_target_cleanup_module);
4854