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