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