18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * iSCSI lib functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 68c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2006 Mike Christie 78c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2005 Dmitry Yusupov 88c2ecf20Sopenharmony_ci * Copyright (C) 2004 - 2005 Alex Aizman 98c2ecf20Sopenharmony_ci * maintained by open-iscsi@googlegroups.com 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci#include <linux/types.h> 128c2ecf20Sopenharmony_ci#include <linux/kfifo.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/log2.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/sched/signal.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 198c2ecf20Sopenharmony_ci#include <net/tcp.h> 208c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h> 218c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h> 228c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h> 238c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h> 248c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 258c2ecf20Sopenharmony_ci#include <scsi/scsi.h> 268c2ecf20Sopenharmony_ci#include <scsi/iscsi_proto.h> 278c2ecf20Sopenharmony_ci#include <scsi/scsi_transport.h> 288c2ecf20Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h> 298c2ecf20Sopenharmony_ci#include <scsi/libiscsi.h> 308c2ecf20Sopenharmony_ci#include <trace/events/iscsi.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic int iscsi_dbg_lib_conn; 338c2ecf20Sopenharmony_cimodule_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, 348c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR); 358c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_conn, 368c2ecf20Sopenharmony_ci "Turn on debugging for connections in libiscsi module. " 378c2ecf20Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic int iscsi_dbg_lib_session; 408c2ecf20Sopenharmony_cimodule_param_named(debug_libiscsi_session, iscsi_dbg_lib_session, int, 418c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_session, 438c2ecf20Sopenharmony_ci "Turn on debugging for sessions in libiscsi module. " 448c2ecf20Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic int iscsi_dbg_lib_eh; 478c2ecf20Sopenharmony_cimodule_param_named(debug_libiscsi_eh, iscsi_dbg_lib_eh, int, 488c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR); 498c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_eh, 508c2ecf20Sopenharmony_ci "Turn on debugging for error handling in libiscsi module. " 518c2ecf20Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \ 548c2ecf20Sopenharmony_ci do { \ 558c2ecf20Sopenharmony_ci if (iscsi_dbg_lib_conn) \ 568c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_INFO, _conn, \ 578c2ecf20Sopenharmony_ci "%s " dbg_fmt, \ 588c2ecf20Sopenharmony_ci __func__, ##arg); \ 598c2ecf20Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_conn, \ 608c2ecf20Sopenharmony_ci &(_conn)->cls_conn->dev, \ 618c2ecf20Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg);\ 628c2ecf20Sopenharmony_ci } while (0); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ 658c2ecf20Sopenharmony_ci do { \ 668c2ecf20Sopenharmony_ci if (iscsi_dbg_lib_session) \ 678c2ecf20Sopenharmony_ci iscsi_session_printk(KERN_INFO, _session, \ 688c2ecf20Sopenharmony_ci "%s " dbg_fmt, \ 698c2ecf20Sopenharmony_ci __func__, ##arg); \ 708c2ecf20Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_session, \ 718c2ecf20Sopenharmony_ci &(_session)->cls_session->dev, \ 728c2ecf20Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg); \ 738c2ecf20Sopenharmony_ci } while (0); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci#define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ 768c2ecf20Sopenharmony_ci do { \ 778c2ecf20Sopenharmony_ci if (iscsi_dbg_lib_eh) \ 788c2ecf20Sopenharmony_ci iscsi_session_printk(KERN_INFO, _session, \ 798c2ecf20Sopenharmony_ci "%s " dbg_fmt, \ 808c2ecf20Sopenharmony_ci __func__, ##arg); \ 818c2ecf20Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_eh, \ 828c2ecf20Sopenharmony_ci &(_session)->cls_session->dev, \ 838c2ecf20Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg); \ 848c2ecf20Sopenharmony_ci } while (0); 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ciinline void iscsi_conn_queue_work(struct iscsi_conn *conn) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 898c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if (ihost->workq) 928c2ecf20Sopenharmony_ci queue_work(ihost->workq, &conn->xmitwork); 938c2ecf20Sopenharmony_ci} 948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_queue_work); 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic void __iscsi_update_cmdsn(struct iscsi_session *session, 978c2ecf20Sopenharmony_ci uint32_t exp_cmdsn, uint32_t max_cmdsn) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci /* 1008c2ecf20Sopenharmony_ci * standard specifies this check for when to update expected and 1018c2ecf20Sopenharmony_ci * max sequence numbers 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1)) 1048c2ecf20Sopenharmony_ci return; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (exp_cmdsn != session->exp_cmdsn && 1078c2ecf20Sopenharmony_ci !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn)) 1088c2ecf20Sopenharmony_ci session->exp_cmdsn = exp_cmdsn; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (max_cmdsn != session->max_cmdsn && 1118c2ecf20Sopenharmony_ci !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) 1128c2ecf20Sopenharmony_ci session->max_cmdsn = max_cmdsn; 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_civoid iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) 1168c2ecf20Sopenharmony_ci{ 1178c2ecf20Sopenharmony_ci __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), 1188c2ecf20Sopenharmony_ci be32_to_cpu(hdr->max_cmdsn)); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_update_cmdsn); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci/** 1238c2ecf20Sopenharmony_ci * iscsi_prep_data_out_pdu - initialize Data-Out 1248c2ecf20Sopenharmony_ci * @task: scsi command task 1258c2ecf20Sopenharmony_ci * @r2t: R2T info 1268c2ecf20Sopenharmony_ci * @hdr: iscsi data in pdu 1278c2ecf20Sopenharmony_ci * 1288c2ecf20Sopenharmony_ci * Notes: 1298c2ecf20Sopenharmony_ci * Initialize Data-Out within this R2T sequence and finds 1308c2ecf20Sopenharmony_ci * proper data_offset within this SCSI command. 1318c2ecf20Sopenharmony_ci * 1328c2ecf20Sopenharmony_ci * This function is called with connection lock taken. 1338c2ecf20Sopenharmony_ci **/ 1348c2ecf20Sopenharmony_civoid iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t, 1358c2ecf20Sopenharmony_ci struct iscsi_data *hdr) 1368c2ecf20Sopenharmony_ci{ 1378c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 1388c2ecf20Sopenharmony_ci unsigned int left = r2t->data_length - r2t->sent; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci task->hdr_len = sizeof(struct iscsi_data); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(struct iscsi_data)); 1438c2ecf20Sopenharmony_ci hdr->ttt = r2t->ttt; 1448c2ecf20Sopenharmony_ci hdr->datasn = cpu_to_be32(r2t->datasn); 1458c2ecf20Sopenharmony_ci r2t->datasn++; 1468c2ecf20Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; 1478c2ecf20Sopenharmony_ci hdr->lun = task->lun; 1488c2ecf20Sopenharmony_ci hdr->itt = task->hdr_itt; 1498c2ecf20Sopenharmony_ci hdr->exp_statsn = r2t->exp_statsn; 1508c2ecf20Sopenharmony_ci hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent); 1518c2ecf20Sopenharmony_ci if (left > conn->max_xmit_dlength) { 1528c2ecf20Sopenharmony_ci hton24(hdr->dlength, conn->max_xmit_dlength); 1538c2ecf20Sopenharmony_ci r2t->data_count = conn->max_xmit_dlength; 1548c2ecf20Sopenharmony_ci hdr->flags = 0; 1558c2ecf20Sopenharmony_ci } else { 1568c2ecf20Sopenharmony_ci hton24(hdr->dlength, left); 1578c2ecf20Sopenharmony_ci r2t->data_count = left; 1588c2ecf20Sopenharmony_ci hdr->flags = ISCSI_FLAG_CMD_FINAL; 1598c2ecf20Sopenharmony_ci } 1608c2ecf20Sopenharmony_ci conn->dataout_pdus_cnt++; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic int iscsi_add_hdr(struct iscsi_task *task, unsigned len) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci unsigned exp_len = task->hdr_len + len; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (exp_len > task->hdr_max) { 1698c2ecf20Sopenharmony_ci WARN_ON(1); 1708c2ecf20Sopenharmony_ci return -EINVAL; 1718c2ecf20Sopenharmony_ci } 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ 1748c2ecf20Sopenharmony_ci task->hdr_len = exp_len; 1758c2ecf20Sopenharmony_ci return 0; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci/* 1798c2ecf20Sopenharmony_ci * make an extended cdb AHS 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_cistatic int iscsi_prep_ecdb_ahs(struct iscsi_task *task) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci struct scsi_cmnd *cmd = task->sc; 1848c2ecf20Sopenharmony_ci unsigned rlen, pad_len; 1858c2ecf20Sopenharmony_ci unsigned short ahslength; 1868c2ecf20Sopenharmony_ci struct iscsi_ecdb_ahdr *ecdb_ahdr; 1878c2ecf20Sopenharmony_ci int rc; 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci ecdb_ahdr = iscsi_next_hdr(task); 1908c2ecf20Sopenharmony_ci rlen = cmd->cmd_len - ISCSI_CDB_SIZE; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb)); 1938c2ecf20Sopenharmony_ci ahslength = rlen + sizeof(ecdb_ahdr->reserved); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci pad_len = iscsi_padding(rlen); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) + 1988c2ecf20Sopenharmony_ci sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len); 1998c2ecf20Sopenharmony_ci if (rc) 2008c2ecf20Sopenharmony_ci return rc; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (pad_len) 2038c2ecf20Sopenharmony_ci memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ecdb_ahdr->ahslength = cpu_to_be16(ahslength); 2068c2ecf20Sopenharmony_ci ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB; 2078c2ecf20Sopenharmony_ci ecdb_ahdr->reserved = 0; 2088c2ecf20Sopenharmony_ci memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(task->conn->session, 2118c2ecf20Sopenharmony_ci "iscsi_prep_ecdb_ahs: varlen_cdb_len %d " 2128c2ecf20Sopenharmony_ci "rlen %d pad_len %d ahs_length %d iscsi_headers_size " 2138c2ecf20Sopenharmony_ci "%u\n", cmd->cmd_len, rlen, pad_len, ahslength, 2148c2ecf20Sopenharmony_ci task->hdr_len); 2158c2ecf20Sopenharmony_ci return 0; 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci/** 2198c2ecf20Sopenharmony_ci * iscsi_check_tmf_restrictions - check if a task is affected by TMF 2208c2ecf20Sopenharmony_ci * @task: iscsi task 2218c2ecf20Sopenharmony_ci * @opcode: opcode to check for 2228c2ecf20Sopenharmony_ci * 2238c2ecf20Sopenharmony_ci * During TMF a task has to be checked if it's affected. 2248c2ecf20Sopenharmony_ci * All unrelated I/O can be passed through, but I/O to the 2258c2ecf20Sopenharmony_ci * affected LUN should be restricted. 2268c2ecf20Sopenharmony_ci * If 'fast_abort' is set we won't be sending any I/O to the 2278c2ecf20Sopenharmony_ci * affected LUN. 2288c2ecf20Sopenharmony_ci * Otherwise the target is waiting for all TTTs to be completed, 2298c2ecf20Sopenharmony_ci * so we have to send all outstanding Data-Out PDUs to the target. 2308c2ecf20Sopenharmony_ci */ 2318c2ecf20Sopenharmony_cistatic int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) 2328c2ecf20Sopenharmony_ci{ 2338c2ecf20Sopenharmony_ci struct iscsi_session *session = task->conn->session; 2348c2ecf20Sopenharmony_ci struct iscsi_tm *tmf = &session->tmhdr; 2358c2ecf20Sopenharmony_ci u64 hdr_lun; 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (session->tmf_state == TMF_INITIAL) 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci switch (ISCSI_TM_FUNC_VALUE(tmf)) { 2448c2ecf20Sopenharmony_ci case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: 2458c2ecf20Sopenharmony_ci /* 2468c2ecf20Sopenharmony_ci * Allow PDUs for unrelated LUNs 2478c2ecf20Sopenharmony_ci */ 2488c2ecf20Sopenharmony_ci hdr_lun = scsilun_to_int(&tmf->lun); 2498c2ecf20Sopenharmony_ci if (hdr_lun != task->sc->device->lun) 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci fallthrough; 2528c2ecf20Sopenharmony_ci case ISCSI_TM_FUNC_TARGET_WARM_RESET: 2538c2ecf20Sopenharmony_ci /* 2548c2ecf20Sopenharmony_ci * Fail all SCSI cmd PDUs 2558c2ecf20Sopenharmony_ci */ 2568c2ecf20Sopenharmony_ci if (opcode != ISCSI_OP_SCSI_DATA_OUT) { 2578c2ecf20Sopenharmony_ci iscsi_session_printk(KERN_INFO, session, 2588c2ecf20Sopenharmony_ci "task [op %x itt 0x%x/0x%x] rejected.\n", 2598c2ecf20Sopenharmony_ci opcode, task->itt, task->hdr_itt); 2608c2ecf20Sopenharmony_ci return -EACCES; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci /* 2638c2ecf20Sopenharmony_ci * And also all data-out PDUs in response to R2T 2648c2ecf20Sopenharmony_ci * if fast_abort is set. 2658c2ecf20Sopenharmony_ci */ 2668c2ecf20Sopenharmony_ci if (session->fast_abort) { 2678c2ecf20Sopenharmony_ci iscsi_session_printk(KERN_INFO, session, 2688c2ecf20Sopenharmony_ci "task [op %x itt 0x%x/0x%x] fast abort.\n", 2698c2ecf20Sopenharmony_ci opcode, task->itt, task->hdr_itt); 2708c2ecf20Sopenharmony_ci return -EACCES; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci case ISCSI_TM_FUNC_ABORT_TASK: 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * the caller has already checked if the task 2768c2ecf20Sopenharmony_ci * they want to abort was in the pending queue so if 2778c2ecf20Sopenharmony_ci * we are here the cmd pdu has gone out already, and 2788c2ecf20Sopenharmony_ci * we will only hit this for data-outs 2798c2ecf20Sopenharmony_ci */ 2808c2ecf20Sopenharmony_ci if (opcode == ISCSI_OP_SCSI_DATA_OUT && 2818c2ecf20Sopenharmony_ci task->hdr_itt == tmf->rtt) { 2828c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, 2838c2ecf20Sopenharmony_ci "Preventing task %x/%x from sending " 2848c2ecf20Sopenharmony_ci "data-out due to abort task in " 2858c2ecf20Sopenharmony_ci "progress\n", task->itt, 2868c2ecf20Sopenharmony_ci task->hdr_itt); 2878c2ecf20Sopenharmony_ci return -EACCES; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci break; 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci return 0; 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci/** 2968c2ecf20Sopenharmony_ci * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu 2978c2ecf20Sopenharmony_ci * @task: iscsi task 2988c2ecf20Sopenharmony_ci * 2998c2ecf20Sopenharmony_ci * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set 3008c2ecf20Sopenharmony_ci * fields like dlength or final based on how much data it sends 3018c2ecf20Sopenharmony_ci */ 3028c2ecf20Sopenharmony_cistatic int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 3058c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 3068c2ecf20Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 3078c2ecf20Sopenharmony_ci struct iscsi_scsi_req *hdr; 3088c2ecf20Sopenharmony_ci unsigned hdrlength, cmd_len, transfer_length; 3098c2ecf20Sopenharmony_ci itt_t itt; 3108c2ecf20Sopenharmony_ci int rc; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); 3138c2ecf20Sopenharmony_ci if (rc) 3148c2ecf20Sopenharmony_ci return rc; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci if (conn->session->tt->alloc_pdu) { 3178c2ecf20Sopenharmony_ci rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); 3188c2ecf20Sopenharmony_ci if (rc) 3198c2ecf20Sopenharmony_ci return rc; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci hdr = (struct iscsi_scsi_req *)task->hdr; 3228c2ecf20Sopenharmony_ci itt = hdr->itt; 3238c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci if (session->tt->parse_pdu_itt) 3268c2ecf20Sopenharmony_ci hdr->itt = task->hdr_itt = itt; 3278c2ecf20Sopenharmony_ci else 3288c2ecf20Sopenharmony_ci hdr->itt = task->hdr_itt = build_itt(task->itt, 3298c2ecf20Sopenharmony_ci task->conn->session->age); 3308c2ecf20Sopenharmony_ci task->hdr_len = 0; 3318c2ecf20Sopenharmony_ci rc = iscsi_add_hdr(task, sizeof(*hdr)); 3328c2ecf20Sopenharmony_ci if (rc) 3338c2ecf20Sopenharmony_ci return rc; 3348c2ecf20Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_CMD; 3358c2ecf20Sopenharmony_ci hdr->flags = ISCSI_ATTR_SIMPLE; 3368c2ecf20Sopenharmony_ci int_to_scsilun(sc->device->lun, &hdr->lun); 3378c2ecf20Sopenharmony_ci task->lun = hdr->lun; 3388c2ecf20Sopenharmony_ci hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); 3398c2ecf20Sopenharmony_ci cmd_len = sc->cmd_len; 3408c2ecf20Sopenharmony_ci if (cmd_len < ISCSI_CDB_SIZE) 3418c2ecf20Sopenharmony_ci memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len); 3428c2ecf20Sopenharmony_ci else if (cmd_len > ISCSI_CDB_SIZE) { 3438c2ecf20Sopenharmony_ci rc = iscsi_prep_ecdb_ahs(task); 3448c2ecf20Sopenharmony_ci if (rc) 3458c2ecf20Sopenharmony_ci return rc; 3468c2ecf20Sopenharmony_ci cmd_len = ISCSI_CDB_SIZE; 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci memcpy(hdr->cdb, sc->cmnd, cmd_len); 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci task->imm_count = 0; 3518c2ecf20Sopenharmony_ci if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) 3528c2ecf20Sopenharmony_ci task->protected = true; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci transfer_length = scsi_transfer_length(sc); 3558c2ecf20Sopenharmony_ci hdr->data_length = cpu_to_be32(transfer_length); 3568c2ecf20Sopenharmony_ci if (sc->sc_data_direction == DMA_TO_DEVICE) { 3578c2ecf20Sopenharmony_ci struct iscsi_r2t_info *r2t = &task->unsol_r2t; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_WRITE; 3608c2ecf20Sopenharmony_ci /* 3618c2ecf20Sopenharmony_ci * Write counters: 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * imm_count bytes to be sent right after 3648c2ecf20Sopenharmony_ci * SCSI PDU Header 3658c2ecf20Sopenharmony_ci * 3668c2ecf20Sopenharmony_ci * unsol_count bytes(as Data-Out) to be sent 3678c2ecf20Sopenharmony_ci * without R2T ack right after 3688c2ecf20Sopenharmony_ci * immediate data 3698c2ecf20Sopenharmony_ci * 3708c2ecf20Sopenharmony_ci * r2t data_length bytes to be sent via R2T ack's 3718c2ecf20Sopenharmony_ci * 3728c2ecf20Sopenharmony_ci * pad_count bytes to be sent as zero-padding 3738c2ecf20Sopenharmony_ci */ 3748c2ecf20Sopenharmony_ci memset(r2t, 0, sizeof(*r2t)); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci if (session->imm_data_en) { 3778c2ecf20Sopenharmony_ci if (transfer_length >= session->first_burst) 3788c2ecf20Sopenharmony_ci task->imm_count = min(session->first_burst, 3798c2ecf20Sopenharmony_ci conn->max_xmit_dlength); 3808c2ecf20Sopenharmony_ci else 3818c2ecf20Sopenharmony_ci task->imm_count = min(transfer_length, 3828c2ecf20Sopenharmony_ci conn->max_xmit_dlength); 3838c2ecf20Sopenharmony_ci hton24(hdr->dlength, task->imm_count); 3848c2ecf20Sopenharmony_ci } else 3858c2ecf20Sopenharmony_ci zero_data(hdr->dlength); 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if (!session->initial_r2t_en) { 3888c2ecf20Sopenharmony_ci r2t->data_length = min(session->first_burst, 3898c2ecf20Sopenharmony_ci transfer_length) - 3908c2ecf20Sopenharmony_ci task->imm_count; 3918c2ecf20Sopenharmony_ci r2t->data_offset = task->imm_count; 3928c2ecf20Sopenharmony_ci r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); 3938c2ecf20Sopenharmony_ci r2t->exp_statsn = cpu_to_be32(conn->exp_statsn); 3948c2ecf20Sopenharmony_ci } 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!task->unsol_r2t.data_length) 3978c2ecf20Sopenharmony_ci /* No unsolicit Data-Out's */ 3988c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 3998c2ecf20Sopenharmony_ci } else { 4008c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 4018c2ecf20Sopenharmony_ci zero_data(hdr->dlength); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (sc->sc_data_direction == DMA_FROM_DEVICE) 4048c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_READ; 4058c2ecf20Sopenharmony_ci } 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci /* calculate size of additional header segments (AHSs) */ 4088c2ecf20Sopenharmony_ci hdrlength = task->hdr_len - sizeof(*hdr); 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); 4118c2ecf20Sopenharmony_ci hdrlength /= ISCSI_PAD_LEN; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci WARN_ON(hdrlength >= 256); 4148c2ecf20Sopenharmony_ci hdr->hlength = hdrlength & 0xFF; 4158c2ecf20Sopenharmony_ci hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci if (session->tt->init_task && session->tt->init_task(task)) 4188c2ecf20Sopenharmony_ci return -EIO; 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_RUNNING; 4218c2ecf20Sopenharmony_ci session->cmdsn++; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci conn->scsicmd_pdus_cnt++; 4248c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " 4258c2ecf20Sopenharmony_ci "itt 0x%x len %d cmdsn %d win %d]\n", 4268c2ecf20Sopenharmony_ci sc->sc_data_direction == DMA_TO_DEVICE ? 4278c2ecf20Sopenharmony_ci "write" : "read", conn->id, sc, sc->cmnd[0], 4288c2ecf20Sopenharmony_ci task->itt, transfer_length, 4298c2ecf20Sopenharmony_ci session->cmdsn, 4308c2ecf20Sopenharmony_ci session->max_cmdsn - session->exp_cmdsn + 1); 4318c2ecf20Sopenharmony_ci return 0; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_ci/** 4358c2ecf20Sopenharmony_ci * iscsi_free_task - free a task 4368c2ecf20Sopenharmony_ci * @task: iscsi cmd task 4378c2ecf20Sopenharmony_ci * 4388c2ecf20Sopenharmony_ci * Must be called with session back_lock. 4398c2ecf20Sopenharmony_ci * This function returns the scsi command to scsi-ml or cleans 4408c2ecf20Sopenharmony_ci * up mgmt tasks then returns the task to the pool. 4418c2ecf20Sopenharmony_ci */ 4428c2ecf20Sopenharmony_cistatic void iscsi_free_task(struct iscsi_task *task) 4438c2ecf20Sopenharmony_ci{ 4448c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 4458c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 4468c2ecf20Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 4478c2ecf20Sopenharmony_ci int oldstate = task->state; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", 4508c2ecf20Sopenharmony_ci task->itt, task->state, task->sc); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci session->tt->cleanup_task(task); 4538c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_FREE; 4548c2ecf20Sopenharmony_ci task->sc = NULL; 4558c2ecf20Sopenharmony_ci /* 4568c2ecf20Sopenharmony_ci * login task is preallocated so do not free 4578c2ecf20Sopenharmony_ci */ 4588c2ecf20Sopenharmony_ci if (conn->login_task == task) 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci if (sc) { 4648c2ecf20Sopenharmony_ci /* SCSI eh reuses commands to verify us */ 4658c2ecf20Sopenharmony_ci sc->SCp.ptr = NULL; 4668c2ecf20Sopenharmony_ci /* 4678c2ecf20Sopenharmony_ci * queue command may call this to free the task, so 4688c2ecf20Sopenharmony_ci * it will decide how to return sc to scsi-ml. 4698c2ecf20Sopenharmony_ci */ 4708c2ecf20Sopenharmony_ci if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) 4718c2ecf20Sopenharmony_ci sc->scsi_done(sc); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci} 4748c2ecf20Sopenharmony_ci 4758c2ecf20Sopenharmony_civoid __iscsi_get_task(struct iscsi_task *task) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci refcount_inc(&task->refcount); 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__iscsi_get_task); 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_civoid __iscsi_put_task(struct iscsi_task *task) 4828c2ecf20Sopenharmony_ci{ 4838c2ecf20Sopenharmony_ci if (refcount_dec_and_test(&task->refcount)) 4848c2ecf20Sopenharmony_ci iscsi_free_task(task); 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__iscsi_put_task); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_civoid iscsi_put_task(struct iscsi_task *task) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci struct iscsi_session *session = task->conn->session; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci /* regular RX path uses back_lock */ 4938c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 4948c2ecf20Sopenharmony_ci __iscsi_put_task(task); 4958c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 4968c2ecf20Sopenharmony_ci} 4978c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_put_task); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci/** 5008c2ecf20Sopenharmony_ci * iscsi_complete_task - finish a task 5018c2ecf20Sopenharmony_ci * @task: iscsi cmd task 5028c2ecf20Sopenharmony_ci * @state: state to complete task with 5038c2ecf20Sopenharmony_ci * 5048c2ecf20Sopenharmony_ci * Must be called with session back_lock. 5058c2ecf20Sopenharmony_ci */ 5068c2ecf20Sopenharmony_cistatic void iscsi_complete_task(struct iscsi_task *task, int state) 5078c2ecf20Sopenharmony_ci{ 5088c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, 5118c2ecf20Sopenharmony_ci "complete task itt 0x%x state %d sc %p\n", 5128c2ecf20Sopenharmony_ci task->itt, task->state, task->sc); 5138c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_COMPLETED || 5148c2ecf20Sopenharmony_ci task->state == ISCSI_TASK_ABRT_TMF || 5158c2ecf20Sopenharmony_ci task->state == ISCSI_TASK_ABRT_SESS_RECOV || 5168c2ecf20Sopenharmony_ci task->state == ISCSI_TASK_REQUEUE_SCSIQ) 5178c2ecf20Sopenharmony_ci return; 5188c2ecf20Sopenharmony_ci WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); 5198c2ecf20Sopenharmony_ci task->state = state; 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci if (READ_ONCE(conn->ping_task) == task) 5228c2ecf20Sopenharmony_ci WRITE_ONCE(conn->ping_task, NULL); 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci /* release get from queueing */ 5258c2ecf20Sopenharmony_ci __iscsi_put_task(task); 5268c2ecf20Sopenharmony_ci} 5278c2ecf20Sopenharmony_ci 5288c2ecf20Sopenharmony_ci/** 5298c2ecf20Sopenharmony_ci * iscsi_complete_scsi_task - finish scsi task normally 5308c2ecf20Sopenharmony_ci * @task: iscsi task for scsi cmd 5318c2ecf20Sopenharmony_ci * @exp_cmdsn: expected cmd sn in cpu format 5328c2ecf20Sopenharmony_ci * @max_cmdsn: max cmd sn in cpu format 5338c2ecf20Sopenharmony_ci * 5348c2ecf20Sopenharmony_ci * This is used when drivers do not need or cannot perform 5358c2ecf20Sopenharmony_ci * lower level pdu processing. 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * Called with session back_lock 5388c2ecf20Sopenharmony_ci */ 5398c2ecf20Sopenharmony_civoid iscsi_complete_scsi_task(struct iscsi_task *task, 5408c2ecf20Sopenharmony_ci uint32_t exp_cmdsn, uint32_t max_cmdsn) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci conn->last_recv = jiffies; 5478c2ecf20Sopenharmony_ci __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn); 5488c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 5498c2ecf20Sopenharmony_ci} 5508c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_complete_scsi_task); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* 5538c2ecf20Sopenharmony_ci * Must be called with back and frwd lock 5548c2ecf20Sopenharmony_ci */ 5558c2ecf20Sopenharmony_cistatic bool cleanup_queued_task(struct iscsi_task *task) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 5588c2ecf20Sopenharmony_ci bool early_complete = false; 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* Bad target might have completed task while it was still running */ 5618c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_COMPLETED) 5628c2ecf20Sopenharmony_ci early_complete = true; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci if (!list_empty(&task->running)) { 5658c2ecf20Sopenharmony_ci list_del_init(&task->running); 5668c2ecf20Sopenharmony_ci /* 5678c2ecf20Sopenharmony_ci * If it's on a list but still running, this could be from 5688c2ecf20Sopenharmony_ci * a bad target sending a rsp early, cleanup from a TMF, or 5698c2ecf20Sopenharmony_ci * session recovery. 5708c2ecf20Sopenharmony_ci */ 5718c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_RUNNING || 5728c2ecf20Sopenharmony_ci task->state == ISCSI_TASK_COMPLETED) 5738c2ecf20Sopenharmony_ci __iscsi_put_task(task); 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci if (conn->task == task) { 5778c2ecf20Sopenharmony_ci conn->task = NULL; 5788c2ecf20Sopenharmony_ci __iscsi_put_task(task); 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return early_complete; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ci/* 5858c2ecf20Sopenharmony_ci * session frwd lock must be held and if not called for a task that is still 5868c2ecf20Sopenharmony_ci * pending or from the xmit thread, then xmit thread must be suspended 5878c2ecf20Sopenharmony_ci */ 5888c2ecf20Sopenharmony_cistatic void fail_scsi_task(struct iscsi_task *task, int err) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 5918c2ecf20Sopenharmony_ci struct scsi_cmnd *sc; 5928c2ecf20Sopenharmony_ci int state; 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->back_lock); 5958c2ecf20Sopenharmony_ci if (cleanup_queued_task(task)) { 5968c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 5978c2ecf20Sopenharmony_ci return; 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci 6008c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) { 6018c2ecf20Sopenharmony_ci /* 6028c2ecf20Sopenharmony_ci * cmd never made it to the xmit thread, so we should not count 6038c2ecf20Sopenharmony_ci * the cmd in the sequencing 6048c2ecf20Sopenharmony_ci */ 6058c2ecf20Sopenharmony_ci conn->session->queued_cmdsn--; 6068c2ecf20Sopenharmony_ci /* it was never sent so just complete like normal */ 6078c2ecf20Sopenharmony_ci state = ISCSI_TASK_COMPLETED; 6088c2ecf20Sopenharmony_ci } else if (err == DID_TRANSPORT_DISRUPTED) 6098c2ecf20Sopenharmony_ci state = ISCSI_TASK_ABRT_SESS_RECOV; 6108c2ecf20Sopenharmony_ci else 6118c2ecf20Sopenharmony_ci state = ISCSI_TASK_ABRT_TMF; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci sc = task->sc; 6148c2ecf20Sopenharmony_ci sc->result = err << 16; 6158c2ecf20Sopenharmony_ci scsi_set_resid(sc, scsi_bufflen(sc)); 6168c2ecf20Sopenharmony_ci iscsi_complete_task(task, state); 6178c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 6188c2ecf20Sopenharmony_ci} 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_cistatic int iscsi_prep_mgmt_task(struct iscsi_conn *conn, 6218c2ecf20Sopenharmony_ci struct iscsi_task *task) 6228c2ecf20Sopenharmony_ci{ 6238c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 6248c2ecf20Sopenharmony_ci struct iscsi_hdr *hdr = task->hdr; 6258c2ecf20Sopenharmony_ci struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; 6268c2ecf20Sopenharmony_ci uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) 6298c2ecf20Sopenharmony_ci return -ENOTCONN; 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT) 6328c2ecf20Sopenharmony_ci nop->exp_statsn = cpu_to_be32(conn->exp_statsn); 6338c2ecf20Sopenharmony_ci /* 6348c2ecf20Sopenharmony_ci * pre-format CmdSN for outgoing PDU. 6358c2ecf20Sopenharmony_ci */ 6368c2ecf20Sopenharmony_ci nop->cmdsn = cpu_to_be32(session->cmdsn); 6378c2ecf20Sopenharmony_ci if (hdr->itt != RESERVED_ITT) { 6388c2ecf20Sopenharmony_ci /* 6398c2ecf20Sopenharmony_ci * TODO: We always use immediate for normal session pdus. 6408c2ecf20Sopenharmony_ci * If we start to send tmfs or nops as non-immediate then 6418c2ecf20Sopenharmony_ci * we should start checking the cmdsn numbers for mgmt tasks. 6428c2ecf20Sopenharmony_ci * 6438c2ecf20Sopenharmony_ci * During discovery sessions iscsid sends TEXT as non immediate, 6448c2ecf20Sopenharmony_ci * but we always only send one PDU at a time. 6458c2ecf20Sopenharmony_ci */ 6468c2ecf20Sopenharmony_ci if (conn->c_stage == ISCSI_CONN_STARTED && 6478c2ecf20Sopenharmony_ci !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { 6488c2ecf20Sopenharmony_ci session->queued_cmdsn++; 6498c2ecf20Sopenharmony_ci session->cmdsn++; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci if (session->tt->init_task && session->tt->init_task(task)) 6548c2ecf20Sopenharmony_ci return -EIO; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) 6578c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_LOGGING_OUT; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_RUNNING; 6608c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "mgmtpdu [op 0x%x hdr->itt 0x%x " 6618c2ecf20Sopenharmony_ci "datalen %d]\n", hdr->opcode & ISCSI_OPCODE_MASK, 6628c2ecf20Sopenharmony_ci hdr->itt, task->data_count); 6638c2ecf20Sopenharmony_ci return 0; 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_cistatic struct iscsi_task * 6678c2ecf20Sopenharmony_ci__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 6688c2ecf20Sopenharmony_ci char *data, uint32_t data_size) 6698c2ecf20Sopenharmony_ci{ 6708c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 6718c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(session->host); 6728c2ecf20Sopenharmony_ci uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; 6738c2ecf20Sopenharmony_ci struct iscsi_task *task; 6748c2ecf20Sopenharmony_ci itt_t itt; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE) 6778c2ecf20Sopenharmony_ci return NULL; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) { 6808c2ecf20Sopenharmony_ci /* 6818c2ecf20Sopenharmony_ci * Login and Text are sent serially, in 6828c2ecf20Sopenharmony_ci * request-followed-by-response sequence. 6838c2ecf20Sopenharmony_ci * Same task can be used. Same ITT must be used. 6848c2ecf20Sopenharmony_ci * Note that login_task is preallocated at conn_create(). 6858c2ecf20Sopenharmony_ci */ 6868c2ecf20Sopenharmony_ci if (conn->login_task->state != ISCSI_TASK_FREE) { 6878c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Login/Text in " 6888c2ecf20Sopenharmony_ci "progress. Cannot start new task.\n"); 6898c2ecf20Sopenharmony_ci return NULL; 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci if (data_size > ISCSI_DEF_MAX_RECV_SEG_LEN) { 6938c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Invalid buffer len of %u for login task. Max len is %u\n", data_size, ISCSI_DEF_MAX_RECV_SEG_LEN); 6948c2ecf20Sopenharmony_ci return NULL; 6958c2ecf20Sopenharmony_ci } 6968c2ecf20Sopenharmony_ci 6978c2ecf20Sopenharmony_ci task = conn->login_task; 6988c2ecf20Sopenharmony_ci } else { 6998c2ecf20Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) 7008c2ecf20Sopenharmony_ci return NULL; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci if (data_size != 0) { 7038c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Can not send data buffer of len %u for op 0x%x\n", data_size, opcode); 7048c2ecf20Sopenharmony_ci return NULL; 7058c2ecf20Sopenharmony_ci } 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); 7088c2ecf20Sopenharmony_ci BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (!kfifo_out(&session->cmdpool.queue, 7118c2ecf20Sopenharmony_ci (void*)&task, sizeof(void*))) 7128c2ecf20Sopenharmony_ci return NULL; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci /* 7158c2ecf20Sopenharmony_ci * released in complete pdu for task we expect a response for, and 7168c2ecf20Sopenharmony_ci * released by the lld when it has transmitted the task for 7178c2ecf20Sopenharmony_ci * pdus we do not expect a response for. 7188c2ecf20Sopenharmony_ci */ 7198c2ecf20Sopenharmony_ci refcount_set(&task->refcount, 1); 7208c2ecf20Sopenharmony_ci task->conn = conn; 7218c2ecf20Sopenharmony_ci task->sc = NULL; 7228c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&task->running); 7238c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_PENDING; 7248c2ecf20Sopenharmony_ci 7258c2ecf20Sopenharmony_ci if (data_size) { 7268c2ecf20Sopenharmony_ci memcpy(task->data, data, data_size); 7278c2ecf20Sopenharmony_ci task->data_count = data_size; 7288c2ecf20Sopenharmony_ci } else 7298c2ecf20Sopenharmony_ci task->data_count = 0; 7308c2ecf20Sopenharmony_ci 7318c2ecf20Sopenharmony_ci if (conn->session->tt->alloc_pdu) { 7328c2ecf20Sopenharmony_ci if (conn->session->tt->alloc_pdu(task, hdr->opcode)) { 7338c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not allocate " 7348c2ecf20Sopenharmony_ci "pdu for mgmt task.\n"); 7358c2ecf20Sopenharmony_ci goto free_task; 7368c2ecf20Sopenharmony_ci } 7378c2ecf20Sopenharmony_ci } 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci itt = task->hdr->itt; 7408c2ecf20Sopenharmony_ci task->hdr_len = sizeof(struct iscsi_hdr); 7418c2ecf20Sopenharmony_ci memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr)); 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (hdr->itt != RESERVED_ITT) { 7448c2ecf20Sopenharmony_ci if (session->tt->parse_pdu_itt) 7458c2ecf20Sopenharmony_ci task->hdr->itt = itt; 7468c2ecf20Sopenharmony_ci else 7478c2ecf20Sopenharmony_ci task->hdr->itt = build_itt(task->itt, 7488c2ecf20Sopenharmony_ci task->conn->session->age); 7498c2ecf20Sopenharmony_ci } 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (unlikely(READ_ONCE(conn->ping_task) == INVALID_SCSI_TASK)) 7528c2ecf20Sopenharmony_ci WRITE_ONCE(conn->ping_task, task); 7538c2ecf20Sopenharmony_ci 7548c2ecf20Sopenharmony_ci if (!ihost->workq) { 7558c2ecf20Sopenharmony_ci if (iscsi_prep_mgmt_task(conn, task)) 7568c2ecf20Sopenharmony_ci goto free_task; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci if (session->tt->xmit_task(task)) 7598c2ecf20Sopenharmony_ci goto free_task; 7608c2ecf20Sopenharmony_ci } else { 7618c2ecf20Sopenharmony_ci list_add_tail(&task->running, &conn->mgmtqueue); 7628c2ecf20Sopenharmony_ci iscsi_conn_queue_work(conn); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci return task; 7668c2ecf20Sopenharmony_ci 7678c2ecf20Sopenharmony_cifree_task: 7688c2ecf20Sopenharmony_ci /* regular RX path uses back_lock */ 7698c2ecf20Sopenharmony_ci spin_lock(&session->back_lock); 7708c2ecf20Sopenharmony_ci __iscsi_put_task(task); 7718c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 7728c2ecf20Sopenharmony_ci return NULL; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ciint iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, 7768c2ecf20Sopenharmony_ci char *data, uint32_t data_size) 7778c2ecf20Sopenharmony_ci{ 7788c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 7798c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 7808c2ecf20Sopenharmony_ci int err = 0; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 7838c2ecf20Sopenharmony_ci if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) 7848c2ecf20Sopenharmony_ci err = -EPERM; 7858c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 7868c2ecf20Sopenharmony_ci return err; 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci/** 7918c2ecf20Sopenharmony_ci * iscsi_cmd_rsp - SCSI Command Response processing 7928c2ecf20Sopenharmony_ci * @conn: iscsi connection 7938c2ecf20Sopenharmony_ci * @hdr: iscsi header 7948c2ecf20Sopenharmony_ci * @task: scsi command task 7958c2ecf20Sopenharmony_ci * @data: cmd data buffer 7968c2ecf20Sopenharmony_ci * @datalen: len of buffer 7978c2ecf20Sopenharmony_ci * 7988c2ecf20Sopenharmony_ci * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and 7998c2ecf20Sopenharmony_ci * then completes the command and task. called under back_lock 8008c2ecf20Sopenharmony_ci **/ 8018c2ecf20Sopenharmony_cistatic void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 8028c2ecf20Sopenharmony_ci struct iscsi_task *task, char *data, 8038c2ecf20Sopenharmony_ci int datalen) 8048c2ecf20Sopenharmony_ci{ 8058c2ecf20Sopenharmony_ci struct iscsi_scsi_rsp *rhdr = (struct iscsi_scsi_rsp *)hdr; 8068c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 8078c2ecf20Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 8088c2ecf20Sopenharmony_ci 8098c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); 8108c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_ci sc->result = (DID_OK << 16) | rhdr->cmd_status; 8138c2ecf20Sopenharmony_ci 8148c2ecf20Sopenharmony_ci if (task->protected) { 8158c2ecf20Sopenharmony_ci sector_t sector; 8168c2ecf20Sopenharmony_ci u8 ascq; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /** 8198c2ecf20Sopenharmony_ci * Transports that didn't implement check_protection 8208c2ecf20Sopenharmony_ci * callback but still published T10-PI support to scsi-mid 8218c2ecf20Sopenharmony_ci * deserve this BUG_ON. 8228c2ecf20Sopenharmony_ci **/ 8238c2ecf20Sopenharmony_ci BUG_ON(!session->tt->check_protection); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci ascq = session->tt->check_protection(task, §or); 8268c2ecf20Sopenharmony_ci if (ascq) { 8278c2ecf20Sopenharmony_ci sc->result = DRIVER_SENSE << 24 | 8288c2ecf20Sopenharmony_ci SAM_STAT_CHECK_CONDITION; 8298c2ecf20Sopenharmony_ci scsi_build_sense_buffer(1, sc->sense_buffer, 8308c2ecf20Sopenharmony_ci ILLEGAL_REQUEST, 0x10, ascq); 8318c2ecf20Sopenharmony_ci scsi_set_sense_information(sc->sense_buffer, 8328c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE, 8338c2ecf20Sopenharmony_ci sector); 8348c2ecf20Sopenharmony_ci goto out; 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci } 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) { 8398c2ecf20Sopenharmony_ci sc->result = DID_ERROR << 16; 8408c2ecf20Sopenharmony_ci goto out; 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) { 8448c2ecf20Sopenharmony_ci uint16_t senselen; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (datalen < 2) { 8478c2ecf20Sopenharmony_ciinvalid_datalen: 8488c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 8498c2ecf20Sopenharmony_ci "Got CHECK_CONDITION but invalid data " 8508c2ecf20Sopenharmony_ci "buffer size of %d\n", datalen); 8518c2ecf20Sopenharmony_ci sc->result = DID_BAD_TARGET << 16; 8528c2ecf20Sopenharmony_ci goto out; 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci senselen = get_unaligned_be16(data); 8568c2ecf20Sopenharmony_ci if (datalen < senselen) 8578c2ecf20Sopenharmony_ci goto invalid_datalen; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci memcpy(sc->sense_buffer, data + 2, 8608c2ecf20Sopenharmony_ci min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); 8618c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "copied %d bytes of sense\n", 8628c2ecf20Sopenharmony_ci min_t(uint16_t, senselen, 8638c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE)); 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | 8678c2ecf20Sopenharmony_ci ISCSI_FLAG_CMD_BIDI_OVERFLOW)) { 8688c2ecf20Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | 8728c2ecf20Sopenharmony_ci ISCSI_FLAG_CMD_OVERFLOW)) { 8738c2ecf20Sopenharmony_ci int res_count = be32_to_cpu(rhdr->residual_count); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci if (res_count > 0 && 8768c2ecf20Sopenharmony_ci (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || 8778c2ecf20Sopenharmony_ci res_count <= scsi_bufflen(sc))) 8788c2ecf20Sopenharmony_ci /* write side for bidi or uni-io set_resid */ 8798c2ecf20Sopenharmony_ci scsi_set_resid(sc, res_count); 8808c2ecf20Sopenharmony_ci else 8818c2ecf20Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 8828c2ecf20Sopenharmony_ci } 8838c2ecf20Sopenharmony_ciout: 8848c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "cmd rsp done [sc %p res %d itt 0x%x]\n", 8858c2ecf20Sopenharmony_ci sc, sc->result, task->itt); 8868c2ecf20Sopenharmony_ci conn->scsirsp_pdus_cnt++; 8878c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 8888c2ecf20Sopenharmony_ci} 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci/** 8918c2ecf20Sopenharmony_ci * iscsi_data_in_rsp - SCSI Data-In Response processing 8928c2ecf20Sopenharmony_ci * @conn: iscsi connection 8938c2ecf20Sopenharmony_ci * @hdr: iscsi pdu 8948c2ecf20Sopenharmony_ci * @task: scsi command task 8958c2ecf20Sopenharmony_ci * 8968c2ecf20Sopenharmony_ci * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received 8978c2ecf20Sopenharmony_ci * then completes the command and task. called under back_lock 8988c2ecf20Sopenharmony_ci **/ 8998c2ecf20Sopenharmony_cistatic void 9008c2ecf20Sopenharmony_ciiscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 9018c2ecf20Sopenharmony_ci struct iscsi_task *task) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr; 9048c2ecf20Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) 9078c2ecf20Sopenharmony_ci return; 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci iscsi_update_cmdsn(conn->session, (struct iscsi_nopin *)hdr); 9108c2ecf20Sopenharmony_ci sc->result = (DID_OK << 16) | rhdr->cmd_status; 9118c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; 9128c2ecf20Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | 9138c2ecf20Sopenharmony_ci ISCSI_FLAG_DATA_OVERFLOW)) { 9148c2ecf20Sopenharmony_ci int res_count = be32_to_cpu(rhdr->residual_count); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci if (res_count > 0 && 9178c2ecf20Sopenharmony_ci (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || 9188c2ecf20Sopenharmony_ci res_count <= sc->sdb.length)) 9198c2ecf20Sopenharmony_ci scsi_set_resid(sc, res_count); 9208c2ecf20Sopenharmony_ci else 9218c2ecf20Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 9228c2ecf20Sopenharmony_ci } 9238c2ecf20Sopenharmony_ci 9248c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "data in with status done " 9258c2ecf20Sopenharmony_ci "[sc %p res %d itt 0x%x]\n", 9268c2ecf20Sopenharmony_ci sc, sc->result, task->itt); 9278c2ecf20Sopenharmony_ci conn->scsirsp_pdus_cnt++; 9288c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cistatic void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; 9348c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 9378c2ecf20Sopenharmony_ci conn->tmfrsp_pdus_cnt++; 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci if (session->tmf_state != TMF_QUEUED) 9408c2ecf20Sopenharmony_ci return; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci if (tmf->response == ISCSI_TMF_RSP_COMPLETE) 9438c2ecf20Sopenharmony_ci session->tmf_state = TMF_SUCCESS; 9448c2ecf20Sopenharmony_ci else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) 9458c2ecf20Sopenharmony_ci session->tmf_state = TMF_NOT_FOUND; 9468c2ecf20Sopenharmony_ci else 9478c2ecf20Sopenharmony_ci session->tmf_state = TMF_FAILED; 9488c2ecf20Sopenharmony_ci wake_up(&session->ehwait); 9498c2ecf20Sopenharmony_ci} 9508c2ecf20Sopenharmony_ci 9518c2ecf20Sopenharmony_cistatic int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) 9528c2ecf20Sopenharmony_ci{ 9538c2ecf20Sopenharmony_ci struct iscsi_nopout hdr; 9548c2ecf20Sopenharmony_ci struct iscsi_task *task; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (!rhdr) { 9578c2ecf20Sopenharmony_ci if (READ_ONCE(conn->ping_task)) 9588c2ecf20Sopenharmony_ci return -EINVAL; 9598c2ecf20Sopenharmony_ci WRITE_ONCE(conn->ping_task, INVALID_SCSI_TASK); 9608c2ecf20Sopenharmony_ci } 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci memset(&hdr, 0, sizeof(struct iscsi_nopout)); 9638c2ecf20Sopenharmony_ci hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; 9648c2ecf20Sopenharmony_ci hdr.flags = ISCSI_FLAG_CMD_FINAL; 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci if (rhdr) { 9678c2ecf20Sopenharmony_ci hdr.lun = rhdr->lun; 9688c2ecf20Sopenharmony_ci hdr.ttt = rhdr->ttt; 9698c2ecf20Sopenharmony_ci hdr.itt = RESERVED_ITT; 9708c2ecf20Sopenharmony_ci } else 9718c2ecf20Sopenharmony_ci hdr.ttt = RESERVED_ITT; 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0); 9748c2ecf20Sopenharmony_ci if (!task) { 9758c2ecf20Sopenharmony_ci if (!rhdr) 9768c2ecf20Sopenharmony_ci WRITE_ONCE(conn->ping_task, NULL); 9778c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); 9788c2ecf20Sopenharmony_ci return -EIO; 9798c2ecf20Sopenharmony_ci } else if (!rhdr) { 9808c2ecf20Sopenharmony_ci /* only track our nops */ 9818c2ecf20Sopenharmony_ci conn->last_ping = jiffies; 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci return 0; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/** 9888c2ecf20Sopenharmony_ci * iscsi_nop_out_rsp - SCSI NOP Response processing 9898c2ecf20Sopenharmony_ci * @task: scsi command task 9908c2ecf20Sopenharmony_ci * @nop: the nop structure 9918c2ecf20Sopenharmony_ci * @data: where to put the data 9928c2ecf20Sopenharmony_ci * @datalen: length of data 9938c2ecf20Sopenharmony_ci * 9948c2ecf20Sopenharmony_ci * iscsi_nop_out_rsp handles nop response from use or 9958c2ecf20Sopenharmony_ci * from user space. called under back_lock 9968c2ecf20Sopenharmony_ci **/ 9978c2ecf20Sopenharmony_cistatic int iscsi_nop_out_rsp(struct iscsi_task *task, 9988c2ecf20Sopenharmony_ci struct iscsi_nopin *nop, char *data, int datalen) 9998c2ecf20Sopenharmony_ci{ 10008c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 10018c2ecf20Sopenharmony_ci int rc = 0; 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci if (READ_ONCE(conn->ping_task) != task) { 10048c2ecf20Sopenharmony_ci /* 10058c2ecf20Sopenharmony_ci * If this is not in response to one of our 10068c2ecf20Sopenharmony_ci * nops then it must be from userspace. 10078c2ecf20Sopenharmony_ci */ 10088c2ecf20Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop, 10098c2ecf20Sopenharmony_ci data, datalen)) 10108c2ecf20Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 10118c2ecf20Sopenharmony_ci } else 10128c2ecf20Sopenharmony_ci mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); 10138c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 10148c2ecf20Sopenharmony_ci return rc; 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 10188c2ecf20Sopenharmony_ci char *data, int datalen) 10198c2ecf20Sopenharmony_ci{ 10208c2ecf20Sopenharmony_ci struct iscsi_reject *reject = (struct iscsi_reject *)hdr; 10218c2ecf20Sopenharmony_ci struct iscsi_hdr rejected_pdu; 10228c2ecf20Sopenharmony_ci int opcode, rc = 0; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci if (ntoh24(reject->dlength) > datalen || 10278c2ecf20Sopenharmony_ci ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) { 10288c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected " 10298c2ecf20Sopenharmony_ci "pdu. Invalid data length (pdu dlength " 10308c2ecf20Sopenharmony_ci "%u, datalen %d\n", ntoh24(reject->dlength), 10318c2ecf20Sopenharmony_ci datalen); 10328c2ecf20Sopenharmony_ci return ISCSI_ERR_PROTO; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); 10358c2ecf20Sopenharmony_ci opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK; 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_ci switch (reject->reason) { 10388c2ecf20Sopenharmony_ci case ISCSI_REASON_DATA_DIGEST_ERROR: 10398c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 10408c2ecf20Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected " 10418c2ecf20Sopenharmony_ci "due to DataDigest error.\n", 10428c2ecf20Sopenharmony_ci opcode, rejected_pdu.itt); 10438c2ecf20Sopenharmony_ci break; 10448c2ecf20Sopenharmony_ci case ISCSI_REASON_IMM_CMD_REJECT: 10458c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 10468c2ecf20Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected. Too many " 10478c2ecf20Sopenharmony_ci "immediate commands.\n", 10488c2ecf20Sopenharmony_ci opcode, rejected_pdu.itt); 10498c2ecf20Sopenharmony_ci /* 10508c2ecf20Sopenharmony_ci * We only send one TMF at a time so if the target could not 10518c2ecf20Sopenharmony_ci * handle it, then it should get fixed (RFC mandates that 10528c2ecf20Sopenharmony_ci * a target can handle one immediate TMF per conn). 10538c2ecf20Sopenharmony_ci * 10548c2ecf20Sopenharmony_ci * For nops-outs, we could have sent more than one if 10558c2ecf20Sopenharmony_ci * the target is sending us lots of nop-ins 10568c2ecf20Sopenharmony_ci */ 10578c2ecf20Sopenharmony_ci if (opcode != ISCSI_OP_NOOP_OUT) 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci 10608c2ecf20Sopenharmony_ci if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) { 10618c2ecf20Sopenharmony_ci /* 10628c2ecf20Sopenharmony_ci * nop-out in response to target's nop-out rejected. 10638c2ecf20Sopenharmony_ci * Just resend. 10648c2ecf20Sopenharmony_ci */ 10658c2ecf20Sopenharmony_ci /* In RX path we are under back lock */ 10668c2ecf20Sopenharmony_ci spin_unlock(&conn->session->back_lock); 10678c2ecf20Sopenharmony_ci spin_lock(&conn->session->frwd_lock); 10688c2ecf20Sopenharmony_ci iscsi_send_nopout(conn, 10698c2ecf20Sopenharmony_ci (struct iscsi_nopin*)&rejected_pdu); 10708c2ecf20Sopenharmony_ci spin_unlock(&conn->session->frwd_lock); 10718c2ecf20Sopenharmony_ci spin_lock(&conn->session->back_lock); 10728c2ecf20Sopenharmony_ci } else { 10738c2ecf20Sopenharmony_ci struct iscsi_task *task; 10748c2ecf20Sopenharmony_ci /* 10758c2ecf20Sopenharmony_ci * Our nop as ping got dropped. We know the target 10768c2ecf20Sopenharmony_ci * and transport are ok so just clean up 10778c2ecf20Sopenharmony_ci */ 10788c2ecf20Sopenharmony_ci task = iscsi_itt_to_task(conn, rejected_pdu.itt); 10798c2ecf20Sopenharmony_ci if (!task) { 10808c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 10818c2ecf20Sopenharmony_ci "Invalid pdu reject. Could " 10828c2ecf20Sopenharmony_ci "not lookup rejected task.\n"); 10838c2ecf20Sopenharmony_ci rc = ISCSI_ERR_BAD_ITT; 10848c2ecf20Sopenharmony_ci } else 10858c2ecf20Sopenharmony_ci rc = iscsi_nop_out_rsp(task, 10868c2ecf20Sopenharmony_ci (struct iscsi_nopin*)&rejected_pdu, 10878c2ecf20Sopenharmony_ci NULL, 0); 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci break; 10908c2ecf20Sopenharmony_ci default: 10918c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 10928c2ecf20Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected. Reason " 10938c2ecf20Sopenharmony_ci "code 0x%x\n", rejected_pdu.opcode, 10948c2ecf20Sopenharmony_ci rejected_pdu.itt, reject->reason); 10958c2ecf20Sopenharmony_ci break; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci return rc; 10988c2ecf20Sopenharmony_ci} 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci/** 11018c2ecf20Sopenharmony_ci * iscsi_itt_to_task - look up task by itt 11028c2ecf20Sopenharmony_ci * @conn: iscsi connection 11038c2ecf20Sopenharmony_ci * @itt: itt 11048c2ecf20Sopenharmony_ci * 11058c2ecf20Sopenharmony_ci * This should be used for mgmt tasks like login and nops, or if 11068c2ecf20Sopenharmony_ci * the LDD's itt space does not include the session age. 11078c2ecf20Sopenharmony_ci * 11088c2ecf20Sopenharmony_ci * The session back_lock must be held. 11098c2ecf20Sopenharmony_ci */ 11108c2ecf20Sopenharmony_cistruct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) 11118c2ecf20Sopenharmony_ci{ 11128c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 11138c2ecf20Sopenharmony_ci int i; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (itt == RESERVED_ITT) 11168c2ecf20Sopenharmony_ci return NULL; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci if (session->tt->parse_pdu_itt) 11198c2ecf20Sopenharmony_ci session->tt->parse_pdu_itt(conn, itt, &i, NULL); 11208c2ecf20Sopenharmony_ci else 11218c2ecf20Sopenharmony_ci i = get_itt(itt); 11228c2ecf20Sopenharmony_ci if (i >= session->cmds_max) 11238c2ecf20Sopenharmony_ci return NULL; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci return session->cmds[i]; 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_itt_to_task); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci/** 11308c2ecf20Sopenharmony_ci * __iscsi_complete_pdu - complete pdu 11318c2ecf20Sopenharmony_ci * @conn: iscsi conn 11328c2ecf20Sopenharmony_ci * @hdr: iscsi header 11338c2ecf20Sopenharmony_ci * @data: data buffer 11348c2ecf20Sopenharmony_ci * @datalen: len of data buffer 11358c2ecf20Sopenharmony_ci * 11368c2ecf20Sopenharmony_ci * Completes pdu processing by freeing any resources allocated at 11378c2ecf20Sopenharmony_ci * queuecommand or send generic. session back_lock must be held and verify 11388c2ecf20Sopenharmony_ci * itt must have been called. 11398c2ecf20Sopenharmony_ci */ 11408c2ecf20Sopenharmony_ciint __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 11418c2ecf20Sopenharmony_ci char *data, int datalen) 11428c2ecf20Sopenharmony_ci{ 11438c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 11448c2ecf20Sopenharmony_ci int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; 11458c2ecf20Sopenharmony_ci struct iscsi_task *task; 11468c2ecf20Sopenharmony_ci uint32_t itt; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci conn->last_recv = jiffies; 11498c2ecf20Sopenharmony_ci rc = iscsi_verify_itt(conn, hdr->itt); 11508c2ecf20Sopenharmony_ci if (rc) 11518c2ecf20Sopenharmony_ci return rc; 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci if (hdr->itt != RESERVED_ITT) 11548c2ecf20Sopenharmony_ci itt = get_itt(hdr->itt); 11558c2ecf20Sopenharmony_ci else 11568c2ecf20Sopenharmony_ci itt = ~0U; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "[op 0x%x cid %d itt 0x%x len %d]\n", 11598c2ecf20Sopenharmony_ci opcode, conn->id, itt, datalen); 11608c2ecf20Sopenharmony_ci 11618c2ecf20Sopenharmony_ci if (itt == ~0U) { 11628c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci switch(opcode) { 11658c2ecf20Sopenharmony_ci case ISCSI_OP_NOOP_IN: 11668c2ecf20Sopenharmony_ci if (datalen) { 11678c2ecf20Sopenharmony_ci rc = ISCSI_ERR_PROTO; 11688c2ecf20Sopenharmony_ci break; 11698c2ecf20Sopenharmony_ci } 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) 11728c2ecf20Sopenharmony_ci break; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci /* In RX path we are under back lock */ 11758c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 11768c2ecf20Sopenharmony_ci spin_lock(&session->frwd_lock); 11778c2ecf20Sopenharmony_ci iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr); 11788c2ecf20Sopenharmony_ci spin_unlock(&session->frwd_lock); 11798c2ecf20Sopenharmony_ci spin_lock(&session->back_lock); 11808c2ecf20Sopenharmony_ci break; 11818c2ecf20Sopenharmony_ci case ISCSI_OP_REJECT: 11828c2ecf20Sopenharmony_ci rc = iscsi_handle_reject(conn, hdr, data, datalen); 11838c2ecf20Sopenharmony_ci break; 11848c2ecf20Sopenharmony_ci case ISCSI_OP_ASYNC_EVENT: 11858c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 11868c2ecf20Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) 11878c2ecf20Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 11888c2ecf20Sopenharmony_ci break; 11898c2ecf20Sopenharmony_ci default: 11908c2ecf20Sopenharmony_ci rc = ISCSI_ERR_BAD_OPCODE; 11918c2ecf20Sopenharmony_ci break; 11928c2ecf20Sopenharmony_ci } 11938c2ecf20Sopenharmony_ci goto out; 11948c2ecf20Sopenharmony_ci } 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci switch(opcode) { 11978c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_CMD_RSP: 11988c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_DATA_IN: 11998c2ecf20Sopenharmony_ci task = iscsi_itt_to_ctask(conn, hdr->itt); 12008c2ecf20Sopenharmony_ci if (!task) 12018c2ecf20Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 12028c2ecf20Sopenharmony_ci task->last_xfer = jiffies; 12038c2ecf20Sopenharmony_ci break; 12048c2ecf20Sopenharmony_ci case ISCSI_OP_R2T: 12058c2ecf20Sopenharmony_ci /* 12068c2ecf20Sopenharmony_ci * LLD handles R2Ts if they need to. 12078c2ecf20Sopenharmony_ci */ 12088c2ecf20Sopenharmony_ci return 0; 12098c2ecf20Sopenharmony_ci case ISCSI_OP_LOGOUT_RSP: 12108c2ecf20Sopenharmony_ci case ISCSI_OP_LOGIN_RSP: 12118c2ecf20Sopenharmony_ci case ISCSI_OP_TEXT_RSP: 12128c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_TMFUNC_RSP: 12138c2ecf20Sopenharmony_ci case ISCSI_OP_NOOP_IN: 12148c2ecf20Sopenharmony_ci task = iscsi_itt_to_task(conn, hdr->itt); 12158c2ecf20Sopenharmony_ci if (!task) 12168c2ecf20Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 12178c2ecf20Sopenharmony_ci break; 12188c2ecf20Sopenharmony_ci default: 12198c2ecf20Sopenharmony_ci return ISCSI_ERR_BAD_OPCODE; 12208c2ecf20Sopenharmony_ci } 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci switch(opcode) { 12238c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_CMD_RSP: 12248c2ecf20Sopenharmony_ci iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); 12258c2ecf20Sopenharmony_ci break; 12268c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_DATA_IN: 12278c2ecf20Sopenharmony_ci iscsi_data_in_rsp(conn, hdr, task); 12288c2ecf20Sopenharmony_ci break; 12298c2ecf20Sopenharmony_ci case ISCSI_OP_LOGOUT_RSP: 12308c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 12318c2ecf20Sopenharmony_ci if (datalen) { 12328c2ecf20Sopenharmony_ci rc = ISCSI_ERR_PROTO; 12338c2ecf20Sopenharmony_ci break; 12348c2ecf20Sopenharmony_ci } 12358c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 12368c2ecf20Sopenharmony_ci goto recv_pdu; 12378c2ecf20Sopenharmony_ci case ISCSI_OP_LOGIN_RSP: 12388c2ecf20Sopenharmony_ci case ISCSI_OP_TEXT_RSP: 12398c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 12408c2ecf20Sopenharmony_ci /* 12418c2ecf20Sopenharmony_ci * login related PDU's exp_statsn is handled in 12428c2ecf20Sopenharmony_ci * userspace 12438c2ecf20Sopenharmony_ci */ 12448c2ecf20Sopenharmony_ci goto recv_pdu; 12458c2ecf20Sopenharmony_ci case ISCSI_OP_SCSI_TMFUNC_RSP: 12468c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 12478c2ecf20Sopenharmony_ci if (datalen) { 12488c2ecf20Sopenharmony_ci rc = ISCSI_ERR_PROTO; 12498c2ecf20Sopenharmony_ci break; 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci iscsi_tmf_rsp(conn, hdr); 12538c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 12548c2ecf20Sopenharmony_ci break; 12558c2ecf20Sopenharmony_ci case ISCSI_OP_NOOP_IN: 12568c2ecf20Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 12578c2ecf20Sopenharmony_ci if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) { 12588c2ecf20Sopenharmony_ci rc = ISCSI_ERR_PROTO; 12598c2ecf20Sopenharmony_ci break; 12608c2ecf20Sopenharmony_ci } 12618c2ecf20Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr, 12648c2ecf20Sopenharmony_ci data, datalen); 12658c2ecf20Sopenharmony_ci break; 12668c2ecf20Sopenharmony_ci default: 12678c2ecf20Sopenharmony_ci rc = ISCSI_ERR_BAD_OPCODE; 12688c2ecf20Sopenharmony_ci break; 12698c2ecf20Sopenharmony_ci } 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ciout: 12728c2ecf20Sopenharmony_ci return rc; 12738c2ecf20Sopenharmony_cirecv_pdu: 12748c2ecf20Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) 12758c2ecf20Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 12768c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 12778c2ecf20Sopenharmony_ci return rc; 12788c2ecf20Sopenharmony_ci} 12798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__iscsi_complete_pdu); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ciint iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 12828c2ecf20Sopenharmony_ci char *data, int datalen) 12838c2ecf20Sopenharmony_ci{ 12848c2ecf20Sopenharmony_ci int rc; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci spin_lock(&conn->session->back_lock); 12878c2ecf20Sopenharmony_ci rc = __iscsi_complete_pdu(conn, hdr, data, datalen); 12888c2ecf20Sopenharmony_ci spin_unlock(&conn->session->back_lock); 12898c2ecf20Sopenharmony_ci return rc; 12908c2ecf20Sopenharmony_ci} 12918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_complete_pdu); 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ciint iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) 12948c2ecf20Sopenharmony_ci{ 12958c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 12968c2ecf20Sopenharmony_ci int age = 0, i = 0; 12978c2ecf20Sopenharmony_ci 12988c2ecf20Sopenharmony_ci if (itt == RESERVED_ITT) 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci if (session->tt->parse_pdu_itt) 13028c2ecf20Sopenharmony_ci session->tt->parse_pdu_itt(conn, itt, &i, &age); 13038c2ecf20Sopenharmony_ci else { 13048c2ecf20Sopenharmony_ci i = get_itt(itt); 13058c2ecf20Sopenharmony_ci age = ((__force u32)itt >> ISCSI_AGE_SHIFT) & ISCSI_AGE_MASK; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (age != session->age) { 13098c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 13108c2ecf20Sopenharmony_ci "received itt %x expected session age (%x)\n", 13118c2ecf20Sopenharmony_ci (__force u32)itt, session->age); 13128c2ecf20Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 13138c2ecf20Sopenharmony_ci } 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci if (i >= session->cmds_max) { 13168c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 13178c2ecf20Sopenharmony_ci "received invalid itt index %u (max cmds " 13188c2ecf20Sopenharmony_ci "%u.\n", i, session->cmds_max); 13198c2ecf20Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 13208c2ecf20Sopenharmony_ci } 13218c2ecf20Sopenharmony_ci return 0; 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_verify_itt); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci/** 13268c2ecf20Sopenharmony_ci * iscsi_itt_to_ctask - look up ctask by itt 13278c2ecf20Sopenharmony_ci * @conn: iscsi connection 13288c2ecf20Sopenharmony_ci * @itt: itt 13298c2ecf20Sopenharmony_ci * 13308c2ecf20Sopenharmony_ci * This should be used for cmd tasks. 13318c2ecf20Sopenharmony_ci * 13328c2ecf20Sopenharmony_ci * The session back_lock must be held. 13338c2ecf20Sopenharmony_ci */ 13348c2ecf20Sopenharmony_cistruct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci struct iscsi_task *task; 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (iscsi_verify_itt(conn, itt)) 13398c2ecf20Sopenharmony_ci return NULL; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci task = iscsi_itt_to_task(conn, itt); 13428c2ecf20Sopenharmony_ci if (!task || !task->sc) 13438c2ecf20Sopenharmony_ci return NULL; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (task->sc->SCp.phase != conn->session->age) { 13468c2ecf20Sopenharmony_ci iscsi_session_printk(KERN_ERR, conn->session, 13478c2ecf20Sopenharmony_ci "task's session age %d, expected %d\n", 13488c2ecf20Sopenharmony_ci task->sc->SCp.phase, conn->session->age); 13498c2ecf20Sopenharmony_ci return NULL; 13508c2ecf20Sopenharmony_ci } 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci return task; 13538c2ecf20Sopenharmony_ci} 13548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_itt_to_ctask); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_civoid iscsi_session_failure(struct iscsi_session *session, 13578c2ecf20Sopenharmony_ci enum iscsi_err err) 13588c2ecf20Sopenharmony_ci{ 13598c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 13628c2ecf20Sopenharmony_ci conn = session->leadconn; 13638c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE || !conn) { 13648c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 13658c2ecf20Sopenharmony_ci return; 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 13698c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 13708c2ecf20Sopenharmony_ci /* 13718c2ecf20Sopenharmony_ci * if the host is being removed bypass the connection 13728c2ecf20Sopenharmony_ci * recovery initialization because we are going to kill 13738c2ecf20Sopenharmony_ci * the session. 13748c2ecf20Sopenharmony_ci */ 13758c2ecf20Sopenharmony_ci if (err == ISCSI_ERR_INVALID_HOST) 13768c2ecf20Sopenharmony_ci iscsi_conn_error_event(conn->cls_conn, err); 13778c2ecf20Sopenharmony_ci else 13788c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, err); 13798c2ecf20Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 13808c2ecf20Sopenharmony_ci} 13818c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_failure); 13828c2ecf20Sopenharmony_ci 13838c2ecf20Sopenharmony_cistatic bool iscsi_set_conn_failed(struct iscsi_conn *conn) 13848c2ecf20Sopenharmony_ci{ 13858c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 13868c2ecf20Sopenharmony_ci 13878c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_FAILED) 13888c2ecf20Sopenharmony_ci return false; 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci if (conn->stop_stage == 0) 13918c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_FAILED; 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 13948c2ecf20Sopenharmony_ci set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); 13958c2ecf20Sopenharmony_ci return true; 13968c2ecf20Sopenharmony_ci} 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_civoid iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) 13998c2ecf20Sopenharmony_ci{ 14008c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 14018c2ecf20Sopenharmony_ci bool needs_evt; 14028c2ecf20Sopenharmony_ci 14038c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 14048c2ecf20Sopenharmony_ci needs_evt = iscsi_set_conn_failed(conn); 14058c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (needs_evt) 14088c2ecf20Sopenharmony_ci iscsi_conn_error_event(conn->cls_conn, err); 14098c2ecf20Sopenharmony_ci} 14108c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_failure); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_cistatic int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn) 14138c2ecf20Sopenharmony_ci{ 14148c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci /* 14178c2ecf20Sopenharmony_ci * Check for iSCSI window and take care of CmdSN wrap-around 14188c2ecf20Sopenharmony_ci */ 14198c2ecf20Sopenharmony_ci if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) { 14208c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "iSCSI CmdSN closed. ExpCmdSn " 14218c2ecf20Sopenharmony_ci "%u MaxCmdSN %u CmdSN %u/%u\n", 14228c2ecf20Sopenharmony_ci session->exp_cmdsn, session->max_cmdsn, 14238c2ecf20Sopenharmony_ci session->cmdsn, session->queued_cmdsn); 14248c2ecf20Sopenharmony_ci return -ENOSPC; 14258c2ecf20Sopenharmony_ci } 14268c2ecf20Sopenharmony_ci return 0; 14278c2ecf20Sopenharmony_ci} 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_cistatic int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, 14308c2ecf20Sopenharmony_ci bool was_requeue) 14318c2ecf20Sopenharmony_ci{ 14328c2ecf20Sopenharmony_ci int rc; 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->back_lock); 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci if (!conn->task) { 14378c2ecf20Sopenharmony_ci /* Take a ref so we can access it after xmit_task() */ 14388c2ecf20Sopenharmony_ci __iscsi_get_task(task); 14398c2ecf20Sopenharmony_ci } else { 14408c2ecf20Sopenharmony_ci /* Already have a ref from when we failed to send it last call */ 14418c2ecf20Sopenharmony_ci conn->task = NULL; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_ci /* 14458c2ecf20Sopenharmony_ci * If this was a requeue for a R2T we have an extra ref on the task in 14468c2ecf20Sopenharmony_ci * case a bad target sends a cmd rsp before we have handled the task. 14478c2ecf20Sopenharmony_ci */ 14488c2ecf20Sopenharmony_ci if (was_requeue) 14498c2ecf20Sopenharmony_ci __iscsi_put_task(task); 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_ci /* 14528c2ecf20Sopenharmony_ci * Do this after dropping the extra ref because if this was a requeue 14538c2ecf20Sopenharmony_ci * it's removed from that list and cleanup_queued_task would miss it. 14548c2ecf20Sopenharmony_ci */ 14558c2ecf20Sopenharmony_ci if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { 14568c2ecf20Sopenharmony_ci /* 14578c2ecf20Sopenharmony_ci * Save the task and ref in case we weren't cleaning up this 14588c2ecf20Sopenharmony_ci * task and get woken up again. 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci conn->task = task; 14618c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 14628c2ecf20Sopenharmony_ci return -ENODATA; 14638c2ecf20Sopenharmony_ci } 14648c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 14678c2ecf20Sopenharmony_ci rc = conn->session->tt->xmit_task(task); 14688c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 14698c2ecf20Sopenharmony_ci if (!rc) { 14708c2ecf20Sopenharmony_ci /* done with this task */ 14718c2ecf20Sopenharmony_ci task->last_xfer = jiffies; 14728c2ecf20Sopenharmony_ci } 14738c2ecf20Sopenharmony_ci /* regular RX path uses back_lock */ 14748c2ecf20Sopenharmony_ci spin_lock(&conn->session->back_lock); 14758c2ecf20Sopenharmony_ci if (rc && task->state == ISCSI_TASK_RUNNING) { 14768c2ecf20Sopenharmony_ci /* 14778c2ecf20Sopenharmony_ci * get an extra ref that is released next time we access it 14788c2ecf20Sopenharmony_ci * as conn->task above. 14798c2ecf20Sopenharmony_ci */ 14808c2ecf20Sopenharmony_ci __iscsi_get_task(task); 14818c2ecf20Sopenharmony_ci conn->task = task; 14828c2ecf20Sopenharmony_ci } 14838c2ecf20Sopenharmony_ci 14848c2ecf20Sopenharmony_ci __iscsi_put_task(task); 14858c2ecf20Sopenharmony_ci spin_unlock(&conn->session->back_lock); 14868c2ecf20Sopenharmony_ci return rc; 14878c2ecf20Sopenharmony_ci} 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci/** 14908c2ecf20Sopenharmony_ci * iscsi_requeue_task - requeue task to run from session workqueue 14918c2ecf20Sopenharmony_ci * @task: task to requeue 14928c2ecf20Sopenharmony_ci * 14938c2ecf20Sopenharmony_ci * Callers must have taken a ref to the task that is going to be requeued. 14948c2ecf20Sopenharmony_ci */ 14958c2ecf20Sopenharmony_civoid iscsi_requeue_task(struct iscsi_task *task) 14968c2ecf20Sopenharmony_ci{ 14978c2ecf20Sopenharmony_ci struct iscsi_conn *conn = task->conn; 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci /* 15008c2ecf20Sopenharmony_ci * this may be on the requeue list already if the xmit_task callout 15018c2ecf20Sopenharmony_ci * is handling the r2ts while we are adding new ones 15028c2ecf20Sopenharmony_ci */ 15038c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 15048c2ecf20Sopenharmony_ci if (list_empty(&task->running)) { 15058c2ecf20Sopenharmony_ci list_add_tail(&task->running, &conn->requeue); 15068c2ecf20Sopenharmony_ci } else { 15078c2ecf20Sopenharmony_ci /* 15088c2ecf20Sopenharmony_ci * Don't need the extra ref since it's already requeued and 15098c2ecf20Sopenharmony_ci * has a ref. 15108c2ecf20Sopenharmony_ci */ 15118c2ecf20Sopenharmony_ci iscsi_put_task(task); 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci iscsi_conn_queue_work(conn); 15148c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 15158c2ecf20Sopenharmony_ci} 15168c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_requeue_task); 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci/** 15198c2ecf20Sopenharmony_ci * iscsi_data_xmit - xmit any command into the scheduled connection 15208c2ecf20Sopenharmony_ci * @conn: iscsi connection 15218c2ecf20Sopenharmony_ci * 15228c2ecf20Sopenharmony_ci * Notes: 15238c2ecf20Sopenharmony_ci * The function can return -EAGAIN in which case the caller must 15248c2ecf20Sopenharmony_ci * re-schedule it again later or recover. '0' return code means 15258c2ecf20Sopenharmony_ci * successful xmit. 15268c2ecf20Sopenharmony_ci **/ 15278c2ecf20Sopenharmony_cistatic int iscsi_data_xmit(struct iscsi_conn *conn) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct iscsi_task *task; 15308c2ecf20Sopenharmony_ci int rc = 0; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 15338c2ecf20Sopenharmony_ci if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { 15348c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n"); 15358c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 15368c2ecf20Sopenharmony_ci return -ENODATA; 15378c2ecf20Sopenharmony_ci } 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci if (conn->task) { 15408c2ecf20Sopenharmony_ci rc = iscsi_xmit_task(conn, conn->task, false); 15418c2ecf20Sopenharmony_ci if (rc) 15428c2ecf20Sopenharmony_ci goto done; 15438c2ecf20Sopenharmony_ci } 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* 15468c2ecf20Sopenharmony_ci * process mgmt pdus like nops before commands since we should 15478c2ecf20Sopenharmony_ci * only have one nop-out as a ping from us and targets should not 15488c2ecf20Sopenharmony_ci * overflow us with nop-ins 15498c2ecf20Sopenharmony_ci */ 15508c2ecf20Sopenharmony_cicheck_mgmt: 15518c2ecf20Sopenharmony_ci while (!list_empty(&conn->mgmtqueue)) { 15528c2ecf20Sopenharmony_ci task = list_entry(conn->mgmtqueue.next, struct iscsi_task, 15538c2ecf20Sopenharmony_ci running); 15548c2ecf20Sopenharmony_ci list_del_init(&task->running); 15558c2ecf20Sopenharmony_ci if (iscsi_prep_mgmt_task(conn, task)) { 15568c2ecf20Sopenharmony_ci /* regular RX path uses back_lock */ 15578c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->back_lock); 15588c2ecf20Sopenharmony_ci __iscsi_put_task(task); 15598c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 15608c2ecf20Sopenharmony_ci continue; 15618c2ecf20Sopenharmony_ci } 15628c2ecf20Sopenharmony_ci rc = iscsi_xmit_task(conn, task, false); 15638c2ecf20Sopenharmony_ci if (rc) 15648c2ecf20Sopenharmony_ci goto done; 15658c2ecf20Sopenharmony_ci } 15668c2ecf20Sopenharmony_ci 15678c2ecf20Sopenharmony_ci /* process pending command queue */ 15688c2ecf20Sopenharmony_ci while (!list_empty(&conn->cmdqueue)) { 15698c2ecf20Sopenharmony_ci task = list_entry(conn->cmdqueue.next, struct iscsi_task, 15708c2ecf20Sopenharmony_ci running); 15718c2ecf20Sopenharmony_ci list_del_init(&task->running); 15728c2ecf20Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { 15738c2ecf20Sopenharmony_ci fail_scsi_task(task, DID_IMM_RETRY); 15748c2ecf20Sopenharmony_ci continue; 15758c2ecf20Sopenharmony_ci } 15768c2ecf20Sopenharmony_ci rc = iscsi_prep_scsi_cmd_pdu(task); 15778c2ecf20Sopenharmony_ci if (rc) { 15788c2ecf20Sopenharmony_ci if (rc == -ENOMEM || rc == -EACCES) 15798c2ecf20Sopenharmony_ci fail_scsi_task(task, DID_IMM_RETRY); 15808c2ecf20Sopenharmony_ci else 15818c2ecf20Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 15828c2ecf20Sopenharmony_ci continue; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci rc = iscsi_xmit_task(conn, task, false); 15858c2ecf20Sopenharmony_ci if (rc) 15868c2ecf20Sopenharmony_ci goto done; 15878c2ecf20Sopenharmony_ci /* 15888c2ecf20Sopenharmony_ci * we could continuously get new task requests so 15898c2ecf20Sopenharmony_ci * we need to check the mgmt queue for nops that need to 15908c2ecf20Sopenharmony_ci * be sent to aviod starvation 15918c2ecf20Sopenharmony_ci */ 15928c2ecf20Sopenharmony_ci if (!list_empty(&conn->mgmtqueue)) 15938c2ecf20Sopenharmony_ci goto check_mgmt; 15948c2ecf20Sopenharmony_ci } 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ci while (!list_empty(&conn->requeue)) { 15978c2ecf20Sopenharmony_ci /* 15988c2ecf20Sopenharmony_ci * we always do fastlogout - conn stop code will clean up. 15998c2ecf20Sopenharmony_ci */ 16008c2ecf20Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) 16018c2ecf20Sopenharmony_ci break; 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci task = list_entry(conn->requeue.next, struct iscsi_task, 16048c2ecf20Sopenharmony_ci running); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) 16078c2ecf20Sopenharmony_ci break; 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci list_del_init(&task->running); 16108c2ecf20Sopenharmony_ci rc = iscsi_xmit_task(conn, task, true); 16118c2ecf20Sopenharmony_ci if (rc) 16128c2ecf20Sopenharmony_ci goto done; 16138c2ecf20Sopenharmony_ci if (!list_empty(&conn->mgmtqueue)) 16148c2ecf20Sopenharmony_ci goto check_mgmt; 16158c2ecf20Sopenharmony_ci } 16168c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 16178c2ecf20Sopenharmony_ci return -ENODATA; 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cidone: 16208c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 16218c2ecf20Sopenharmony_ci return rc; 16228c2ecf20Sopenharmony_ci} 16238c2ecf20Sopenharmony_ci 16248c2ecf20Sopenharmony_cistatic void iscsi_xmitworker(struct work_struct *work) 16258c2ecf20Sopenharmony_ci{ 16268c2ecf20Sopenharmony_ci struct iscsi_conn *conn = 16278c2ecf20Sopenharmony_ci container_of(work, struct iscsi_conn, xmitwork); 16288c2ecf20Sopenharmony_ci int rc; 16298c2ecf20Sopenharmony_ci /* 16308c2ecf20Sopenharmony_ci * serialize Xmit worker on a per-connection basis. 16318c2ecf20Sopenharmony_ci */ 16328c2ecf20Sopenharmony_ci do { 16338c2ecf20Sopenharmony_ci rc = iscsi_data_xmit(conn); 16348c2ecf20Sopenharmony_ci } while (rc >= 0 || rc == -EAGAIN); 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, 16388c2ecf20Sopenharmony_ci struct scsi_cmnd *sc) 16398c2ecf20Sopenharmony_ci{ 16408c2ecf20Sopenharmony_ci struct iscsi_task *task; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci if (!kfifo_out(&conn->session->cmdpool.queue, 16438c2ecf20Sopenharmony_ci (void *) &task, sizeof(void *))) 16448c2ecf20Sopenharmony_ci return NULL; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci sc->SCp.phase = conn->session->age; 16478c2ecf20Sopenharmony_ci sc->SCp.ptr = (char *) task; 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_ci refcount_set(&task->refcount, 1); 16508c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_PENDING; 16518c2ecf20Sopenharmony_ci task->conn = conn; 16528c2ecf20Sopenharmony_ci task->sc = sc; 16538c2ecf20Sopenharmony_ci task->have_checked_conn = false; 16548c2ecf20Sopenharmony_ci task->last_timeout = jiffies; 16558c2ecf20Sopenharmony_ci task->last_xfer = jiffies; 16568c2ecf20Sopenharmony_ci task->protected = false; 16578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&task->running); 16588c2ecf20Sopenharmony_ci return task; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_cienum { 16628c2ecf20Sopenharmony_ci FAILURE_BAD_HOST = 1, 16638c2ecf20Sopenharmony_ci FAILURE_SESSION_FAILED, 16648c2ecf20Sopenharmony_ci FAILURE_SESSION_FREED, 16658c2ecf20Sopenharmony_ci FAILURE_WINDOW_CLOSED, 16668c2ecf20Sopenharmony_ci FAILURE_OOM, 16678c2ecf20Sopenharmony_ci FAILURE_SESSION_TERMINATE, 16688c2ecf20Sopenharmony_ci FAILURE_SESSION_IN_RECOVERY, 16698c2ecf20Sopenharmony_ci FAILURE_SESSION_RECOVERY_TIMEOUT, 16708c2ecf20Sopenharmony_ci FAILURE_SESSION_LOGGING_OUT, 16718c2ecf20Sopenharmony_ci FAILURE_SESSION_NOT_READY, 16728c2ecf20Sopenharmony_ci}; 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ciint iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) 16758c2ecf20Sopenharmony_ci{ 16768c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 16778c2ecf20Sopenharmony_ci struct iscsi_host *ihost; 16788c2ecf20Sopenharmony_ci int reason = 0; 16798c2ecf20Sopenharmony_ci struct iscsi_session *session; 16808c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 16818c2ecf20Sopenharmony_ci struct iscsi_task *task = NULL; 16828c2ecf20Sopenharmony_ci 16838c2ecf20Sopenharmony_ci sc->result = 0; 16848c2ecf20Sopenharmony_ci sc->SCp.ptr = NULL; 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci ihost = shost_priv(host); 16878c2ecf20Sopenharmony_ci 16888c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 16898c2ecf20Sopenharmony_ci session = cls_session->dd_data; 16908c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 16918c2ecf20Sopenharmony_ci 16928c2ecf20Sopenharmony_ci reason = iscsi_session_chkready(cls_session); 16938c2ecf20Sopenharmony_ci if (reason) { 16948c2ecf20Sopenharmony_ci sc->result = reason; 16958c2ecf20Sopenharmony_ci goto fault; 16968c2ecf20Sopenharmony_ci } 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 16998c2ecf20Sopenharmony_ci /* 17008c2ecf20Sopenharmony_ci * to handle the race between when we set the recovery state 17018c2ecf20Sopenharmony_ci * and block the session we requeue here (commands could 17028c2ecf20Sopenharmony_ci * be entering our queuecommand while a block is starting 17038c2ecf20Sopenharmony_ci * up because the block code is not locked) 17048c2ecf20Sopenharmony_ci */ 17058c2ecf20Sopenharmony_ci switch (session->state) { 17068c2ecf20Sopenharmony_ci case ISCSI_STATE_FAILED: 17078c2ecf20Sopenharmony_ci /* 17088c2ecf20Sopenharmony_ci * cmds should fail during shutdown, if the session 17098c2ecf20Sopenharmony_ci * state is bad, allowing completion to happen 17108c2ecf20Sopenharmony_ci */ 17118c2ecf20Sopenharmony_ci if (unlikely(system_state != SYSTEM_RUNNING)) { 17128c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_FAILED; 17138c2ecf20Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 17148c2ecf20Sopenharmony_ci break; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci fallthrough; 17178c2ecf20Sopenharmony_ci case ISCSI_STATE_IN_RECOVERY: 17188c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_IN_RECOVERY; 17198c2ecf20Sopenharmony_ci sc->result = DID_IMM_RETRY << 16; 17208c2ecf20Sopenharmony_ci break; 17218c2ecf20Sopenharmony_ci case ISCSI_STATE_LOGGING_OUT: 17228c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_LOGGING_OUT; 17238c2ecf20Sopenharmony_ci sc->result = DID_IMM_RETRY << 16; 17248c2ecf20Sopenharmony_ci break; 17258c2ecf20Sopenharmony_ci case ISCSI_STATE_RECOVERY_FAILED: 17268c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_RECOVERY_TIMEOUT; 17278c2ecf20Sopenharmony_ci sc->result = DID_TRANSPORT_FAILFAST << 16; 17288c2ecf20Sopenharmony_ci break; 17298c2ecf20Sopenharmony_ci case ISCSI_STATE_TERMINATE: 17308c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_TERMINATE; 17318c2ecf20Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 17328c2ecf20Sopenharmony_ci break; 17338c2ecf20Sopenharmony_ci default: 17348c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_FREED; 17358c2ecf20Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 17368c2ecf20Sopenharmony_ci } 17378c2ecf20Sopenharmony_ci goto fault; 17388c2ecf20Sopenharmony_ci } 17398c2ecf20Sopenharmony_ci 17408c2ecf20Sopenharmony_ci conn = session->leadconn; 17418c2ecf20Sopenharmony_ci if (!conn) { 17428c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_FREED; 17438c2ecf20Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 17448c2ecf20Sopenharmony_ci goto fault; 17458c2ecf20Sopenharmony_ci } 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) { 17488c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_IN_RECOVERY; 17498c2ecf20Sopenharmony_ci sc->result = DID_REQUEUE << 16; 17508c2ecf20Sopenharmony_ci goto fault; 17518c2ecf20Sopenharmony_ci } 17528c2ecf20Sopenharmony_ci 17538c2ecf20Sopenharmony_ci if (iscsi_check_cmdsn_window_closed(conn)) { 17548c2ecf20Sopenharmony_ci reason = FAILURE_WINDOW_CLOSED; 17558c2ecf20Sopenharmony_ci goto reject; 17568c2ecf20Sopenharmony_ci } 17578c2ecf20Sopenharmony_ci 17588c2ecf20Sopenharmony_ci task = iscsi_alloc_task(conn, sc); 17598c2ecf20Sopenharmony_ci if (!task) { 17608c2ecf20Sopenharmony_ci reason = FAILURE_OOM; 17618c2ecf20Sopenharmony_ci goto reject; 17628c2ecf20Sopenharmony_ci } 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_ci if (!ihost->workq) { 17658c2ecf20Sopenharmony_ci reason = iscsi_prep_scsi_cmd_pdu(task); 17668c2ecf20Sopenharmony_ci if (reason) { 17678c2ecf20Sopenharmony_ci if (reason == -ENOMEM || reason == -EACCES) { 17688c2ecf20Sopenharmony_ci reason = FAILURE_OOM; 17698c2ecf20Sopenharmony_ci goto prepd_reject; 17708c2ecf20Sopenharmony_ci } else { 17718c2ecf20Sopenharmony_ci sc->result = DID_ABORT << 16; 17728c2ecf20Sopenharmony_ci goto prepd_fault; 17738c2ecf20Sopenharmony_ci } 17748c2ecf20Sopenharmony_ci } 17758c2ecf20Sopenharmony_ci if (session->tt->xmit_task(task)) { 17768c2ecf20Sopenharmony_ci session->cmdsn--; 17778c2ecf20Sopenharmony_ci reason = FAILURE_SESSION_NOT_READY; 17788c2ecf20Sopenharmony_ci goto prepd_reject; 17798c2ecf20Sopenharmony_ci } 17808c2ecf20Sopenharmony_ci } else { 17818c2ecf20Sopenharmony_ci list_add_tail(&task->running, &conn->cmdqueue); 17828c2ecf20Sopenharmony_ci iscsi_conn_queue_work(conn); 17838c2ecf20Sopenharmony_ci } 17848c2ecf20Sopenharmony_ci 17858c2ecf20Sopenharmony_ci session->queued_cmdsn++; 17868c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 17878c2ecf20Sopenharmony_ci return 0; 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ciprepd_reject: 17908c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 17918c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); 17928c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 17938c2ecf20Sopenharmony_cireject: 17948c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 17958c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", 17968c2ecf20Sopenharmony_ci sc->cmnd[0], reason); 17978c2ecf20Sopenharmony_ci return SCSI_MLQUEUE_TARGET_BUSY; 17988c2ecf20Sopenharmony_ci 17998c2ecf20Sopenharmony_ciprepd_fault: 18008c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 18018c2ecf20Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); 18028c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 18038c2ecf20Sopenharmony_cifault: 18048c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 18058c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", 18068c2ecf20Sopenharmony_ci sc->cmnd[0], reason); 18078c2ecf20Sopenharmony_ci scsi_set_resid(sc, scsi_bufflen(sc)); 18088c2ecf20Sopenharmony_ci sc->scsi_done(sc); 18098c2ecf20Sopenharmony_ci return 0; 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_queuecommand); 18128c2ecf20Sopenharmony_ci 18138c2ecf20Sopenharmony_ciint iscsi_target_alloc(struct scsi_target *starget) 18148c2ecf20Sopenharmony_ci{ 18158c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session = starget_to_session(starget); 18168c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci starget->can_queue = session->scsi_cmds_max; 18198c2ecf20Sopenharmony_ci return 0; 18208c2ecf20Sopenharmony_ci} 18218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_target_alloc); 18228c2ecf20Sopenharmony_ci 18238c2ecf20Sopenharmony_cistatic void iscsi_tmf_timedout(struct timer_list *t) 18248c2ecf20Sopenharmony_ci{ 18258c2ecf20Sopenharmony_ci struct iscsi_session *session = from_timer(session, t, tmf_timer); 18268c2ecf20Sopenharmony_ci 18278c2ecf20Sopenharmony_ci spin_lock(&session->frwd_lock); 18288c2ecf20Sopenharmony_ci if (session->tmf_state == TMF_QUEUED) { 18298c2ecf20Sopenharmony_ci session->tmf_state = TMF_TIMEDOUT; 18308c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "tmf timedout\n"); 18318c2ecf20Sopenharmony_ci /* unblock eh_abort() */ 18328c2ecf20Sopenharmony_ci wake_up(&session->ehwait); 18338c2ecf20Sopenharmony_ci } 18348c2ecf20Sopenharmony_ci spin_unlock(&session->frwd_lock); 18358c2ecf20Sopenharmony_ci} 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_cistatic int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, 18388c2ecf20Sopenharmony_ci struct iscsi_tm *hdr, int age, 18398c2ecf20Sopenharmony_ci int timeout) 18408c2ecf20Sopenharmony_ci __must_hold(&session->frwd_lock) 18418c2ecf20Sopenharmony_ci{ 18428c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 18438c2ecf20Sopenharmony_ci struct iscsi_task *task; 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_ci task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, 18468c2ecf20Sopenharmony_ci NULL, 0); 18478c2ecf20Sopenharmony_ci if (!task) { 18488c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 18498c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n"); 18508c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); 18518c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 18528c2ecf20Sopenharmony_ci return -EPERM; 18538c2ecf20Sopenharmony_ci } 18548c2ecf20Sopenharmony_ci conn->tmfcmd_pdus_cnt++; 18558c2ecf20Sopenharmony_ci session->tmf_timer.expires = timeout * HZ + jiffies; 18568c2ecf20Sopenharmony_ci add_timer(&session->tmf_timer); 18578c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "tmf set timeout\n"); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 18608c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci /* 18638c2ecf20Sopenharmony_ci * block eh thread until: 18648c2ecf20Sopenharmony_ci * 18658c2ecf20Sopenharmony_ci * 1) tmf response 18668c2ecf20Sopenharmony_ci * 2) tmf timeout 18678c2ecf20Sopenharmony_ci * 3) session is terminated or restarted or userspace has 18688c2ecf20Sopenharmony_ci * given up on recovery 18698c2ecf20Sopenharmony_ci */ 18708c2ecf20Sopenharmony_ci wait_event_interruptible(session->ehwait, age != session->age || 18718c2ecf20Sopenharmony_ci session->state != ISCSI_STATE_LOGGED_IN || 18728c2ecf20Sopenharmony_ci session->tmf_state != TMF_QUEUED); 18738c2ecf20Sopenharmony_ci if (signal_pending(current)) 18748c2ecf20Sopenharmony_ci flush_signals(current); 18758c2ecf20Sopenharmony_ci del_timer_sync(&session->tmf_timer); 18768c2ecf20Sopenharmony_ci 18778c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 18788c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 18798c2ecf20Sopenharmony_ci /* if the session drops it will clean up the task */ 18808c2ecf20Sopenharmony_ci if (age != session->age || 18818c2ecf20Sopenharmony_ci session->state != ISCSI_STATE_LOGGED_IN) 18828c2ecf20Sopenharmony_ci return -ENOTCONN; 18838c2ecf20Sopenharmony_ci return 0; 18848c2ecf20Sopenharmony_ci} 18858c2ecf20Sopenharmony_ci 18868c2ecf20Sopenharmony_ci/* 18878c2ecf20Sopenharmony_ci * Fail commands. session frwd lock held and xmit thread flushed. 18888c2ecf20Sopenharmony_ci */ 18898c2ecf20Sopenharmony_cistatic void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) 18908c2ecf20Sopenharmony_ci{ 18918c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 18928c2ecf20Sopenharmony_ci struct iscsi_task *task; 18938c2ecf20Sopenharmony_ci int i; 18948c2ecf20Sopenharmony_ci 18958c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 18968c2ecf20Sopenharmony_ci for (i = 0; i < session->cmds_max; i++) { 18978c2ecf20Sopenharmony_ci task = session->cmds[i]; 18988c2ecf20Sopenharmony_ci if (!task->sc || task->state == ISCSI_TASK_FREE) 18998c2ecf20Sopenharmony_ci continue; 19008c2ecf20Sopenharmony_ci 19018c2ecf20Sopenharmony_ci if (lun != -1 && lun != task->sc->device->lun) 19028c2ecf20Sopenharmony_ci continue; 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci __iscsi_get_task(task); 19058c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 19068c2ecf20Sopenharmony_ci 19078c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, 19088c2ecf20Sopenharmony_ci "failing sc %p itt 0x%x state %d\n", 19098c2ecf20Sopenharmony_ci task->sc, task->itt, task->state); 19108c2ecf20Sopenharmony_ci fail_scsi_task(task, error); 19118c2ecf20Sopenharmony_ci 19128c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 19138c2ecf20Sopenharmony_ci iscsi_put_task(task); 19148c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 19158c2ecf20Sopenharmony_ci 19168c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 19208c2ecf20Sopenharmony_ci} 19218c2ecf20Sopenharmony_ci 19228c2ecf20Sopenharmony_ci/** 19238c2ecf20Sopenharmony_ci * iscsi_suspend_queue - suspend iscsi_queuecommand 19248c2ecf20Sopenharmony_ci * @conn: iscsi conn to stop queueing IO on 19258c2ecf20Sopenharmony_ci * 19268c2ecf20Sopenharmony_ci * This grabs the session frwd_lock to make sure no one is in 19278c2ecf20Sopenharmony_ci * xmit_task/queuecommand, and then sets suspend to prevent 19288c2ecf20Sopenharmony_ci * new commands from being queued. This only needs to be called 19298c2ecf20Sopenharmony_ci * by offload drivers that need to sync a path like ep disconnect 19308c2ecf20Sopenharmony_ci * with the iscsi_queuecommand/xmit_task. To start IO again libiscsi 19318c2ecf20Sopenharmony_ci * will call iscsi_start_tx and iscsi_unblock_session when in FFP. 19328c2ecf20Sopenharmony_ci */ 19338c2ecf20Sopenharmony_civoid iscsi_suspend_queue(struct iscsi_conn *conn) 19348c2ecf20Sopenharmony_ci{ 19358c2ecf20Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 19368c2ecf20Sopenharmony_ci set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 19378c2ecf20Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 19388c2ecf20Sopenharmony_ci} 19398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_suspend_queue); 19408c2ecf20Sopenharmony_ci 19418c2ecf20Sopenharmony_ci/** 19428c2ecf20Sopenharmony_ci * iscsi_suspend_tx - suspend iscsi_data_xmit 19438c2ecf20Sopenharmony_ci * @conn: iscsi conn tp stop processing IO on. 19448c2ecf20Sopenharmony_ci * 19458c2ecf20Sopenharmony_ci * This function sets the suspend bit to prevent iscsi_data_xmit 19468c2ecf20Sopenharmony_ci * from sending new IO, and if work is queued on the xmit thread 19478c2ecf20Sopenharmony_ci * it will wait for it to be completed. 19488c2ecf20Sopenharmony_ci */ 19498c2ecf20Sopenharmony_civoid iscsi_suspend_tx(struct iscsi_conn *conn) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 19528c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 19538c2ecf20Sopenharmony_ci 19548c2ecf20Sopenharmony_ci set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 19558c2ecf20Sopenharmony_ci if (ihost->workq) 19568c2ecf20Sopenharmony_ci flush_workqueue(ihost->workq); 19578c2ecf20Sopenharmony_ci} 19588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_suspend_tx); 19598c2ecf20Sopenharmony_ci 19608c2ecf20Sopenharmony_cistatic void iscsi_start_tx(struct iscsi_conn *conn) 19618c2ecf20Sopenharmony_ci{ 19628c2ecf20Sopenharmony_ci clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 19638c2ecf20Sopenharmony_ci iscsi_conn_queue_work(conn); 19648c2ecf20Sopenharmony_ci} 19658c2ecf20Sopenharmony_ci 19668c2ecf20Sopenharmony_ci/* 19678c2ecf20Sopenharmony_ci * We want to make sure a ping is in flight. It has timed out. 19688c2ecf20Sopenharmony_ci * And we are not busy processing a pdu that is making 19698c2ecf20Sopenharmony_ci * progress but got started before the ping and is taking a while 19708c2ecf20Sopenharmony_ci * to complete so the ping is just stuck behind it in a queue. 19718c2ecf20Sopenharmony_ci */ 19728c2ecf20Sopenharmony_cistatic int iscsi_has_ping_timed_out(struct iscsi_conn *conn) 19738c2ecf20Sopenharmony_ci{ 19748c2ecf20Sopenharmony_ci if (READ_ONCE(conn->ping_task) && 19758c2ecf20Sopenharmony_ci time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + 19768c2ecf20Sopenharmony_ci (conn->ping_timeout * HZ), jiffies)) 19778c2ecf20Sopenharmony_ci return 1; 19788c2ecf20Sopenharmony_ci else 19798c2ecf20Sopenharmony_ci return 0; 19808c2ecf20Sopenharmony_ci} 19818c2ecf20Sopenharmony_ci 19828c2ecf20Sopenharmony_cienum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) 19838c2ecf20Sopenharmony_ci{ 19848c2ecf20Sopenharmony_ci enum blk_eh_timer_return rc = BLK_EH_DONE; 19858c2ecf20Sopenharmony_ci struct iscsi_task *task = NULL, *running_task; 19868c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 19878c2ecf20Sopenharmony_ci struct iscsi_session *session; 19888c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 19898c2ecf20Sopenharmony_ci int i; 19908c2ecf20Sopenharmony_ci 19918c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 19928c2ecf20Sopenharmony_ci session = cls_session->dd_data; 19938c2ecf20Sopenharmony_ci 19948c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc); 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 19978c2ecf20Sopenharmony_ci spin_lock(&session->back_lock); 19988c2ecf20Sopenharmony_ci task = (struct iscsi_task *)sc->SCp.ptr; 19998c2ecf20Sopenharmony_ci if (!task) { 20008c2ecf20Sopenharmony_ci /* 20018c2ecf20Sopenharmony_ci * Raced with completion. Blk layer has taken ownership 20028c2ecf20Sopenharmony_ci * so let timeout code complete it now. 20038c2ecf20Sopenharmony_ci */ 20048c2ecf20Sopenharmony_ci rc = BLK_EH_DONE; 20058c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 20068c2ecf20Sopenharmony_ci goto done; 20078c2ecf20Sopenharmony_ci } 20088c2ecf20Sopenharmony_ci __iscsi_get_task(task); 20098c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 20128c2ecf20Sopenharmony_ci /* 20138c2ecf20Sopenharmony_ci * During shutdown, if session is prematurely disconnected, 20148c2ecf20Sopenharmony_ci * recovery won't happen and there will be hung cmds. Not 20158c2ecf20Sopenharmony_ci * handling cmds would trigger EH, also bad in this case. 20168c2ecf20Sopenharmony_ci * Instead, handle cmd, allow completion to happen and let 20178c2ecf20Sopenharmony_ci * upper layer to deal with the result. 20188c2ecf20Sopenharmony_ci */ 20198c2ecf20Sopenharmony_ci if (unlikely(system_state != SYSTEM_RUNNING)) { 20208c2ecf20Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 20218c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "sc on shutdown, handled\n"); 20228c2ecf20Sopenharmony_ci rc = BLK_EH_DONE; 20238c2ecf20Sopenharmony_ci goto done; 20248c2ecf20Sopenharmony_ci } 20258c2ecf20Sopenharmony_ci /* 20268c2ecf20Sopenharmony_ci * We are probably in the middle of iscsi recovery so let 20278c2ecf20Sopenharmony_ci * that complete and handle the error. 20288c2ecf20Sopenharmony_ci */ 20298c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 20308c2ecf20Sopenharmony_ci goto done; 20318c2ecf20Sopenharmony_ci } 20328c2ecf20Sopenharmony_ci 20338c2ecf20Sopenharmony_ci conn = session->leadconn; 20348c2ecf20Sopenharmony_ci if (!conn) { 20358c2ecf20Sopenharmony_ci /* In the middle of shuting down */ 20368c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 20378c2ecf20Sopenharmony_ci goto done; 20388c2ecf20Sopenharmony_ci } 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci /* 20418c2ecf20Sopenharmony_ci * If we have sent (at least queued to the network layer) a pdu or 20428c2ecf20Sopenharmony_ci * recvd one for the task since the last timeout ask for 20438c2ecf20Sopenharmony_ci * more time. If on the next timeout we have not made progress 20448c2ecf20Sopenharmony_ci * we can check if it is the task or connection when we send the 20458c2ecf20Sopenharmony_ci * nop as a ping. 20468c2ecf20Sopenharmony_ci */ 20478c2ecf20Sopenharmony_ci if (time_after(task->last_xfer, task->last_timeout)) { 20488c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "Command making progress. Asking " 20498c2ecf20Sopenharmony_ci "scsi-ml for more time to complete. " 20508c2ecf20Sopenharmony_ci "Last data xfer at %lu. Last timeout was at " 20518c2ecf20Sopenharmony_ci "%lu\n.", task->last_xfer, task->last_timeout); 20528c2ecf20Sopenharmony_ci task->have_checked_conn = false; 20538c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 20548c2ecf20Sopenharmony_ci goto done; 20558c2ecf20Sopenharmony_ci } 20568c2ecf20Sopenharmony_ci 20578c2ecf20Sopenharmony_ci if (!conn->recv_timeout && !conn->ping_timeout) 20588c2ecf20Sopenharmony_ci goto done; 20598c2ecf20Sopenharmony_ci /* 20608c2ecf20Sopenharmony_ci * if the ping timedout then we are in the middle of cleaning up 20618c2ecf20Sopenharmony_ci * and can let the iscsi eh handle it 20628c2ecf20Sopenharmony_ci */ 20638c2ecf20Sopenharmony_ci if (iscsi_has_ping_timed_out(conn)) { 20648c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 20658c2ecf20Sopenharmony_ci goto done; 20668c2ecf20Sopenharmony_ci } 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci spin_lock(&session->back_lock); 20698c2ecf20Sopenharmony_ci for (i = 0; i < conn->session->cmds_max; i++) { 20708c2ecf20Sopenharmony_ci running_task = conn->session->cmds[i]; 20718c2ecf20Sopenharmony_ci if (!running_task->sc || running_task == task || 20728c2ecf20Sopenharmony_ci running_task->state != ISCSI_TASK_RUNNING) 20738c2ecf20Sopenharmony_ci continue; 20748c2ecf20Sopenharmony_ci 20758c2ecf20Sopenharmony_ci /* 20768c2ecf20Sopenharmony_ci * Only check if cmds started before this one have made 20778c2ecf20Sopenharmony_ci * progress, or this could never fail 20788c2ecf20Sopenharmony_ci */ 20798c2ecf20Sopenharmony_ci if (time_after(running_task->sc->jiffies_at_alloc, 20808c2ecf20Sopenharmony_ci task->sc->jiffies_at_alloc)) 20818c2ecf20Sopenharmony_ci continue; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci if (time_after(running_task->last_xfer, task->last_timeout)) { 20848c2ecf20Sopenharmony_ci /* 20858c2ecf20Sopenharmony_ci * This task has not made progress, but a task 20868c2ecf20Sopenharmony_ci * started before us has transferred data since 20878c2ecf20Sopenharmony_ci * we started/last-checked. We could be queueing 20888c2ecf20Sopenharmony_ci * too many tasks or the LU is bad. 20898c2ecf20Sopenharmony_ci * 20908c2ecf20Sopenharmony_ci * If the device is bad the cmds ahead of us on 20918c2ecf20Sopenharmony_ci * other devs will complete, and this loop will 20928c2ecf20Sopenharmony_ci * eventually fail starting the scsi eh. 20938c2ecf20Sopenharmony_ci */ 20948c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "Command has not made progress " 20958c2ecf20Sopenharmony_ci "but commands ahead of it have. " 20968c2ecf20Sopenharmony_ci "Asking scsi-ml for more time to " 20978c2ecf20Sopenharmony_ci "complete. Our last xfer vs running task " 20988c2ecf20Sopenharmony_ci "last xfer %lu/%lu. Last check %lu.\n", 20998c2ecf20Sopenharmony_ci task->last_xfer, running_task->last_xfer, 21008c2ecf20Sopenharmony_ci task->last_timeout); 21018c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 21028c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 21038c2ecf20Sopenharmony_ci goto done; 21048c2ecf20Sopenharmony_ci } 21058c2ecf20Sopenharmony_ci } 21068c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci /* Assumes nop timeout is shorter than scsi cmd timeout */ 21098c2ecf20Sopenharmony_ci if (task->have_checked_conn) 21108c2ecf20Sopenharmony_ci goto done; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci /* 21138c2ecf20Sopenharmony_ci * Checking the transport already or nop from a cmd timeout still 21148c2ecf20Sopenharmony_ci * running 21158c2ecf20Sopenharmony_ci */ 21168c2ecf20Sopenharmony_ci if (READ_ONCE(conn->ping_task)) { 21178c2ecf20Sopenharmony_ci task->have_checked_conn = true; 21188c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 21198c2ecf20Sopenharmony_ci goto done; 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci /* Make sure there is a transport check done */ 21238c2ecf20Sopenharmony_ci iscsi_send_nopout(conn, NULL); 21248c2ecf20Sopenharmony_ci task->have_checked_conn = true; 21258c2ecf20Sopenharmony_ci rc = BLK_EH_RESET_TIMER; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_cidone: 21288c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci if (task) { 21318c2ecf20Sopenharmony_ci task->last_timeout = jiffies; 21328c2ecf20Sopenharmony_ci iscsi_put_task(task); 21338c2ecf20Sopenharmony_ci } 21348c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? 21358c2ecf20Sopenharmony_ci "timer reset" : "shutdown or nh"); 21368c2ecf20Sopenharmony_ci return rc; 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_cmd_timed_out); 21398c2ecf20Sopenharmony_ci 21408c2ecf20Sopenharmony_cistatic void iscsi_check_transport_timeouts(struct timer_list *t) 21418c2ecf20Sopenharmony_ci{ 21428c2ecf20Sopenharmony_ci struct iscsi_conn *conn = from_timer(conn, t, transport_timer); 21438c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 21448c2ecf20Sopenharmony_ci unsigned long recv_timeout, next_timeout = 0, last_recv; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci spin_lock(&session->frwd_lock); 21478c2ecf20Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) 21488c2ecf20Sopenharmony_ci goto done; 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci recv_timeout = conn->recv_timeout; 21518c2ecf20Sopenharmony_ci if (!recv_timeout) 21528c2ecf20Sopenharmony_ci goto done; 21538c2ecf20Sopenharmony_ci 21548c2ecf20Sopenharmony_ci recv_timeout *= HZ; 21558c2ecf20Sopenharmony_ci last_recv = conn->last_recv; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (iscsi_has_ping_timed_out(conn)) { 21588c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs " 21598c2ecf20Sopenharmony_ci "expired, recv timeout %d, last rx %lu, " 21608c2ecf20Sopenharmony_ci "last ping %lu, now %lu\n", 21618c2ecf20Sopenharmony_ci conn->ping_timeout, conn->recv_timeout, 21628c2ecf20Sopenharmony_ci last_recv, conn->last_ping, jiffies); 21638c2ecf20Sopenharmony_ci spin_unlock(&session->frwd_lock); 21648c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_NOP_TIMEDOUT); 21658c2ecf20Sopenharmony_ci return; 21668c2ecf20Sopenharmony_ci } 21678c2ecf20Sopenharmony_ci 21688c2ecf20Sopenharmony_ci if (time_before_eq(last_recv + recv_timeout, jiffies)) { 21698c2ecf20Sopenharmony_ci /* send a ping to try to provoke some traffic */ 21708c2ecf20Sopenharmony_ci ISCSI_DBG_CONN(conn, "Sending nopout as ping\n"); 21718c2ecf20Sopenharmony_ci if (iscsi_send_nopout(conn, NULL)) 21728c2ecf20Sopenharmony_ci next_timeout = jiffies + (1 * HZ); 21738c2ecf20Sopenharmony_ci else 21748c2ecf20Sopenharmony_ci next_timeout = conn->last_ping + (conn->ping_timeout * HZ); 21758c2ecf20Sopenharmony_ci } else 21768c2ecf20Sopenharmony_ci next_timeout = last_recv + recv_timeout; 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_ci ISCSI_DBG_CONN(conn, "Setting next tmo %lu\n", next_timeout); 21798c2ecf20Sopenharmony_ci mod_timer(&conn->transport_timer, next_timeout); 21808c2ecf20Sopenharmony_cidone: 21818c2ecf20Sopenharmony_ci spin_unlock(&session->frwd_lock); 21828c2ecf20Sopenharmony_ci} 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci/** 21858c2ecf20Sopenharmony_ci * iscsi_conn_unbind - prevent queueing to conn. 21868c2ecf20Sopenharmony_ci * @cls_conn: iscsi conn ep is bound to. 21878c2ecf20Sopenharmony_ci * @is_active: is the conn in use for boot or is this for EH/termination 21888c2ecf20Sopenharmony_ci * 21898c2ecf20Sopenharmony_ci * This must be called by drivers implementing the ep_disconnect callout. 21908c2ecf20Sopenharmony_ci * It disables queueing to the connection from libiscsi in preparation for 21918c2ecf20Sopenharmony_ci * an ep_disconnect call. 21928c2ecf20Sopenharmony_ci */ 21938c2ecf20Sopenharmony_civoid iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active) 21948c2ecf20Sopenharmony_ci{ 21958c2ecf20Sopenharmony_ci struct iscsi_session *session; 21968c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci if (!cls_conn) 21998c2ecf20Sopenharmony_ci return; 22008c2ecf20Sopenharmony_ci 22018c2ecf20Sopenharmony_ci conn = cls_conn->dd_data; 22028c2ecf20Sopenharmony_ci session = conn->session; 22038c2ecf20Sopenharmony_ci /* 22048c2ecf20Sopenharmony_ci * Wait for iscsi_eh calls to exit. We don't wait for the tmf to 22058c2ecf20Sopenharmony_ci * complete or timeout. The caller just wants to know what's running 22068c2ecf20Sopenharmony_ci * is everything that needs to be cleaned up, and no cmds will be 22078c2ecf20Sopenharmony_ci * queued. 22088c2ecf20Sopenharmony_ci */ 22098c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 22108c2ecf20Sopenharmony_ci 22118c2ecf20Sopenharmony_ci iscsi_suspend_queue(conn); 22128c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 22138c2ecf20Sopenharmony_ci 22148c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 22158c2ecf20Sopenharmony_ci if (!is_active) { 22168c2ecf20Sopenharmony_ci /* 22178c2ecf20Sopenharmony_ci * if logout timed out before userspace could even send a PDU 22188c2ecf20Sopenharmony_ci * the state might still be in ISCSI_STATE_LOGGED_IN and 22198c2ecf20Sopenharmony_ci * allowing new cmds and TMFs. 22208c2ecf20Sopenharmony_ci */ 22218c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_LOGGED_IN) 22228c2ecf20Sopenharmony_ci iscsi_set_conn_failed(conn); 22238c2ecf20Sopenharmony_ci } 22248c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 22258c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 22268c2ecf20Sopenharmony_ci} 22278c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_unbind); 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_cistatic void iscsi_prep_abort_task_pdu(struct iscsi_task *task, 22308c2ecf20Sopenharmony_ci struct iscsi_tm *hdr) 22318c2ecf20Sopenharmony_ci{ 22328c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 22338c2ecf20Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 22348c2ecf20Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK; 22358c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 22368c2ecf20Sopenharmony_ci hdr->lun = task->lun; 22378c2ecf20Sopenharmony_ci hdr->rtt = task->hdr_itt; 22388c2ecf20Sopenharmony_ci hdr->refcmdsn = task->cmdsn; 22398c2ecf20Sopenharmony_ci} 22408c2ecf20Sopenharmony_ci 22418c2ecf20Sopenharmony_ciint iscsi_eh_abort(struct scsi_cmnd *sc) 22428c2ecf20Sopenharmony_ci{ 22438c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 22448c2ecf20Sopenharmony_ci struct iscsi_session *session; 22458c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 22468c2ecf20Sopenharmony_ci struct iscsi_task *task; 22478c2ecf20Sopenharmony_ci struct iscsi_tm *hdr; 22488c2ecf20Sopenharmony_ci int age; 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 22518c2ecf20Sopenharmony_ci session = cls_session->dd_data; 22528c2ecf20Sopenharmony_ci 22538c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "aborting sc %p\n", sc); 22548c2ecf20Sopenharmony_ci 22558c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 22568c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 22578c2ecf20Sopenharmony_ci /* 22588c2ecf20Sopenharmony_ci * if session was ISCSI_STATE_IN_RECOVERY then we may not have 22598c2ecf20Sopenharmony_ci * got the command. 22608c2ecf20Sopenharmony_ci */ 22618c2ecf20Sopenharmony_ci if (!sc->SCp.ptr) { 22628c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "sc never reached iscsi layer or " 22638c2ecf20Sopenharmony_ci "it completed.\n"); 22648c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 22658c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 22668c2ecf20Sopenharmony_ci return SUCCESS; 22678c2ecf20Sopenharmony_ci } 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_ci /* 22708c2ecf20Sopenharmony_ci * If we are not logged in or we have started a new session 22718c2ecf20Sopenharmony_ci * then let the host reset code handle this 22728c2ecf20Sopenharmony_ci */ 22738c2ecf20Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN || 22748c2ecf20Sopenharmony_ci sc->SCp.phase != session->age) { 22758c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 22768c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 22778c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "failing abort due to dropped " 22788c2ecf20Sopenharmony_ci "session.\n"); 22798c2ecf20Sopenharmony_ci return FAILED; 22808c2ecf20Sopenharmony_ci } 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci conn = session->leadconn; 22838c2ecf20Sopenharmony_ci conn->eh_abort_cnt++; 22848c2ecf20Sopenharmony_ci age = session->age; 22858c2ecf20Sopenharmony_ci 22868c2ecf20Sopenharmony_ci spin_lock(&session->back_lock); 22878c2ecf20Sopenharmony_ci task = (struct iscsi_task *)sc->SCp.ptr; 22888c2ecf20Sopenharmony_ci if (!task || !task->sc) { 22898c2ecf20Sopenharmony_ci /* task completed before time out */ 22908c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "sc completed while abort in progress\n"); 22918c2ecf20Sopenharmony_ci 22928c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 22938c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 22948c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 22958c2ecf20Sopenharmony_ci return SUCCESS; 22968c2ecf20Sopenharmony_ci } 22978c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt); 22988c2ecf20Sopenharmony_ci __iscsi_get_task(task); 22998c2ecf20Sopenharmony_ci spin_unlock(&session->back_lock); 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) { 23028c2ecf20Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 23038c2ecf20Sopenharmony_ci goto success; 23048c2ecf20Sopenharmony_ci } 23058c2ecf20Sopenharmony_ci 23068c2ecf20Sopenharmony_ci /* only have one tmf outstanding at a time */ 23078c2ecf20Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 23088c2ecf20Sopenharmony_ci goto failed; 23098c2ecf20Sopenharmony_ci session->tmf_state = TMF_QUEUED; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci hdr = &session->tmhdr; 23128c2ecf20Sopenharmony_ci iscsi_prep_abort_task_pdu(task, hdr); 23138c2ecf20Sopenharmony_ci 23148c2ecf20Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) 23158c2ecf20Sopenharmony_ci goto failed; 23168c2ecf20Sopenharmony_ci 23178c2ecf20Sopenharmony_ci switch (session->tmf_state) { 23188c2ecf20Sopenharmony_ci case TMF_SUCCESS: 23198c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 23208c2ecf20Sopenharmony_ci /* 23218c2ecf20Sopenharmony_ci * stop tx side incase the target had sent a abort rsp but 23228c2ecf20Sopenharmony_ci * the initiator was still writing out data. 23238c2ecf20Sopenharmony_ci */ 23248c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 23258c2ecf20Sopenharmony_ci /* 23268c2ecf20Sopenharmony_ci * we do not stop the recv side because targets have been 23278c2ecf20Sopenharmony_ci * good and have never sent us a successful tmf response 23288c2ecf20Sopenharmony_ci * then sent more data for the cmd. 23298c2ecf20Sopenharmony_ci */ 23308c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 23318c2ecf20Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 23328c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 23338c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 23348c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 23358c2ecf20Sopenharmony_ci iscsi_start_tx(conn); 23368c2ecf20Sopenharmony_ci goto success_unlocked; 23378c2ecf20Sopenharmony_ci case TMF_TIMEDOUT: 23388c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 23398c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 23408c2ecf20Sopenharmony_ci goto failed_unlocked; 23418c2ecf20Sopenharmony_ci case TMF_NOT_FOUND: 23428c2ecf20Sopenharmony_ci if (!sc->SCp.ptr) { 23438c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 23448c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 23458c2ecf20Sopenharmony_ci /* task completed before tmf abort response */ 23468c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "sc completed while abort in " 23478c2ecf20Sopenharmony_ci "progress\n"); 23488c2ecf20Sopenharmony_ci goto success; 23498c2ecf20Sopenharmony_ci } 23508c2ecf20Sopenharmony_ci fallthrough; 23518c2ecf20Sopenharmony_ci default: 23528c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 23538c2ecf20Sopenharmony_ci goto failed; 23548c2ecf20Sopenharmony_ci } 23558c2ecf20Sopenharmony_ci 23568c2ecf20Sopenharmony_cisuccess: 23578c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 23588c2ecf20Sopenharmony_cisuccess_unlocked: 23598c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n", 23608c2ecf20Sopenharmony_ci sc, task->itt); 23618c2ecf20Sopenharmony_ci iscsi_put_task(task); 23628c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 23638c2ecf20Sopenharmony_ci return SUCCESS; 23648c2ecf20Sopenharmony_ci 23658c2ecf20Sopenharmony_cifailed: 23668c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 23678c2ecf20Sopenharmony_cifailed_unlocked: 23688c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc, 23698c2ecf20Sopenharmony_ci task ? task->itt : 0); 23708c2ecf20Sopenharmony_ci iscsi_put_task(task); 23718c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 23728c2ecf20Sopenharmony_ci return FAILED; 23738c2ecf20Sopenharmony_ci} 23748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_abort); 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_cistatic void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) 23778c2ecf20Sopenharmony_ci{ 23788c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 23798c2ecf20Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 23808c2ecf20Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK; 23818c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 23828c2ecf20Sopenharmony_ci int_to_scsilun(sc->device->lun, &hdr->lun); 23838c2ecf20Sopenharmony_ci hdr->rtt = RESERVED_ITT; 23848c2ecf20Sopenharmony_ci} 23858c2ecf20Sopenharmony_ci 23868c2ecf20Sopenharmony_ciint iscsi_eh_device_reset(struct scsi_cmnd *sc) 23878c2ecf20Sopenharmony_ci{ 23888c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 23898c2ecf20Sopenharmony_ci struct iscsi_session *session; 23908c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 23918c2ecf20Sopenharmony_ci struct iscsi_tm *hdr; 23928c2ecf20Sopenharmony_ci int rc = FAILED; 23938c2ecf20Sopenharmony_ci 23948c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 23958c2ecf20Sopenharmony_ci session = cls_session->dd_data; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "LU Reset [sc %p lun %llu]\n", sc, 23988c2ecf20Sopenharmony_ci sc->device->lun); 23998c2ecf20Sopenharmony_ci 24008c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 24018c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 24028c2ecf20Sopenharmony_ci /* 24038c2ecf20Sopenharmony_ci * Just check if we are not logged in. We cannot check for 24048c2ecf20Sopenharmony_ci * the phase because the reset could come from a ioctl. 24058c2ecf20Sopenharmony_ci */ 24068c2ecf20Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) 24078c2ecf20Sopenharmony_ci goto unlock; 24088c2ecf20Sopenharmony_ci conn = session->leadconn; 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci /* only have one tmf outstanding at a time */ 24118c2ecf20Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 24128c2ecf20Sopenharmony_ci goto unlock; 24138c2ecf20Sopenharmony_ci session->tmf_state = TMF_QUEUED; 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci hdr = &session->tmhdr; 24168c2ecf20Sopenharmony_ci iscsi_prep_lun_reset_pdu(sc, hdr); 24178c2ecf20Sopenharmony_ci 24188c2ecf20Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, 24198c2ecf20Sopenharmony_ci session->lu_reset_timeout)) { 24208c2ecf20Sopenharmony_ci rc = FAILED; 24218c2ecf20Sopenharmony_ci goto unlock; 24228c2ecf20Sopenharmony_ci } 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_ci switch (session->tmf_state) { 24258c2ecf20Sopenharmony_ci case TMF_SUCCESS: 24268c2ecf20Sopenharmony_ci break; 24278c2ecf20Sopenharmony_ci case TMF_TIMEDOUT: 24288c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24298c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 24308c2ecf20Sopenharmony_ci goto done; 24318c2ecf20Sopenharmony_ci default: 24328c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 24338c2ecf20Sopenharmony_ci goto unlock; 24348c2ecf20Sopenharmony_ci } 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_ci rc = SUCCESS; 24378c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24388c2ecf20Sopenharmony_ci 24398c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 24408c2ecf20Sopenharmony_ci 24418c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 24428c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 24438c2ecf20Sopenharmony_ci fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); 24448c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 24458c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci iscsi_start_tx(conn); 24488c2ecf20Sopenharmony_ci goto done; 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ciunlock: 24518c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24528c2ecf20Sopenharmony_cidone: 24538c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "dev reset result = %s\n", 24548c2ecf20Sopenharmony_ci rc == SUCCESS ? "SUCCESS" : "FAILED"); 24558c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 24568c2ecf20Sopenharmony_ci return rc; 24578c2ecf20Sopenharmony_ci} 24588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_device_reset); 24598c2ecf20Sopenharmony_ci 24608c2ecf20Sopenharmony_civoid iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) 24618c2ecf20Sopenharmony_ci{ 24628c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 24638c2ecf20Sopenharmony_ci 24648c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 24658c2ecf20Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 24668c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_RECOVERY_FAILED; 24678c2ecf20Sopenharmony_ci wake_up(&session->ehwait); 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24708c2ecf20Sopenharmony_ci} 24718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_ci/** 24748c2ecf20Sopenharmony_ci * iscsi_eh_session_reset - drop session and attempt relogin 24758c2ecf20Sopenharmony_ci * @sc: scsi command 24768c2ecf20Sopenharmony_ci * 24778c2ecf20Sopenharmony_ci * This function will wait for a relogin, session termination from 24788c2ecf20Sopenharmony_ci * userspace, or a recovery/replacement timeout. 24798c2ecf20Sopenharmony_ci */ 24808c2ecf20Sopenharmony_ciint iscsi_eh_session_reset(struct scsi_cmnd *sc) 24818c2ecf20Sopenharmony_ci{ 24828c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 24838c2ecf20Sopenharmony_ci struct iscsi_session *session; 24848c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 24878c2ecf20Sopenharmony_ci session = cls_session->dd_data; 24888c2ecf20Sopenharmony_ci conn = session->leadconn; 24898c2ecf20Sopenharmony_ci 24908c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 24918c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 24928c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE) { 24938c2ecf20Sopenharmony_cifailed: 24948c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, 24958c2ecf20Sopenharmony_ci "failing session reset: Could not log back into " 24968c2ecf20Sopenharmony_ci "%s [age %d]\n", session->targetname, 24978c2ecf20Sopenharmony_ci session->age); 24988c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 24998c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 25008c2ecf20Sopenharmony_ci return FAILED; 25018c2ecf20Sopenharmony_ci } 25028c2ecf20Sopenharmony_ci 25038c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 25048c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 25058c2ecf20Sopenharmony_ci /* 25068c2ecf20Sopenharmony_ci * we drop the lock here but the leadconn cannot be destoyed while 25078c2ecf20Sopenharmony_ci * we are in the scsi eh 25088c2ecf20Sopenharmony_ci */ 25098c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 25108c2ecf20Sopenharmony_ci 25118c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "wait for relogin\n"); 25128c2ecf20Sopenharmony_ci wait_event_interruptible(session->ehwait, 25138c2ecf20Sopenharmony_ci session->state == ISCSI_STATE_TERMINATE || 25148c2ecf20Sopenharmony_ci session->state == ISCSI_STATE_LOGGED_IN || 25158c2ecf20Sopenharmony_ci session->state == ISCSI_STATE_RECOVERY_FAILED); 25168c2ecf20Sopenharmony_ci if (signal_pending(current)) 25178c2ecf20Sopenharmony_ci flush_signals(current); 25188c2ecf20Sopenharmony_ci 25198c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 25208c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 25218c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_LOGGED_IN) { 25228c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, 25238c2ecf20Sopenharmony_ci "session reset succeeded for %s,%s\n", 25248c2ecf20Sopenharmony_ci session->targetname, conn->persistent_address); 25258c2ecf20Sopenharmony_ci } else 25268c2ecf20Sopenharmony_ci goto failed; 25278c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 25288c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 25298c2ecf20Sopenharmony_ci return SUCCESS; 25308c2ecf20Sopenharmony_ci} 25318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_session_reset); 25328c2ecf20Sopenharmony_ci 25338c2ecf20Sopenharmony_cistatic void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) 25348c2ecf20Sopenharmony_ci{ 25358c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 25368c2ecf20Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 25378c2ecf20Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; 25388c2ecf20Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 25398c2ecf20Sopenharmony_ci hdr->rtt = RESERVED_ITT; 25408c2ecf20Sopenharmony_ci} 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci/** 25438c2ecf20Sopenharmony_ci * iscsi_eh_target_reset - reset target 25448c2ecf20Sopenharmony_ci * @sc: scsi command 25458c2ecf20Sopenharmony_ci * 25468c2ecf20Sopenharmony_ci * This will attempt to send a warm target reset. 25478c2ecf20Sopenharmony_ci */ 25488c2ecf20Sopenharmony_cistatic int iscsi_eh_target_reset(struct scsi_cmnd *sc) 25498c2ecf20Sopenharmony_ci{ 25508c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 25518c2ecf20Sopenharmony_ci struct iscsi_session *session; 25528c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 25538c2ecf20Sopenharmony_ci struct iscsi_tm *hdr; 25548c2ecf20Sopenharmony_ci int rc = FAILED; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 25578c2ecf20Sopenharmony_ci session = cls_session->dd_data; 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, 25608c2ecf20Sopenharmony_ci session->targetname); 25618c2ecf20Sopenharmony_ci 25628c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 25638c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 25648c2ecf20Sopenharmony_ci /* 25658c2ecf20Sopenharmony_ci * Just check if we are not logged in. We cannot check for 25668c2ecf20Sopenharmony_ci * the phase because the reset could come from a ioctl. 25678c2ecf20Sopenharmony_ci */ 25688c2ecf20Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) 25698c2ecf20Sopenharmony_ci goto unlock; 25708c2ecf20Sopenharmony_ci conn = session->leadconn; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci /* only have one tmf outstanding at a time */ 25738c2ecf20Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 25748c2ecf20Sopenharmony_ci goto unlock; 25758c2ecf20Sopenharmony_ci session->tmf_state = TMF_QUEUED; 25768c2ecf20Sopenharmony_ci 25778c2ecf20Sopenharmony_ci hdr = &session->tmhdr; 25788c2ecf20Sopenharmony_ci iscsi_prep_tgt_reset_pdu(sc, hdr); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, 25818c2ecf20Sopenharmony_ci session->tgt_reset_timeout)) { 25828c2ecf20Sopenharmony_ci rc = FAILED; 25838c2ecf20Sopenharmony_ci goto unlock; 25848c2ecf20Sopenharmony_ci } 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci switch (session->tmf_state) { 25878c2ecf20Sopenharmony_ci case TMF_SUCCESS: 25888c2ecf20Sopenharmony_ci break; 25898c2ecf20Sopenharmony_ci case TMF_TIMEDOUT: 25908c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 25918c2ecf20Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 25928c2ecf20Sopenharmony_ci goto done; 25938c2ecf20Sopenharmony_ci default: 25948c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 25958c2ecf20Sopenharmony_ci goto unlock; 25968c2ecf20Sopenharmony_ci } 25978c2ecf20Sopenharmony_ci 25988c2ecf20Sopenharmony_ci rc = SUCCESS; 25998c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 26008c2ecf20Sopenharmony_ci 26018c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 26028c2ecf20Sopenharmony_ci 26038c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 26048c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 26058c2ecf20Sopenharmony_ci fail_scsi_tasks(conn, -1, DID_ERROR); 26068c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 26078c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci iscsi_start_tx(conn); 26108c2ecf20Sopenharmony_ci goto done; 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ciunlock: 26138c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 26148c2ecf20Sopenharmony_cidone: 26158c2ecf20Sopenharmony_ci ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, 26168c2ecf20Sopenharmony_ci rc == SUCCESS ? "SUCCESS" : "FAILED"); 26178c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 26188c2ecf20Sopenharmony_ci return rc; 26198c2ecf20Sopenharmony_ci} 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci/** 26228c2ecf20Sopenharmony_ci * iscsi_eh_recover_target - reset target and possibly the session 26238c2ecf20Sopenharmony_ci * @sc: scsi command 26248c2ecf20Sopenharmony_ci * 26258c2ecf20Sopenharmony_ci * This will attempt to send a warm target reset. If that fails, 26268c2ecf20Sopenharmony_ci * we will escalate to ERL0 session recovery. 26278c2ecf20Sopenharmony_ci */ 26288c2ecf20Sopenharmony_ciint iscsi_eh_recover_target(struct scsi_cmnd *sc) 26298c2ecf20Sopenharmony_ci{ 26308c2ecf20Sopenharmony_ci int rc; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci rc = iscsi_eh_target_reset(sc); 26338c2ecf20Sopenharmony_ci if (rc == FAILED) 26348c2ecf20Sopenharmony_ci rc = iscsi_eh_session_reset(sc); 26358c2ecf20Sopenharmony_ci return rc; 26368c2ecf20Sopenharmony_ci} 26378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_recover_target); 26388c2ecf20Sopenharmony_ci 26398c2ecf20Sopenharmony_ci/* 26408c2ecf20Sopenharmony_ci * Pre-allocate a pool of @max items of @item_size. By default, the pool 26418c2ecf20Sopenharmony_ci * should be accessed via kfifo_{get,put} on q->queue. 26428c2ecf20Sopenharmony_ci * Optionally, the caller can obtain the array of object pointers 26438c2ecf20Sopenharmony_ci * by passing in a non-NULL @items pointer 26448c2ecf20Sopenharmony_ci */ 26458c2ecf20Sopenharmony_ciint 26468c2ecf20Sopenharmony_ciiscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size) 26478c2ecf20Sopenharmony_ci{ 26488c2ecf20Sopenharmony_ci int i, num_arrays = 1; 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_ci memset(q, 0, sizeof(*q)); 26518c2ecf20Sopenharmony_ci 26528c2ecf20Sopenharmony_ci q->max = max; 26538c2ecf20Sopenharmony_ci 26548c2ecf20Sopenharmony_ci /* If the user passed an items pointer, he wants a copy of 26558c2ecf20Sopenharmony_ci * the array. */ 26568c2ecf20Sopenharmony_ci if (items) 26578c2ecf20Sopenharmony_ci num_arrays++; 26588c2ecf20Sopenharmony_ci q->pool = kvcalloc(num_arrays * max, sizeof(void *), GFP_KERNEL); 26598c2ecf20Sopenharmony_ci if (q->pool == NULL) 26608c2ecf20Sopenharmony_ci return -ENOMEM; 26618c2ecf20Sopenharmony_ci 26628c2ecf20Sopenharmony_ci kfifo_init(&q->queue, (void*)q->pool, max * sizeof(void*)); 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci for (i = 0; i < max; i++) { 26658c2ecf20Sopenharmony_ci q->pool[i] = kzalloc(item_size, GFP_KERNEL); 26668c2ecf20Sopenharmony_ci if (q->pool[i] == NULL) { 26678c2ecf20Sopenharmony_ci q->max = i; 26688c2ecf20Sopenharmony_ci goto enomem; 26698c2ecf20Sopenharmony_ci } 26708c2ecf20Sopenharmony_ci kfifo_in(&q->queue, (void*)&q->pool[i], sizeof(void*)); 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci if (items) { 26748c2ecf20Sopenharmony_ci *items = q->pool + max; 26758c2ecf20Sopenharmony_ci memcpy(*items, q->pool, max * sizeof(void *)); 26768c2ecf20Sopenharmony_ci } 26778c2ecf20Sopenharmony_ci 26788c2ecf20Sopenharmony_ci return 0; 26798c2ecf20Sopenharmony_ci 26808c2ecf20Sopenharmony_cienomem: 26818c2ecf20Sopenharmony_ci iscsi_pool_free(q); 26828c2ecf20Sopenharmony_ci return -ENOMEM; 26838c2ecf20Sopenharmony_ci} 26848c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_pool_init); 26858c2ecf20Sopenharmony_ci 26868c2ecf20Sopenharmony_civoid iscsi_pool_free(struct iscsi_pool *q) 26878c2ecf20Sopenharmony_ci{ 26888c2ecf20Sopenharmony_ci int i; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci for (i = 0; i < q->max; i++) 26918c2ecf20Sopenharmony_ci kfree(q->pool[i]); 26928c2ecf20Sopenharmony_ci kvfree(q->pool); 26938c2ecf20Sopenharmony_ci} 26948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_pool_free); 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ciint iscsi_host_get_max_scsi_cmds(struct Scsi_Host *shost, 26978c2ecf20Sopenharmony_ci uint16_t requested_cmds_max) 26988c2ecf20Sopenharmony_ci{ 26998c2ecf20Sopenharmony_ci int scsi_cmds, total_cmds = requested_cmds_max; 27008c2ecf20Sopenharmony_ci 27018c2ecf20Sopenharmony_cicheck: 27028c2ecf20Sopenharmony_ci if (!total_cmds) 27038c2ecf20Sopenharmony_ci total_cmds = ISCSI_DEF_XMIT_CMDS_MAX; 27048c2ecf20Sopenharmony_ci /* 27058c2ecf20Sopenharmony_ci * The iscsi layer needs some tasks for nop handling and tmfs, 27068c2ecf20Sopenharmony_ci * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX 27078c2ecf20Sopenharmony_ci * + 1 command for scsi IO. 27088c2ecf20Sopenharmony_ci */ 27098c2ecf20Sopenharmony_ci if (total_cmds < ISCSI_TOTAL_CMDS_MIN) { 27108c2ecf20Sopenharmony_ci printk(KERN_ERR "iscsi: invalid max cmds of %d. Must be a power of two that is at least %d.\n", 27118c2ecf20Sopenharmony_ci total_cmds, ISCSI_TOTAL_CMDS_MIN); 27128c2ecf20Sopenharmony_ci return -EINVAL; 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci if (total_cmds > ISCSI_TOTAL_CMDS_MAX) { 27168c2ecf20Sopenharmony_ci printk(KERN_INFO "iscsi: invalid max cmds of %d. Must be a power of 2 less than or equal to %d. Using %d.\n", 27178c2ecf20Sopenharmony_ci requested_cmds_max, ISCSI_TOTAL_CMDS_MAX, 27188c2ecf20Sopenharmony_ci ISCSI_TOTAL_CMDS_MAX); 27198c2ecf20Sopenharmony_ci total_cmds = ISCSI_TOTAL_CMDS_MAX; 27208c2ecf20Sopenharmony_ci } 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci if (!is_power_of_2(total_cmds)) { 27238c2ecf20Sopenharmony_ci total_cmds = rounddown_pow_of_two(total_cmds); 27248c2ecf20Sopenharmony_ci if (total_cmds < ISCSI_TOTAL_CMDS_MIN) { 27258c2ecf20Sopenharmony_ci printk(KERN_ERR "iscsi: invalid max cmds of %d. Must be a power of 2 greater than %d.\n", requested_cmds_max, ISCSI_TOTAL_CMDS_MIN); 27268c2ecf20Sopenharmony_ci return -EINVAL; 27278c2ecf20Sopenharmony_ci } 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci printk(KERN_INFO "iscsi: invalid max cmds %d. Must be a power of 2. Rounding max cmds down to %d.\n", 27308c2ecf20Sopenharmony_ci requested_cmds_max, total_cmds); 27318c2ecf20Sopenharmony_ci } 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; 27348c2ecf20Sopenharmony_ci if (shost->can_queue && scsi_cmds > shost->can_queue) { 27358c2ecf20Sopenharmony_ci total_cmds = shost->can_queue; 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci printk(KERN_INFO "iscsi: requested max cmds %u is higher than driver limit. Using driver limit %u\n", 27388c2ecf20Sopenharmony_ci requested_cmds_max, shost->can_queue); 27398c2ecf20Sopenharmony_ci goto check; 27408c2ecf20Sopenharmony_ci } 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci return scsi_cmds; 27438c2ecf20Sopenharmony_ci} 27448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_get_max_scsi_cmds); 27458c2ecf20Sopenharmony_ci 27468c2ecf20Sopenharmony_ci/** 27478c2ecf20Sopenharmony_ci * iscsi_host_add - add host to system 27488c2ecf20Sopenharmony_ci * @shost: scsi host 27498c2ecf20Sopenharmony_ci * @pdev: parent device 27508c2ecf20Sopenharmony_ci * 27518c2ecf20Sopenharmony_ci * This should be called by partial offload and software iscsi drivers 27528c2ecf20Sopenharmony_ci * to add a host to the system. 27538c2ecf20Sopenharmony_ci */ 27548c2ecf20Sopenharmony_ciint iscsi_host_add(struct Scsi_Host *shost, struct device *pdev) 27558c2ecf20Sopenharmony_ci{ 27568c2ecf20Sopenharmony_ci if (!shost->can_queue) 27578c2ecf20Sopenharmony_ci shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci if (!shost->cmd_per_lun) 27608c2ecf20Sopenharmony_ci shost->cmd_per_lun = ISCSI_DEF_CMD_PER_LUN; 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci return scsi_add_host(shost, pdev); 27638c2ecf20Sopenharmony_ci} 27648c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_add); 27658c2ecf20Sopenharmony_ci 27668c2ecf20Sopenharmony_ci/** 27678c2ecf20Sopenharmony_ci * iscsi_host_alloc - allocate a host and driver data 27688c2ecf20Sopenharmony_ci * @sht: scsi host template 27698c2ecf20Sopenharmony_ci * @dd_data_size: driver host data size 27708c2ecf20Sopenharmony_ci * @xmit_can_sleep: bool indicating if LLD will queue IO from a work queue 27718c2ecf20Sopenharmony_ci * 27728c2ecf20Sopenharmony_ci * This should be called by partial offload and software iscsi drivers. 27738c2ecf20Sopenharmony_ci * To access the driver specific memory use the iscsi_host_priv() macro. 27748c2ecf20Sopenharmony_ci */ 27758c2ecf20Sopenharmony_cistruct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, 27768c2ecf20Sopenharmony_ci int dd_data_size, bool xmit_can_sleep) 27778c2ecf20Sopenharmony_ci{ 27788c2ecf20Sopenharmony_ci struct Scsi_Host *shost; 27798c2ecf20Sopenharmony_ci struct iscsi_host *ihost; 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_ci shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); 27828c2ecf20Sopenharmony_ci if (!shost) 27838c2ecf20Sopenharmony_ci return NULL; 27848c2ecf20Sopenharmony_ci ihost = shost_priv(shost); 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_ci if (xmit_can_sleep) { 27878c2ecf20Sopenharmony_ci snprintf(ihost->workq_name, sizeof(ihost->workq_name), 27888c2ecf20Sopenharmony_ci "iscsi_q_%d", shost->host_no); 27898c2ecf20Sopenharmony_ci ihost->workq = alloc_workqueue("%s", 27908c2ecf20Sopenharmony_ci WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, 27918c2ecf20Sopenharmony_ci 1, ihost->workq_name); 27928c2ecf20Sopenharmony_ci if (!ihost->workq) 27938c2ecf20Sopenharmony_ci goto free_host; 27948c2ecf20Sopenharmony_ci } 27958c2ecf20Sopenharmony_ci 27968c2ecf20Sopenharmony_ci spin_lock_init(&ihost->lock); 27978c2ecf20Sopenharmony_ci ihost->state = ISCSI_HOST_SETUP; 27988c2ecf20Sopenharmony_ci ihost->num_sessions = 0; 27998c2ecf20Sopenharmony_ci init_waitqueue_head(&ihost->session_removal_wq); 28008c2ecf20Sopenharmony_ci return shost; 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_cifree_host: 28038c2ecf20Sopenharmony_ci scsi_host_put(shost); 28048c2ecf20Sopenharmony_ci return NULL; 28058c2ecf20Sopenharmony_ci} 28068c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_alloc); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_cistatic void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session) 28098c2ecf20Sopenharmony_ci{ 28108c2ecf20Sopenharmony_ci iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_INVALID_HOST); 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_ci/** 28148c2ecf20Sopenharmony_ci * iscsi_host_remove - remove host and sessions 28158c2ecf20Sopenharmony_ci * @shost: scsi host 28168c2ecf20Sopenharmony_ci * 28178c2ecf20Sopenharmony_ci * If there are any sessions left, this will initiate the removal and wait 28188c2ecf20Sopenharmony_ci * for the completion. 28198c2ecf20Sopenharmony_ci */ 28208c2ecf20Sopenharmony_civoid iscsi_host_remove(struct Scsi_Host *shost) 28218c2ecf20Sopenharmony_ci{ 28228c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 28238c2ecf20Sopenharmony_ci unsigned long flags; 28248c2ecf20Sopenharmony_ci 28258c2ecf20Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 28268c2ecf20Sopenharmony_ci ihost->state = ISCSI_HOST_REMOVED; 28278c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 28288c2ecf20Sopenharmony_ci 28298c2ecf20Sopenharmony_ci iscsi_host_for_each_session(shost, iscsi_notify_host_removed); 28308c2ecf20Sopenharmony_ci wait_event_interruptible(ihost->session_removal_wq, 28318c2ecf20Sopenharmony_ci ihost->num_sessions == 0); 28328c2ecf20Sopenharmony_ci if (signal_pending(current)) 28338c2ecf20Sopenharmony_ci flush_signals(current); 28348c2ecf20Sopenharmony_ci 28358c2ecf20Sopenharmony_ci scsi_remove_host(shost); 28368c2ecf20Sopenharmony_ci} 28378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_remove); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_civoid iscsi_host_free(struct Scsi_Host *shost) 28408c2ecf20Sopenharmony_ci{ 28418c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 28428c2ecf20Sopenharmony_ci 28438c2ecf20Sopenharmony_ci if (ihost->workq) 28448c2ecf20Sopenharmony_ci destroy_workqueue(ihost->workq); 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci kfree(ihost->netdev); 28478c2ecf20Sopenharmony_ci kfree(ihost->hwaddress); 28488c2ecf20Sopenharmony_ci kfree(ihost->initiatorname); 28498c2ecf20Sopenharmony_ci scsi_host_put(shost); 28508c2ecf20Sopenharmony_ci} 28518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_free); 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_cistatic void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) 28548c2ecf20Sopenharmony_ci{ 28558c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 28568c2ecf20Sopenharmony_ci unsigned long flags; 28578c2ecf20Sopenharmony_ci 28588c2ecf20Sopenharmony_ci shost = scsi_host_get(shost); 28598c2ecf20Sopenharmony_ci if (!shost) { 28608c2ecf20Sopenharmony_ci printk(KERN_ERR "Invalid state. Cannot notify host removal " 28618c2ecf20Sopenharmony_ci "of session teardown event because host already " 28628c2ecf20Sopenharmony_ci "removed.\n"); 28638c2ecf20Sopenharmony_ci return; 28648c2ecf20Sopenharmony_ci } 28658c2ecf20Sopenharmony_ci 28668c2ecf20Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 28678c2ecf20Sopenharmony_ci ihost->num_sessions--; 28688c2ecf20Sopenharmony_ci if (ihost->num_sessions == 0) 28698c2ecf20Sopenharmony_ci wake_up(&ihost->session_removal_wq); 28708c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 28718c2ecf20Sopenharmony_ci scsi_host_put(shost); 28728c2ecf20Sopenharmony_ci} 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci/** 28758c2ecf20Sopenharmony_ci * iscsi_session_setup - create iscsi cls session and host and session 28768c2ecf20Sopenharmony_ci * @iscsit: iscsi transport template 28778c2ecf20Sopenharmony_ci * @shost: scsi host 28788c2ecf20Sopenharmony_ci * @cmds_max: session can queue 28798c2ecf20Sopenharmony_ci * @dd_size: private driver data size, added to session allocation size 28808c2ecf20Sopenharmony_ci * @cmd_task_size: LLD task private data size 28818c2ecf20Sopenharmony_ci * @initial_cmdsn: initial CmdSN 28828c2ecf20Sopenharmony_ci * @id: target ID to add to this session 28838c2ecf20Sopenharmony_ci * 28848c2ecf20Sopenharmony_ci * This can be used by software iscsi_transports that allocate 28858c2ecf20Sopenharmony_ci * a session per scsi host. 28868c2ecf20Sopenharmony_ci * 28878c2ecf20Sopenharmony_ci * Callers should set cmds_max to the largest total numer (mgmt + scsi) of 28888c2ecf20Sopenharmony_ci * tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks 28898c2ecf20Sopenharmony_ci * for nop handling and login/logout requests. 28908c2ecf20Sopenharmony_ci */ 28918c2ecf20Sopenharmony_cistruct iscsi_cls_session * 28928c2ecf20Sopenharmony_ciiscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, 28938c2ecf20Sopenharmony_ci uint16_t cmds_max, int dd_size, int cmd_task_size, 28948c2ecf20Sopenharmony_ci uint32_t initial_cmdsn, unsigned int id) 28958c2ecf20Sopenharmony_ci{ 28968c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 28978c2ecf20Sopenharmony_ci struct iscsi_session *session; 28988c2ecf20Sopenharmony_ci struct iscsi_cls_session *cls_session; 28998c2ecf20Sopenharmony_ci int cmd_i, scsi_cmds; 29008c2ecf20Sopenharmony_ci unsigned long flags; 29018c2ecf20Sopenharmony_ci 29028c2ecf20Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 29038c2ecf20Sopenharmony_ci if (ihost->state == ISCSI_HOST_REMOVED) { 29048c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 29058c2ecf20Sopenharmony_ci return NULL; 29068c2ecf20Sopenharmony_ci } 29078c2ecf20Sopenharmony_ci ihost->num_sessions++; 29088c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 29098c2ecf20Sopenharmony_ci 29108c2ecf20Sopenharmony_ci scsi_cmds = iscsi_host_get_max_scsi_cmds(shost, cmds_max); 29118c2ecf20Sopenharmony_ci if (scsi_cmds < 0) 29128c2ecf20Sopenharmony_ci goto dec_session_count; 29138c2ecf20Sopenharmony_ci 29148c2ecf20Sopenharmony_ci cls_session = iscsi_alloc_session(shost, iscsit, 29158c2ecf20Sopenharmony_ci sizeof(struct iscsi_session) + 29168c2ecf20Sopenharmony_ci dd_size); 29178c2ecf20Sopenharmony_ci if (!cls_session) 29188c2ecf20Sopenharmony_ci goto dec_session_count; 29198c2ecf20Sopenharmony_ci session = cls_session->dd_data; 29208c2ecf20Sopenharmony_ci session->cls_session = cls_session; 29218c2ecf20Sopenharmony_ci session->host = shost; 29228c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_FREE; 29238c2ecf20Sopenharmony_ci session->fast_abort = 1; 29248c2ecf20Sopenharmony_ci session->tgt_reset_timeout = 30; 29258c2ecf20Sopenharmony_ci session->lu_reset_timeout = 15; 29268c2ecf20Sopenharmony_ci session->abort_timeout = 10; 29278c2ecf20Sopenharmony_ci session->scsi_cmds_max = scsi_cmds; 29288c2ecf20Sopenharmony_ci session->cmds_max = scsi_cmds + ISCSI_MGMT_CMDS_MAX; 29298c2ecf20Sopenharmony_ci session->queued_cmdsn = session->cmdsn = initial_cmdsn; 29308c2ecf20Sopenharmony_ci session->exp_cmdsn = initial_cmdsn + 1; 29318c2ecf20Sopenharmony_ci session->max_cmdsn = initial_cmdsn + 1; 29328c2ecf20Sopenharmony_ci session->max_r2t = 1; 29338c2ecf20Sopenharmony_ci session->tt = iscsit; 29348c2ecf20Sopenharmony_ci session->dd_data = cls_session->dd_data + sizeof(*session); 29358c2ecf20Sopenharmony_ci 29368c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 29378c2ecf20Sopenharmony_ci timer_setup(&session->tmf_timer, iscsi_tmf_timedout, 0); 29388c2ecf20Sopenharmony_ci mutex_init(&session->eh_mutex); 29398c2ecf20Sopenharmony_ci 29408c2ecf20Sopenharmony_ci spin_lock_init(&session->frwd_lock); 29418c2ecf20Sopenharmony_ci spin_lock_init(&session->back_lock); 29428c2ecf20Sopenharmony_ci 29438c2ecf20Sopenharmony_ci /* initialize SCSI PDU commands pool */ 29448c2ecf20Sopenharmony_ci if (iscsi_pool_init(&session->cmdpool, session->cmds_max, 29458c2ecf20Sopenharmony_ci (void***)&session->cmds, 29468c2ecf20Sopenharmony_ci cmd_task_size + sizeof(struct iscsi_task))) 29478c2ecf20Sopenharmony_ci goto cmdpool_alloc_fail; 29488c2ecf20Sopenharmony_ci 29498c2ecf20Sopenharmony_ci /* pre-format cmds pool with ITT */ 29508c2ecf20Sopenharmony_ci for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { 29518c2ecf20Sopenharmony_ci struct iscsi_task *task = session->cmds[cmd_i]; 29528c2ecf20Sopenharmony_ci 29538c2ecf20Sopenharmony_ci if (cmd_task_size) 29548c2ecf20Sopenharmony_ci task->dd_data = &task[1]; 29558c2ecf20Sopenharmony_ci task->itt = cmd_i; 29568c2ecf20Sopenharmony_ci task->state = ISCSI_TASK_FREE; 29578c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&task->running); 29588c2ecf20Sopenharmony_ci } 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci if (!try_module_get(iscsit->owner)) 29618c2ecf20Sopenharmony_ci goto module_get_fail; 29628c2ecf20Sopenharmony_ci 29638c2ecf20Sopenharmony_ci if (iscsi_add_session(cls_session, id)) 29648c2ecf20Sopenharmony_ci goto cls_session_fail; 29658c2ecf20Sopenharmony_ci 29668c2ecf20Sopenharmony_ci return cls_session; 29678c2ecf20Sopenharmony_ci 29688c2ecf20Sopenharmony_cicls_session_fail: 29698c2ecf20Sopenharmony_ci module_put(iscsit->owner); 29708c2ecf20Sopenharmony_cimodule_get_fail: 29718c2ecf20Sopenharmony_ci iscsi_pool_free(&session->cmdpool); 29728c2ecf20Sopenharmony_cicmdpool_alloc_fail: 29738c2ecf20Sopenharmony_ci iscsi_free_session(cls_session); 29748c2ecf20Sopenharmony_cidec_session_count: 29758c2ecf20Sopenharmony_ci iscsi_host_dec_session_cnt(shost); 29768c2ecf20Sopenharmony_ci return NULL; 29778c2ecf20Sopenharmony_ci} 29788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_setup); 29798c2ecf20Sopenharmony_ci 29808c2ecf20Sopenharmony_ci/** 29818c2ecf20Sopenharmony_ci * iscsi_session_teardown - destroy session, host, and cls_session 29828c2ecf20Sopenharmony_ci * @cls_session: iscsi session 29838c2ecf20Sopenharmony_ci */ 29848c2ecf20Sopenharmony_civoid iscsi_session_teardown(struct iscsi_cls_session *cls_session) 29858c2ecf20Sopenharmony_ci{ 29868c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 29878c2ecf20Sopenharmony_ci struct module *owner = cls_session->transport->owner; 29888c2ecf20Sopenharmony_ci struct Scsi_Host *shost = session->host; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci iscsi_pool_free(&session->cmdpool); 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_ci iscsi_remove_session(cls_session); 29938c2ecf20Sopenharmony_ci 29948c2ecf20Sopenharmony_ci kfree(session->password); 29958c2ecf20Sopenharmony_ci kfree(session->password_in); 29968c2ecf20Sopenharmony_ci kfree(session->username); 29978c2ecf20Sopenharmony_ci kfree(session->username_in); 29988c2ecf20Sopenharmony_ci kfree(session->targetname); 29998c2ecf20Sopenharmony_ci kfree(session->targetalias); 30008c2ecf20Sopenharmony_ci kfree(session->initiatorname); 30018c2ecf20Sopenharmony_ci kfree(session->boot_root); 30028c2ecf20Sopenharmony_ci kfree(session->boot_nic); 30038c2ecf20Sopenharmony_ci kfree(session->boot_target); 30048c2ecf20Sopenharmony_ci kfree(session->ifacename); 30058c2ecf20Sopenharmony_ci kfree(session->portal_type); 30068c2ecf20Sopenharmony_ci kfree(session->discovery_parent_type); 30078c2ecf20Sopenharmony_ci 30088c2ecf20Sopenharmony_ci iscsi_free_session(cls_session); 30098c2ecf20Sopenharmony_ci 30108c2ecf20Sopenharmony_ci iscsi_host_dec_session_cnt(shost); 30118c2ecf20Sopenharmony_ci module_put(owner); 30128c2ecf20Sopenharmony_ci} 30138c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_teardown); 30148c2ecf20Sopenharmony_ci 30158c2ecf20Sopenharmony_ci/** 30168c2ecf20Sopenharmony_ci * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn 30178c2ecf20Sopenharmony_ci * @cls_session: iscsi_cls_session 30188c2ecf20Sopenharmony_ci * @dd_size: private driver data size 30198c2ecf20Sopenharmony_ci * @conn_idx: cid 30208c2ecf20Sopenharmony_ci */ 30218c2ecf20Sopenharmony_cistruct iscsi_cls_conn * 30228c2ecf20Sopenharmony_ciiscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, 30238c2ecf20Sopenharmony_ci uint32_t conn_idx) 30248c2ecf20Sopenharmony_ci{ 30258c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 30268c2ecf20Sopenharmony_ci struct iscsi_conn *conn; 30278c2ecf20Sopenharmony_ci struct iscsi_cls_conn *cls_conn; 30288c2ecf20Sopenharmony_ci char *data; 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_ci cls_conn = iscsi_create_conn(cls_session, sizeof(*conn) + dd_size, 30318c2ecf20Sopenharmony_ci conn_idx); 30328c2ecf20Sopenharmony_ci if (!cls_conn) 30338c2ecf20Sopenharmony_ci return NULL; 30348c2ecf20Sopenharmony_ci conn = cls_conn->dd_data; 30358c2ecf20Sopenharmony_ci memset(conn, 0, sizeof(*conn) + dd_size); 30368c2ecf20Sopenharmony_ci 30378c2ecf20Sopenharmony_ci conn->dd_data = cls_conn->dd_data + sizeof(*conn); 30388c2ecf20Sopenharmony_ci conn->session = session; 30398c2ecf20Sopenharmony_ci conn->cls_conn = cls_conn; 30408c2ecf20Sopenharmony_ci conn->c_stage = ISCSI_CONN_INITIAL_STAGE; 30418c2ecf20Sopenharmony_ci conn->id = conn_idx; 30428c2ecf20Sopenharmony_ci conn->exp_statsn = 0; 30438c2ecf20Sopenharmony_ci 30448c2ecf20Sopenharmony_ci timer_setup(&conn->transport_timer, iscsi_check_transport_timeouts, 0); 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&conn->mgmtqueue); 30478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&conn->cmdqueue); 30488c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&conn->requeue); 30498c2ecf20Sopenharmony_ci INIT_WORK(&conn->xmitwork, iscsi_xmitworker); 30508c2ecf20Sopenharmony_ci 30518c2ecf20Sopenharmony_ci /* allocate login_task used for the login/text sequences */ 30528c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 30538c2ecf20Sopenharmony_ci if (!kfifo_out(&session->cmdpool.queue, 30548c2ecf20Sopenharmony_ci (void*)&conn->login_task, 30558c2ecf20Sopenharmony_ci sizeof(void*))) { 30568c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 30578c2ecf20Sopenharmony_ci goto login_task_alloc_fail; 30588c2ecf20Sopenharmony_ci } 30598c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 30608c2ecf20Sopenharmony_ci 30618c2ecf20Sopenharmony_ci data = (char *) __get_free_pages(GFP_KERNEL, 30628c2ecf20Sopenharmony_ci get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); 30638c2ecf20Sopenharmony_ci if (!data) 30648c2ecf20Sopenharmony_ci goto login_task_data_alloc_fail; 30658c2ecf20Sopenharmony_ci conn->login_task->data = conn->data = data; 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci init_waitqueue_head(&session->ehwait); 30688c2ecf20Sopenharmony_ci 30698c2ecf20Sopenharmony_ci return cls_conn; 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_cilogin_task_data_alloc_fail: 30728c2ecf20Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, 30738c2ecf20Sopenharmony_ci sizeof(void*)); 30748c2ecf20Sopenharmony_cilogin_task_alloc_fail: 30758c2ecf20Sopenharmony_ci iscsi_destroy_conn(cls_conn); 30768c2ecf20Sopenharmony_ci return NULL; 30778c2ecf20Sopenharmony_ci} 30788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_setup); 30798c2ecf20Sopenharmony_ci 30808c2ecf20Sopenharmony_ci/** 30818c2ecf20Sopenharmony_ci * iscsi_conn_teardown - teardown iscsi connection 30828c2ecf20Sopenharmony_ci * @cls_conn: iscsi class connection 30838c2ecf20Sopenharmony_ci * 30848c2ecf20Sopenharmony_ci * TODO: we may need to make this into a two step process 30858c2ecf20Sopenharmony_ci * like scsi-mls remove + put host 30868c2ecf20Sopenharmony_ci */ 30878c2ecf20Sopenharmony_civoid iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) 30888c2ecf20Sopenharmony_ci{ 30898c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 30908c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 30918c2ecf20Sopenharmony_ci char *tmp_persistent_address = conn->persistent_address; 30928c2ecf20Sopenharmony_ci char *tmp_local_ipaddr = conn->local_ipaddr; 30938c2ecf20Sopenharmony_ci 30948c2ecf20Sopenharmony_ci del_timer_sync(&conn->transport_timer); 30958c2ecf20Sopenharmony_ci 30968c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 30978c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 30988c2ecf20Sopenharmony_ci conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; 30998c2ecf20Sopenharmony_ci if (session->leadconn == conn) { 31008c2ecf20Sopenharmony_ci /* 31018c2ecf20Sopenharmony_ci * leading connection? then give up on recovery. 31028c2ecf20Sopenharmony_ci */ 31038c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_TERMINATE; 31048c2ecf20Sopenharmony_ci wake_up(&session->ehwait); 31058c2ecf20Sopenharmony_ci } 31068c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 31078c2ecf20Sopenharmony_ci 31088c2ecf20Sopenharmony_ci /* flush queued up work because we free the connection below */ 31098c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 31108c2ecf20Sopenharmony_ci 31118c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 31128c2ecf20Sopenharmony_ci free_pages((unsigned long) conn->data, 31138c2ecf20Sopenharmony_ci get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); 31148c2ecf20Sopenharmony_ci /* regular RX path uses back_lock */ 31158c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 31168c2ecf20Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, 31178c2ecf20Sopenharmony_ci sizeof(void*)); 31188c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 31198c2ecf20Sopenharmony_ci if (session->leadconn == conn) 31208c2ecf20Sopenharmony_ci session->leadconn = NULL; 31218c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 31228c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci iscsi_destroy_conn(cls_conn); 31258c2ecf20Sopenharmony_ci kfree(tmp_persistent_address); 31268c2ecf20Sopenharmony_ci kfree(tmp_local_ipaddr); 31278c2ecf20Sopenharmony_ci} 31288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_teardown); 31298c2ecf20Sopenharmony_ci 31308c2ecf20Sopenharmony_ciint iscsi_conn_start(struct iscsi_cls_conn *cls_conn) 31318c2ecf20Sopenharmony_ci{ 31328c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 31338c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 31348c2ecf20Sopenharmony_ci 31358c2ecf20Sopenharmony_ci if (!session) { 31368c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 31378c2ecf20Sopenharmony_ci "can't start unbound connection\n"); 31388c2ecf20Sopenharmony_ci return -EPERM; 31398c2ecf20Sopenharmony_ci } 31408c2ecf20Sopenharmony_ci 31418c2ecf20Sopenharmony_ci if ((session->imm_data_en || !session->initial_r2t_en) && 31428c2ecf20Sopenharmony_ci session->first_burst > session->max_burst) { 31438c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: " 31448c2ecf20Sopenharmony_ci "first_burst %d max_burst %d\n", 31458c2ecf20Sopenharmony_ci session->first_burst, session->max_burst); 31468c2ecf20Sopenharmony_ci return -EINVAL; 31478c2ecf20Sopenharmony_ci } 31488c2ecf20Sopenharmony_ci 31498c2ecf20Sopenharmony_ci if (conn->ping_timeout && !conn->recv_timeout) { 31508c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of " 31518c2ecf20Sopenharmony_ci "zero. Using 5 seconds\n."); 31528c2ecf20Sopenharmony_ci conn->recv_timeout = 5; 31538c2ecf20Sopenharmony_ci } 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci if (conn->recv_timeout && !conn->ping_timeout) { 31568c2ecf20Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of " 31578c2ecf20Sopenharmony_ci "zero. Using 5 seconds.\n"); 31588c2ecf20Sopenharmony_ci conn->ping_timeout = 5; 31598c2ecf20Sopenharmony_ci } 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 31628c2ecf20Sopenharmony_ci conn->c_stage = ISCSI_CONN_STARTED; 31638c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_LOGGED_IN; 31648c2ecf20Sopenharmony_ci session->queued_cmdsn = session->cmdsn; 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci conn->last_recv = jiffies; 31678c2ecf20Sopenharmony_ci conn->last_ping = jiffies; 31688c2ecf20Sopenharmony_ci if (conn->recv_timeout && conn->ping_timeout) 31698c2ecf20Sopenharmony_ci mod_timer(&conn->transport_timer, 31708c2ecf20Sopenharmony_ci jiffies + (conn->recv_timeout * HZ)); 31718c2ecf20Sopenharmony_ci 31728c2ecf20Sopenharmony_ci switch(conn->stop_stage) { 31738c2ecf20Sopenharmony_ci case STOP_CONN_RECOVER: 31748c2ecf20Sopenharmony_ci /* 31758c2ecf20Sopenharmony_ci * unblock eh_abort() if it is blocked. re-try all 31768c2ecf20Sopenharmony_ci * commands after successful recovery 31778c2ecf20Sopenharmony_ci */ 31788c2ecf20Sopenharmony_ci conn->stop_stage = 0; 31798c2ecf20Sopenharmony_ci session->tmf_state = TMF_INITIAL; 31808c2ecf20Sopenharmony_ci session->age++; 31818c2ecf20Sopenharmony_ci if (session->age == 16) 31828c2ecf20Sopenharmony_ci session->age = 0; 31838c2ecf20Sopenharmony_ci break; 31848c2ecf20Sopenharmony_ci case STOP_CONN_TERM: 31858c2ecf20Sopenharmony_ci conn->stop_stage = 0; 31868c2ecf20Sopenharmony_ci break; 31878c2ecf20Sopenharmony_ci default: 31888c2ecf20Sopenharmony_ci break; 31898c2ecf20Sopenharmony_ci } 31908c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_ci iscsi_unblock_session(session->cls_session); 31938c2ecf20Sopenharmony_ci wake_up(&session->ehwait); 31948c2ecf20Sopenharmony_ci return 0; 31958c2ecf20Sopenharmony_ci} 31968c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_start); 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_cistatic void 31998c2ecf20Sopenharmony_cifail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) 32008c2ecf20Sopenharmony_ci{ 32018c2ecf20Sopenharmony_ci struct iscsi_task *task; 32028c2ecf20Sopenharmony_ci int i, state; 32038c2ecf20Sopenharmony_ci 32048c2ecf20Sopenharmony_ci for (i = 0; i < conn->session->cmds_max; i++) { 32058c2ecf20Sopenharmony_ci task = conn->session->cmds[i]; 32068c2ecf20Sopenharmony_ci if (task->sc) 32078c2ecf20Sopenharmony_ci continue; 32088c2ecf20Sopenharmony_ci 32098c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_FREE) 32108c2ecf20Sopenharmony_ci continue; 32118c2ecf20Sopenharmony_ci 32128c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, 32138c2ecf20Sopenharmony_ci "failing mgmt itt 0x%x state %d\n", 32148c2ecf20Sopenharmony_ci task->itt, task->state); 32158c2ecf20Sopenharmony_ci 32168c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 32178c2ecf20Sopenharmony_ci if (cleanup_queued_task(task)) { 32188c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 32198c2ecf20Sopenharmony_ci continue; 32208c2ecf20Sopenharmony_ci } 32218c2ecf20Sopenharmony_ci 32228c2ecf20Sopenharmony_ci state = ISCSI_TASK_ABRT_SESS_RECOV; 32238c2ecf20Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) 32248c2ecf20Sopenharmony_ci state = ISCSI_TASK_COMPLETED; 32258c2ecf20Sopenharmony_ci iscsi_complete_task(task, state); 32268c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 32278c2ecf20Sopenharmony_ci } 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_civoid iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) 32318c2ecf20Sopenharmony_ci{ 32328c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 32338c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 32348c2ecf20Sopenharmony_ci int old_stop_stage; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci mutex_lock(&session->eh_mutex); 32378c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 32388c2ecf20Sopenharmony_ci if (conn->stop_stage == STOP_CONN_TERM) { 32398c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 32408c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 32418c2ecf20Sopenharmony_ci return; 32428c2ecf20Sopenharmony_ci } 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci /* 32458c2ecf20Sopenharmony_ci * When this is called for the in_login state, we only want to clean 32468c2ecf20Sopenharmony_ci * up the login task and connection. We do not need to block and set 32478c2ecf20Sopenharmony_ci * the recovery state again 32488c2ecf20Sopenharmony_ci */ 32498c2ecf20Sopenharmony_ci if (flag == STOP_CONN_TERM) 32508c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_TERMINATE; 32518c2ecf20Sopenharmony_ci else if (conn->stop_stage != STOP_CONN_RECOVER) 32528c2ecf20Sopenharmony_ci session->state = ISCSI_STATE_IN_RECOVERY; 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_ci old_stop_stage = conn->stop_stage; 32558c2ecf20Sopenharmony_ci conn->stop_stage = flag; 32568c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 32578c2ecf20Sopenharmony_ci 32588c2ecf20Sopenharmony_ci del_timer_sync(&conn->transport_timer); 32598c2ecf20Sopenharmony_ci iscsi_suspend_tx(conn); 32608c2ecf20Sopenharmony_ci 32618c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 32628c2ecf20Sopenharmony_ci conn->c_stage = ISCSI_CONN_STOPPED; 32638c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_ci /* 32668c2ecf20Sopenharmony_ci * for connection level recovery we should not calculate 32678c2ecf20Sopenharmony_ci * header digest. conn->hdr_size used for optimization 32688c2ecf20Sopenharmony_ci * in hdr_extract() and will be re-negotiated at 32698c2ecf20Sopenharmony_ci * set_param() time. 32708c2ecf20Sopenharmony_ci */ 32718c2ecf20Sopenharmony_ci if (flag == STOP_CONN_RECOVER) { 32728c2ecf20Sopenharmony_ci conn->hdrdgst_en = 0; 32738c2ecf20Sopenharmony_ci conn->datadgst_en = 0; 32748c2ecf20Sopenharmony_ci if (session->state == ISCSI_STATE_IN_RECOVERY && 32758c2ecf20Sopenharmony_ci old_stop_stage != STOP_CONN_RECOVER) { 32768c2ecf20Sopenharmony_ci ISCSI_DBG_SESSION(session, "blocking session\n"); 32778c2ecf20Sopenharmony_ci iscsi_block_session(session->cls_session); 32788c2ecf20Sopenharmony_ci } 32798c2ecf20Sopenharmony_ci } 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci /* 32828c2ecf20Sopenharmony_ci * flush queues. 32838c2ecf20Sopenharmony_ci */ 32848c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 32858c2ecf20Sopenharmony_ci fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); 32868c2ecf20Sopenharmony_ci fail_mgmt_tasks(session, conn); 32878c2ecf20Sopenharmony_ci memset(&session->tmhdr, 0, sizeof(session->tmhdr)); 32888c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 32898c2ecf20Sopenharmony_ci mutex_unlock(&session->eh_mutex); 32908c2ecf20Sopenharmony_ci} 32918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_stop); 32928c2ecf20Sopenharmony_ci 32938c2ecf20Sopenharmony_ciint iscsi_conn_bind(struct iscsi_cls_session *cls_session, 32948c2ecf20Sopenharmony_ci struct iscsi_cls_conn *cls_conn, int is_leading) 32958c2ecf20Sopenharmony_ci{ 32968c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 32978c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 33008c2ecf20Sopenharmony_ci if (is_leading) 33018c2ecf20Sopenharmony_ci session->leadconn = conn; 33028c2ecf20Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 33038c2ecf20Sopenharmony_ci 33048c2ecf20Sopenharmony_ci /* 33058c2ecf20Sopenharmony_ci * The target could have reduced it's window size between logins, so 33068c2ecf20Sopenharmony_ci * we have to reset max/exp cmdsn so we can see the new values. 33078c2ecf20Sopenharmony_ci */ 33088c2ecf20Sopenharmony_ci spin_lock_bh(&session->back_lock); 33098c2ecf20Sopenharmony_ci session->max_cmdsn = session->exp_cmdsn = session->cmdsn + 1; 33108c2ecf20Sopenharmony_ci spin_unlock_bh(&session->back_lock); 33118c2ecf20Sopenharmony_ci /* 33128c2ecf20Sopenharmony_ci * Unblock xmitworker(), Login Phase will pass through. 33138c2ecf20Sopenharmony_ci */ 33148c2ecf20Sopenharmony_ci clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_rx); 33158c2ecf20Sopenharmony_ci clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx); 33168c2ecf20Sopenharmony_ci return 0; 33178c2ecf20Sopenharmony_ci} 33188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_bind); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ciint iscsi_switch_str_param(char **param, char *new_val_buf) 33218c2ecf20Sopenharmony_ci{ 33228c2ecf20Sopenharmony_ci char *new_val; 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci if (*param) { 33258c2ecf20Sopenharmony_ci if (!strcmp(*param, new_val_buf)) 33268c2ecf20Sopenharmony_ci return 0; 33278c2ecf20Sopenharmony_ci } 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci new_val = kstrdup(new_val_buf, GFP_NOIO); 33308c2ecf20Sopenharmony_ci if (!new_val) 33318c2ecf20Sopenharmony_ci return -ENOMEM; 33328c2ecf20Sopenharmony_ci 33338c2ecf20Sopenharmony_ci kfree(*param); 33348c2ecf20Sopenharmony_ci *param = new_val; 33358c2ecf20Sopenharmony_ci return 0; 33368c2ecf20Sopenharmony_ci} 33378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_switch_str_param); 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ciint iscsi_set_param(struct iscsi_cls_conn *cls_conn, 33408c2ecf20Sopenharmony_ci enum iscsi_param param, char *buf, int buflen) 33418c2ecf20Sopenharmony_ci{ 33428c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 33438c2ecf20Sopenharmony_ci struct iscsi_session *session = conn->session; 33448c2ecf20Sopenharmony_ci int val; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci switch(param) { 33478c2ecf20Sopenharmony_ci case ISCSI_PARAM_FAST_ABORT: 33488c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->fast_abort); 33498c2ecf20Sopenharmony_ci break; 33508c2ecf20Sopenharmony_ci case ISCSI_PARAM_ABORT_TMO: 33518c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->abort_timeout); 33528c2ecf20Sopenharmony_ci break; 33538c2ecf20Sopenharmony_ci case ISCSI_PARAM_LU_RESET_TMO: 33548c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->lu_reset_timeout); 33558c2ecf20Sopenharmony_ci break; 33568c2ecf20Sopenharmony_ci case ISCSI_PARAM_TGT_RESET_TMO: 33578c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->tgt_reset_timeout); 33588c2ecf20Sopenharmony_ci break; 33598c2ecf20Sopenharmony_ci case ISCSI_PARAM_PING_TMO: 33608c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->ping_timeout); 33618c2ecf20Sopenharmony_ci break; 33628c2ecf20Sopenharmony_ci case ISCSI_PARAM_RECV_TMO: 33638c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->recv_timeout); 33648c2ecf20Sopenharmony_ci break; 33658c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_RECV_DLENGTH: 33668c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->max_recv_dlength); 33678c2ecf20Sopenharmony_ci break; 33688c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_XMIT_DLENGTH: 33698c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->max_xmit_dlength); 33708c2ecf20Sopenharmony_ci break; 33718c2ecf20Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 33728c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->hdrdgst_en); 33738c2ecf20Sopenharmony_ci break; 33748c2ecf20Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 33758c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->datadgst_en); 33768c2ecf20Sopenharmony_ci break; 33778c2ecf20Sopenharmony_ci case ISCSI_PARAM_INITIAL_R2T_EN: 33788c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->initial_r2t_en); 33798c2ecf20Sopenharmony_ci break; 33808c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 33818c2ecf20Sopenharmony_ci sscanf(buf, "%hu", &session->max_r2t); 33828c2ecf20Sopenharmony_ci break; 33838c2ecf20Sopenharmony_ci case ISCSI_PARAM_IMM_DATA_EN: 33848c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->imm_data_en); 33858c2ecf20Sopenharmony_ci break; 33868c2ecf20Sopenharmony_ci case ISCSI_PARAM_FIRST_BURST: 33878c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->first_burst); 33888c2ecf20Sopenharmony_ci break; 33898c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_BURST: 33908c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->max_burst); 33918c2ecf20Sopenharmony_ci break; 33928c2ecf20Sopenharmony_ci case ISCSI_PARAM_PDU_INORDER_EN: 33938c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->pdu_inorder_en); 33948c2ecf20Sopenharmony_ci break; 33958c2ecf20Sopenharmony_ci case ISCSI_PARAM_DATASEQ_INORDER_EN: 33968c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->dataseq_inorder_en); 33978c2ecf20Sopenharmony_ci break; 33988c2ecf20Sopenharmony_ci case ISCSI_PARAM_ERL: 33998c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->erl); 34008c2ecf20Sopenharmony_ci break; 34018c2ecf20Sopenharmony_ci case ISCSI_PARAM_EXP_STATSN: 34028c2ecf20Sopenharmony_ci sscanf(buf, "%u", &conn->exp_statsn); 34038c2ecf20Sopenharmony_ci break; 34048c2ecf20Sopenharmony_ci case ISCSI_PARAM_USERNAME: 34058c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->username, buf); 34068c2ecf20Sopenharmony_ci case ISCSI_PARAM_USERNAME_IN: 34078c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->username_in, buf); 34088c2ecf20Sopenharmony_ci case ISCSI_PARAM_PASSWORD: 34098c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->password, buf); 34108c2ecf20Sopenharmony_ci case ISCSI_PARAM_PASSWORD_IN: 34118c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->password_in, buf); 34128c2ecf20Sopenharmony_ci case ISCSI_PARAM_TARGET_NAME: 34138c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->targetname, buf); 34148c2ecf20Sopenharmony_ci case ISCSI_PARAM_TARGET_ALIAS: 34158c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->targetalias, buf); 34168c2ecf20Sopenharmony_ci case ISCSI_PARAM_TPGT: 34178c2ecf20Sopenharmony_ci sscanf(buf, "%d", &session->tpgt); 34188c2ecf20Sopenharmony_ci break; 34198c2ecf20Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_PORT: 34208c2ecf20Sopenharmony_ci sscanf(buf, "%d", &conn->persistent_port); 34218c2ecf20Sopenharmony_ci break; 34228c2ecf20Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_ADDRESS: 34238c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&conn->persistent_address, buf); 34248c2ecf20Sopenharmony_ci case ISCSI_PARAM_IFACE_NAME: 34258c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->ifacename, buf); 34268c2ecf20Sopenharmony_ci case ISCSI_PARAM_INITIATOR_NAME: 34278c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->initiatorname, buf); 34288c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_ROOT: 34298c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->boot_root, buf); 34308c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_NIC: 34318c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->boot_nic, buf); 34328c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_TARGET: 34338c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->boot_target, buf); 34348c2ecf20Sopenharmony_ci case ISCSI_PARAM_PORTAL_TYPE: 34358c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->portal_type, buf); 34368c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: 34378c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&session->discovery_parent_type, 34388c2ecf20Sopenharmony_ci buf); 34398c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_SESS: 34408c2ecf20Sopenharmony_ci sscanf(buf, "%d", &val); 34418c2ecf20Sopenharmony_ci session->discovery_sess = !!val; 34428c2ecf20Sopenharmony_ci break; 34438c2ecf20Sopenharmony_ci case ISCSI_PARAM_LOCAL_IPADDR: 34448c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&conn->local_ipaddr, buf); 34458c2ecf20Sopenharmony_ci default: 34468c2ecf20Sopenharmony_ci return -ENOSYS; 34478c2ecf20Sopenharmony_ci } 34488c2ecf20Sopenharmony_ci 34498c2ecf20Sopenharmony_ci return 0; 34508c2ecf20Sopenharmony_ci} 34518c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_set_param); 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ciint iscsi_session_get_param(struct iscsi_cls_session *cls_session, 34548c2ecf20Sopenharmony_ci enum iscsi_param param, char *buf) 34558c2ecf20Sopenharmony_ci{ 34568c2ecf20Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 34578c2ecf20Sopenharmony_ci int len; 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_ci switch(param) { 34608c2ecf20Sopenharmony_ci case ISCSI_PARAM_FAST_ABORT: 34618c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->fast_abort); 34628c2ecf20Sopenharmony_ci break; 34638c2ecf20Sopenharmony_ci case ISCSI_PARAM_ABORT_TMO: 34648c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->abort_timeout); 34658c2ecf20Sopenharmony_ci break; 34668c2ecf20Sopenharmony_ci case ISCSI_PARAM_LU_RESET_TMO: 34678c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->lu_reset_timeout); 34688c2ecf20Sopenharmony_ci break; 34698c2ecf20Sopenharmony_ci case ISCSI_PARAM_TGT_RESET_TMO: 34708c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->tgt_reset_timeout); 34718c2ecf20Sopenharmony_ci break; 34728c2ecf20Sopenharmony_ci case ISCSI_PARAM_INITIAL_R2T_EN: 34738c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->initial_r2t_en); 34748c2ecf20Sopenharmony_ci break; 34758c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 34768c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", session->max_r2t); 34778c2ecf20Sopenharmony_ci break; 34788c2ecf20Sopenharmony_ci case ISCSI_PARAM_IMM_DATA_EN: 34798c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->imm_data_en); 34808c2ecf20Sopenharmony_ci break; 34818c2ecf20Sopenharmony_ci case ISCSI_PARAM_FIRST_BURST: 34828c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->first_burst); 34838c2ecf20Sopenharmony_ci break; 34848c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_BURST: 34858c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->max_burst); 34868c2ecf20Sopenharmony_ci break; 34878c2ecf20Sopenharmony_ci case ISCSI_PARAM_PDU_INORDER_EN: 34888c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->pdu_inorder_en); 34898c2ecf20Sopenharmony_ci break; 34908c2ecf20Sopenharmony_ci case ISCSI_PARAM_DATASEQ_INORDER_EN: 34918c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->dataseq_inorder_en); 34928c2ecf20Sopenharmony_ci break; 34938c2ecf20Sopenharmony_ci case ISCSI_PARAM_DEF_TASKMGMT_TMO: 34948c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->def_taskmgmt_tmo); 34958c2ecf20Sopenharmony_ci break; 34968c2ecf20Sopenharmony_ci case ISCSI_PARAM_ERL: 34978c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->erl); 34988c2ecf20Sopenharmony_ci break; 34998c2ecf20Sopenharmony_ci case ISCSI_PARAM_TARGET_NAME: 35008c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->targetname); 35018c2ecf20Sopenharmony_ci break; 35028c2ecf20Sopenharmony_ci case ISCSI_PARAM_TARGET_ALIAS: 35038c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->targetalias); 35048c2ecf20Sopenharmony_ci break; 35058c2ecf20Sopenharmony_ci case ISCSI_PARAM_TPGT: 35068c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->tpgt); 35078c2ecf20Sopenharmony_ci break; 35088c2ecf20Sopenharmony_ci case ISCSI_PARAM_USERNAME: 35098c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->username); 35108c2ecf20Sopenharmony_ci break; 35118c2ecf20Sopenharmony_ci case ISCSI_PARAM_USERNAME_IN: 35128c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->username_in); 35138c2ecf20Sopenharmony_ci break; 35148c2ecf20Sopenharmony_ci case ISCSI_PARAM_PASSWORD: 35158c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->password); 35168c2ecf20Sopenharmony_ci break; 35178c2ecf20Sopenharmony_ci case ISCSI_PARAM_PASSWORD_IN: 35188c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->password_in); 35198c2ecf20Sopenharmony_ci break; 35208c2ecf20Sopenharmony_ci case ISCSI_PARAM_IFACE_NAME: 35218c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->ifacename); 35228c2ecf20Sopenharmony_ci break; 35238c2ecf20Sopenharmony_ci case ISCSI_PARAM_INITIATOR_NAME: 35248c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->initiatorname); 35258c2ecf20Sopenharmony_ci break; 35268c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_ROOT: 35278c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_root); 35288c2ecf20Sopenharmony_ci break; 35298c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_NIC: 35308c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_nic); 35318c2ecf20Sopenharmony_ci break; 35328c2ecf20Sopenharmony_ci case ISCSI_PARAM_BOOT_TARGET: 35338c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_target); 35348c2ecf20Sopenharmony_ci break; 35358c2ecf20Sopenharmony_ci case ISCSI_PARAM_AUTO_SND_TGT_DISABLE: 35368c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->auto_snd_tgt_disable); 35378c2ecf20Sopenharmony_ci break; 35388c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_SESS: 35398c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_sess); 35408c2ecf20Sopenharmony_ci break; 35418c2ecf20Sopenharmony_ci case ISCSI_PARAM_PORTAL_TYPE: 35428c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->portal_type); 35438c2ecf20Sopenharmony_ci break; 35448c2ecf20Sopenharmony_ci case ISCSI_PARAM_CHAP_AUTH_EN: 35458c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->chap_auth_en); 35468c2ecf20Sopenharmony_ci break; 35478c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_LOGOUT_EN: 35488c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_logout_en); 35498c2ecf20Sopenharmony_ci break; 35508c2ecf20Sopenharmony_ci case ISCSI_PARAM_BIDI_CHAP_EN: 35518c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->bidi_chap_en); 35528c2ecf20Sopenharmony_ci break; 35538c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL: 35548c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_auth_optional); 35558c2ecf20Sopenharmony_ci break; 35568c2ecf20Sopenharmony_ci case ISCSI_PARAM_DEF_TIME2WAIT: 35578c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->time2wait); 35588c2ecf20Sopenharmony_ci break; 35598c2ecf20Sopenharmony_ci case ISCSI_PARAM_DEF_TIME2RETAIN: 35608c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->time2retain); 35618c2ecf20Sopenharmony_ci break; 35628c2ecf20Sopenharmony_ci case ISCSI_PARAM_TSID: 35638c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->tsid); 35648c2ecf20Sopenharmony_ci break; 35658c2ecf20Sopenharmony_ci case ISCSI_PARAM_ISID: 35668c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", 35678c2ecf20Sopenharmony_ci session->isid[0], session->isid[1], 35688c2ecf20Sopenharmony_ci session->isid[2], session->isid[3], 35698c2ecf20Sopenharmony_ci session->isid[4], session->isid[5]); 35708c2ecf20Sopenharmony_ci break; 35718c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_IDX: 35728c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_parent_idx); 35738c2ecf20Sopenharmony_ci break; 35748c2ecf20Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: 35758c2ecf20Sopenharmony_ci if (session->discovery_parent_type) 35768c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", 35778c2ecf20Sopenharmony_ci session->discovery_parent_type); 35788c2ecf20Sopenharmony_ci else 35798c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "\n"); 35808c2ecf20Sopenharmony_ci break; 35818c2ecf20Sopenharmony_ci default: 35828c2ecf20Sopenharmony_ci return -ENOSYS; 35838c2ecf20Sopenharmony_ci } 35848c2ecf20Sopenharmony_ci 35858c2ecf20Sopenharmony_ci return len; 35868c2ecf20Sopenharmony_ci} 35878c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_get_param); 35888c2ecf20Sopenharmony_ci 35898c2ecf20Sopenharmony_ciint iscsi_conn_get_addr_param(struct sockaddr_storage *addr, 35908c2ecf20Sopenharmony_ci enum iscsi_param param, char *buf) 35918c2ecf20Sopenharmony_ci{ 35928c2ecf20Sopenharmony_ci struct sockaddr_in6 *sin6 = NULL; 35938c2ecf20Sopenharmony_ci struct sockaddr_in *sin = NULL; 35948c2ecf20Sopenharmony_ci int len; 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_ci switch (addr->ss_family) { 35978c2ecf20Sopenharmony_ci case AF_INET: 35988c2ecf20Sopenharmony_ci sin = (struct sockaddr_in *)addr; 35998c2ecf20Sopenharmony_ci break; 36008c2ecf20Sopenharmony_ci case AF_INET6: 36018c2ecf20Sopenharmony_ci sin6 = (struct sockaddr_in6 *)addr; 36028c2ecf20Sopenharmony_ci break; 36038c2ecf20Sopenharmony_ci default: 36048c2ecf20Sopenharmony_ci return -EINVAL; 36058c2ecf20Sopenharmony_ci } 36068c2ecf20Sopenharmony_ci 36078c2ecf20Sopenharmony_ci switch (param) { 36088c2ecf20Sopenharmony_ci case ISCSI_PARAM_CONN_ADDRESS: 36098c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_IPADDRESS: 36108c2ecf20Sopenharmony_ci if (sin) 36118c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%pI4\n", &sin->sin_addr.s_addr); 36128c2ecf20Sopenharmony_ci else 36138c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%pI6\n", &sin6->sin6_addr); 36148c2ecf20Sopenharmony_ci break; 36158c2ecf20Sopenharmony_ci case ISCSI_PARAM_CONN_PORT: 36168c2ecf20Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 36178c2ecf20Sopenharmony_ci if (sin) 36188c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", be16_to_cpu(sin->sin_port)); 36198c2ecf20Sopenharmony_ci else 36208c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", 36218c2ecf20Sopenharmony_ci be16_to_cpu(sin6->sin6_port)); 36228c2ecf20Sopenharmony_ci break; 36238c2ecf20Sopenharmony_ci default: 36248c2ecf20Sopenharmony_ci return -EINVAL; 36258c2ecf20Sopenharmony_ci } 36268c2ecf20Sopenharmony_ci 36278c2ecf20Sopenharmony_ci return len; 36288c2ecf20Sopenharmony_ci} 36298c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_get_addr_param); 36308c2ecf20Sopenharmony_ci 36318c2ecf20Sopenharmony_ciint iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, 36328c2ecf20Sopenharmony_ci enum iscsi_param param, char *buf) 36338c2ecf20Sopenharmony_ci{ 36348c2ecf20Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 36358c2ecf20Sopenharmony_ci int len; 36368c2ecf20Sopenharmony_ci 36378c2ecf20Sopenharmony_ci switch(param) { 36388c2ecf20Sopenharmony_ci case ISCSI_PARAM_PING_TMO: 36398c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ping_timeout); 36408c2ecf20Sopenharmony_ci break; 36418c2ecf20Sopenharmony_ci case ISCSI_PARAM_RECV_TMO: 36428c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->recv_timeout); 36438c2ecf20Sopenharmony_ci break; 36448c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_RECV_DLENGTH: 36458c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_recv_dlength); 36468c2ecf20Sopenharmony_ci break; 36478c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_XMIT_DLENGTH: 36488c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_xmit_dlength); 36498c2ecf20Sopenharmony_ci break; 36508c2ecf20Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 36518c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->hdrdgst_en); 36528c2ecf20Sopenharmony_ci break; 36538c2ecf20Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 36548c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->datadgst_en); 36558c2ecf20Sopenharmony_ci break; 36568c2ecf20Sopenharmony_ci case ISCSI_PARAM_IFMARKER_EN: 36578c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->ifmarker_en); 36588c2ecf20Sopenharmony_ci break; 36598c2ecf20Sopenharmony_ci case ISCSI_PARAM_OFMARKER_EN: 36608c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->ofmarker_en); 36618c2ecf20Sopenharmony_ci break; 36628c2ecf20Sopenharmony_ci case ISCSI_PARAM_EXP_STATSN: 36638c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->exp_statsn); 36648c2ecf20Sopenharmony_ci break; 36658c2ecf20Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_PORT: 36668c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->persistent_port); 36678c2ecf20Sopenharmony_ci break; 36688c2ecf20Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_ADDRESS: 36698c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", conn->persistent_address); 36708c2ecf20Sopenharmony_ci break; 36718c2ecf20Sopenharmony_ci case ISCSI_PARAM_STATSN: 36728c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->statsn); 36738c2ecf20Sopenharmony_ci break; 36748c2ecf20Sopenharmony_ci case ISCSI_PARAM_MAX_SEGMENT_SIZE: 36758c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_segment_size); 36768c2ecf20Sopenharmony_ci break; 36778c2ecf20Sopenharmony_ci case ISCSI_PARAM_KEEPALIVE_TMO: 36788c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->keepalive_tmo); 36798c2ecf20Sopenharmony_ci break; 36808c2ecf20Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 36818c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->local_port); 36828c2ecf20Sopenharmony_ci break; 36838c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_TIMESTAMP_STAT: 36848c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_stat); 36858c2ecf20Sopenharmony_ci break; 36868c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_NAGLE_DISABLE: 36878c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_nagle_disable); 36888c2ecf20Sopenharmony_ci break; 36898c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_WSF_DISABLE: 36908c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_wsf_disable); 36918c2ecf20Sopenharmony_ci break; 36928c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_TIMER_SCALE: 36938c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timer_scale); 36948c2ecf20Sopenharmony_ci break; 36958c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_TIMESTAMP_EN: 36968c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_en); 36978c2ecf20Sopenharmony_ci break; 36988c2ecf20Sopenharmony_ci case ISCSI_PARAM_IP_FRAGMENT_DISABLE: 36998c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->fragment_disable); 37008c2ecf20Sopenharmony_ci break; 37018c2ecf20Sopenharmony_ci case ISCSI_PARAM_IPV4_TOS: 37028c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv4_tos); 37038c2ecf20Sopenharmony_ci break; 37048c2ecf20Sopenharmony_ci case ISCSI_PARAM_IPV6_TC: 37058c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv6_traffic_class); 37068c2ecf20Sopenharmony_ci break; 37078c2ecf20Sopenharmony_ci case ISCSI_PARAM_IPV6_FLOW_LABEL: 37088c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv6_flow_label); 37098c2ecf20Sopenharmony_ci break; 37108c2ecf20Sopenharmony_ci case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6: 37118c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->is_fw_assigned_ipv6); 37128c2ecf20Sopenharmony_ci break; 37138c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_XMIT_WSF: 37148c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_xmit_wsf); 37158c2ecf20Sopenharmony_ci break; 37168c2ecf20Sopenharmony_ci case ISCSI_PARAM_TCP_RECV_WSF: 37178c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_recv_wsf); 37188c2ecf20Sopenharmony_ci break; 37198c2ecf20Sopenharmony_ci case ISCSI_PARAM_LOCAL_IPADDR: 37208c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", conn->local_ipaddr); 37218c2ecf20Sopenharmony_ci break; 37228c2ecf20Sopenharmony_ci default: 37238c2ecf20Sopenharmony_ci return -ENOSYS; 37248c2ecf20Sopenharmony_ci } 37258c2ecf20Sopenharmony_ci 37268c2ecf20Sopenharmony_ci return len; 37278c2ecf20Sopenharmony_ci} 37288c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_get_param); 37298c2ecf20Sopenharmony_ci 37308c2ecf20Sopenharmony_ciint iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, 37318c2ecf20Sopenharmony_ci char *buf) 37328c2ecf20Sopenharmony_ci{ 37338c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 37348c2ecf20Sopenharmony_ci int len; 37358c2ecf20Sopenharmony_ci 37368c2ecf20Sopenharmony_ci switch (param) { 37378c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_NETDEV_NAME: 37388c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->netdev); 37398c2ecf20Sopenharmony_ci break; 37408c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_HWADDRESS: 37418c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->hwaddress); 37428c2ecf20Sopenharmony_ci break; 37438c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_INITIATOR_NAME: 37448c2ecf20Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->initiatorname); 37458c2ecf20Sopenharmony_ci break; 37468c2ecf20Sopenharmony_ci default: 37478c2ecf20Sopenharmony_ci return -ENOSYS; 37488c2ecf20Sopenharmony_ci } 37498c2ecf20Sopenharmony_ci 37508c2ecf20Sopenharmony_ci return len; 37518c2ecf20Sopenharmony_ci} 37528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_get_param); 37538c2ecf20Sopenharmony_ci 37548c2ecf20Sopenharmony_ciint iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param, 37558c2ecf20Sopenharmony_ci char *buf, int buflen) 37568c2ecf20Sopenharmony_ci{ 37578c2ecf20Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 37588c2ecf20Sopenharmony_ci 37598c2ecf20Sopenharmony_ci switch (param) { 37608c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_NETDEV_NAME: 37618c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&ihost->netdev, buf); 37628c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_HWADDRESS: 37638c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&ihost->hwaddress, buf); 37648c2ecf20Sopenharmony_ci case ISCSI_HOST_PARAM_INITIATOR_NAME: 37658c2ecf20Sopenharmony_ci return iscsi_switch_str_param(&ihost->initiatorname, buf); 37668c2ecf20Sopenharmony_ci default: 37678c2ecf20Sopenharmony_ci return -ENOSYS; 37688c2ecf20Sopenharmony_ci } 37698c2ecf20Sopenharmony_ci 37708c2ecf20Sopenharmony_ci return 0; 37718c2ecf20Sopenharmony_ci} 37728c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_set_param); 37738c2ecf20Sopenharmony_ci 37748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Mike Christie"); 37758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("iSCSI library functions"); 37768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 3777