162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * iSCSI lib functions 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2006 Red Hat, Inc. All rights reserved. 662306a36Sopenharmony_ci * Copyright (C) 2004 - 2006 Mike Christie 762306a36Sopenharmony_ci * Copyright (C) 2004 - 2005 Dmitry Yusupov 862306a36Sopenharmony_ci * Copyright (C) 2004 - 2005 Alex Aizman 962306a36Sopenharmony_ci * maintained by open-iscsi@googlegroups.com 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci#include <linux/types.h> 1262306a36Sopenharmony_ci#include <linux/kfifo.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/log2.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <linux/sched/signal.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <asm/unaligned.h> 1962306a36Sopenharmony_ci#include <net/tcp.h> 2062306a36Sopenharmony_ci#include <scsi/scsi_cmnd.h> 2162306a36Sopenharmony_ci#include <scsi/scsi_device.h> 2262306a36Sopenharmony_ci#include <scsi/scsi_eh.h> 2362306a36Sopenharmony_ci#include <scsi/scsi_tcq.h> 2462306a36Sopenharmony_ci#include <scsi/scsi_host.h> 2562306a36Sopenharmony_ci#include <scsi/scsi.h> 2662306a36Sopenharmony_ci#include <scsi/iscsi_proto.h> 2762306a36Sopenharmony_ci#include <scsi/scsi_transport.h> 2862306a36Sopenharmony_ci#include <scsi/scsi_transport_iscsi.h> 2962306a36Sopenharmony_ci#include <scsi/libiscsi.h> 3062306a36Sopenharmony_ci#include <trace/events/iscsi.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic int iscsi_dbg_lib_conn; 3362306a36Sopenharmony_cimodule_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, 3462306a36Sopenharmony_ci S_IRUGO | S_IWUSR); 3562306a36Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_conn, 3662306a36Sopenharmony_ci "Turn on debugging for connections in libiscsi module. " 3762306a36Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistatic int iscsi_dbg_lib_session; 4062306a36Sopenharmony_cimodule_param_named(debug_libiscsi_session, iscsi_dbg_lib_session, int, 4162306a36Sopenharmony_ci S_IRUGO | S_IWUSR); 4262306a36Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_session, 4362306a36Sopenharmony_ci "Turn on debugging for sessions in libiscsi module. " 4462306a36Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_cistatic int iscsi_dbg_lib_eh; 4762306a36Sopenharmony_cimodule_param_named(debug_libiscsi_eh, iscsi_dbg_lib_eh, int, 4862306a36Sopenharmony_ci S_IRUGO | S_IWUSR); 4962306a36Sopenharmony_ciMODULE_PARM_DESC(debug_libiscsi_eh, 5062306a36Sopenharmony_ci "Turn on debugging for error handling in libiscsi module. " 5162306a36Sopenharmony_ci "Set to 1 to turn on, and zero to turn off. Default is off."); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci#define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \ 5462306a36Sopenharmony_ci do { \ 5562306a36Sopenharmony_ci if (iscsi_dbg_lib_conn) \ 5662306a36Sopenharmony_ci iscsi_conn_printk(KERN_INFO, _conn, \ 5762306a36Sopenharmony_ci "%s " dbg_fmt, \ 5862306a36Sopenharmony_ci __func__, ##arg); \ 5962306a36Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_conn, \ 6062306a36Sopenharmony_ci &(_conn)->cls_conn->dev, \ 6162306a36Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg);\ 6262306a36Sopenharmony_ci } while (0); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ 6562306a36Sopenharmony_ci do { \ 6662306a36Sopenharmony_ci if (iscsi_dbg_lib_session) \ 6762306a36Sopenharmony_ci iscsi_session_printk(KERN_INFO, _session, \ 6862306a36Sopenharmony_ci "%s " dbg_fmt, \ 6962306a36Sopenharmony_ci __func__, ##arg); \ 7062306a36Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_session, \ 7162306a36Sopenharmony_ci &(_session)->cls_session->dev, \ 7262306a36Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg); \ 7362306a36Sopenharmony_ci } while (0); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ 7662306a36Sopenharmony_ci do { \ 7762306a36Sopenharmony_ci if (iscsi_dbg_lib_eh) \ 7862306a36Sopenharmony_ci iscsi_session_printk(KERN_INFO, _session, \ 7962306a36Sopenharmony_ci "%s " dbg_fmt, \ 8062306a36Sopenharmony_ci __func__, ##arg); \ 8162306a36Sopenharmony_ci iscsi_dbg_trace(trace_iscsi_dbg_eh, \ 8262306a36Sopenharmony_ci &(_session)->cls_session->dev, \ 8362306a36Sopenharmony_ci "%s " dbg_fmt, __func__, ##arg); \ 8462306a36Sopenharmony_ci } while (0); 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci#define ISCSI_CMD_COMPL_WAIT 5 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ciinline void iscsi_conn_queue_xmit(struct iscsi_conn *conn) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 9162306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci if (ihost->workq) 9462306a36Sopenharmony_ci queue_work(ihost->workq, &conn->xmitwork); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_queue_xmit); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ciinline void iscsi_conn_queue_recv(struct iscsi_conn *conn) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 10162306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci if (ihost->workq && !test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags)) 10462306a36Sopenharmony_ci queue_work(ihost->workq, &conn->recvwork); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_queue_recv); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void __iscsi_update_cmdsn(struct iscsi_session *session, 10962306a36Sopenharmony_ci uint32_t exp_cmdsn, uint32_t max_cmdsn) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci /* 11262306a36Sopenharmony_ci * standard specifies this check for when to update expected and 11362306a36Sopenharmony_ci * max sequence numbers 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_ci if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1)) 11662306a36Sopenharmony_ci return; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (exp_cmdsn != session->exp_cmdsn && 11962306a36Sopenharmony_ci !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn)) 12062306a36Sopenharmony_ci session->exp_cmdsn = exp_cmdsn; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci if (max_cmdsn != session->max_cmdsn && 12362306a36Sopenharmony_ci !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) 12462306a36Sopenharmony_ci session->max_cmdsn = max_cmdsn; 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_civoid iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), 13062306a36Sopenharmony_ci be32_to_cpu(hdr->max_cmdsn)); 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_update_cmdsn); 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci/** 13562306a36Sopenharmony_ci * iscsi_prep_data_out_pdu - initialize Data-Out 13662306a36Sopenharmony_ci * @task: scsi command task 13762306a36Sopenharmony_ci * @r2t: R2T info 13862306a36Sopenharmony_ci * @hdr: iscsi data in pdu 13962306a36Sopenharmony_ci * 14062306a36Sopenharmony_ci * Notes: 14162306a36Sopenharmony_ci * Initialize Data-Out within this R2T sequence and finds 14262306a36Sopenharmony_ci * proper data_offset within this SCSI command. 14362306a36Sopenharmony_ci * 14462306a36Sopenharmony_ci * This function is called with connection lock taken. 14562306a36Sopenharmony_ci **/ 14662306a36Sopenharmony_civoid iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t, 14762306a36Sopenharmony_ci struct iscsi_data *hdr) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 15062306a36Sopenharmony_ci unsigned int left = r2t->data_length - r2t->sent; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci task->hdr_len = sizeof(struct iscsi_data); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci memset(hdr, 0, sizeof(struct iscsi_data)); 15562306a36Sopenharmony_ci hdr->ttt = r2t->ttt; 15662306a36Sopenharmony_ci hdr->datasn = cpu_to_be32(r2t->datasn); 15762306a36Sopenharmony_ci r2t->datasn++; 15862306a36Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; 15962306a36Sopenharmony_ci hdr->lun = task->lun; 16062306a36Sopenharmony_ci hdr->itt = task->hdr_itt; 16162306a36Sopenharmony_ci hdr->exp_statsn = r2t->exp_statsn; 16262306a36Sopenharmony_ci hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent); 16362306a36Sopenharmony_ci if (left > conn->max_xmit_dlength) { 16462306a36Sopenharmony_ci hton24(hdr->dlength, conn->max_xmit_dlength); 16562306a36Sopenharmony_ci r2t->data_count = conn->max_xmit_dlength; 16662306a36Sopenharmony_ci hdr->flags = 0; 16762306a36Sopenharmony_ci } else { 16862306a36Sopenharmony_ci hton24(hdr->dlength, left); 16962306a36Sopenharmony_ci r2t->data_count = left; 17062306a36Sopenharmony_ci hdr->flags = ISCSI_FLAG_CMD_FINAL; 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci conn->dataout_pdus_cnt++; 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic int iscsi_add_hdr(struct iscsi_task *task, unsigned len) 17762306a36Sopenharmony_ci{ 17862306a36Sopenharmony_ci unsigned exp_len = task->hdr_len + len; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (exp_len > task->hdr_max) { 18162306a36Sopenharmony_ci WARN_ON(1); 18262306a36Sopenharmony_ci return -EINVAL; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ 18662306a36Sopenharmony_ci task->hdr_len = exp_len; 18762306a36Sopenharmony_ci return 0; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci/* 19162306a36Sopenharmony_ci * make an extended cdb AHS 19262306a36Sopenharmony_ci */ 19362306a36Sopenharmony_cistatic int iscsi_prep_ecdb_ahs(struct iscsi_task *task) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci struct scsi_cmnd *cmd = task->sc; 19662306a36Sopenharmony_ci unsigned rlen, pad_len; 19762306a36Sopenharmony_ci unsigned short ahslength; 19862306a36Sopenharmony_ci struct iscsi_ecdb_ahdr *ecdb_ahdr; 19962306a36Sopenharmony_ci int rc; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci ecdb_ahdr = iscsi_next_hdr(task); 20262306a36Sopenharmony_ci rlen = cmd->cmd_len - ISCSI_CDB_SIZE; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb)); 20562306a36Sopenharmony_ci ahslength = rlen + sizeof(ecdb_ahdr->reserved); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci pad_len = iscsi_padding(rlen); 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) + 21062306a36Sopenharmony_ci sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len); 21162306a36Sopenharmony_ci if (rc) 21262306a36Sopenharmony_ci return rc; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci if (pad_len) 21562306a36Sopenharmony_ci memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len); 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci ecdb_ahdr->ahslength = cpu_to_be16(ahslength); 21862306a36Sopenharmony_ci ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB; 21962306a36Sopenharmony_ci ecdb_ahdr->reserved = 0; 22062306a36Sopenharmony_ci memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci ISCSI_DBG_SESSION(task->conn->session, 22362306a36Sopenharmony_ci "iscsi_prep_ecdb_ahs: varlen_cdb_len %d " 22462306a36Sopenharmony_ci "rlen %d pad_len %d ahs_length %d iscsi_headers_size " 22562306a36Sopenharmony_ci "%u\n", cmd->cmd_len, rlen, pad_len, ahslength, 22662306a36Sopenharmony_ci task->hdr_len); 22762306a36Sopenharmony_ci return 0; 22862306a36Sopenharmony_ci} 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci/** 23162306a36Sopenharmony_ci * iscsi_check_tmf_restrictions - check if a task is affected by TMF 23262306a36Sopenharmony_ci * @task: iscsi task 23362306a36Sopenharmony_ci * @opcode: opcode to check for 23462306a36Sopenharmony_ci * 23562306a36Sopenharmony_ci * During TMF a task has to be checked if it's affected. 23662306a36Sopenharmony_ci * All unrelated I/O can be passed through, but I/O to the 23762306a36Sopenharmony_ci * affected LUN should be restricted. 23862306a36Sopenharmony_ci * If 'fast_abort' is set we won't be sending any I/O to the 23962306a36Sopenharmony_ci * affected LUN. 24062306a36Sopenharmony_ci * Otherwise the target is waiting for all TTTs to be completed, 24162306a36Sopenharmony_ci * so we have to send all outstanding Data-Out PDUs to the target. 24262306a36Sopenharmony_ci */ 24362306a36Sopenharmony_cistatic int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci struct iscsi_session *session = task->conn->session; 24662306a36Sopenharmony_ci struct iscsi_tm *tmf = &session->tmhdr; 24762306a36Sopenharmony_ci u64 hdr_lun; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci if (session->tmf_state == TMF_INITIAL) 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) 25362306a36Sopenharmony_ci return 0; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci switch (ISCSI_TM_FUNC_VALUE(tmf)) { 25662306a36Sopenharmony_ci case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: 25762306a36Sopenharmony_ci /* 25862306a36Sopenharmony_ci * Allow PDUs for unrelated LUNs 25962306a36Sopenharmony_ci */ 26062306a36Sopenharmony_ci hdr_lun = scsilun_to_int(&tmf->lun); 26162306a36Sopenharmony_ci if (hdr_lun != task->sc->device->lun) 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci fallthrough; 26462306a36Sopenharmony_ci case ISCSI_TM_FUNC_TARGET_WARM_RESET: 26562306a36Sopenharmony_ci /* 26662306a36Sopenharmony_ci * Fail all SCSI cmd PDUs 26762306a36Sopenharmony_ci */ 26862306a36Sopenharmony_ci if (opcode != ISCSI_OP_SCSI_DATA_OUT) { 26962306a36Sopenharmony_ci iscsi_session_printk(KERN_INFO, session, 27062306a36Sopenharmony_ci "task [op %x itt 0x%x/0x%x] rejected.\n", 27162306a36Sopenharmony_ci opcode, task->itt, task->hdr_itt); 27262306a36Sopenharmony_ci return -EACCES; 27362306a36Sopenharmony_ci } 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * And also all data-out PDUs in response to R2T 27662306a36Sopenharmony_ci * if fast_abort is set. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci if (session->fast_abort) { 27962306a36Sopenharmony_ci iscsi_session_printk(KERN_INFO, session, 28062306a36Sopenharmony_ci "task [op %x itt 0x%x/0x%x] fast abort.\n", 28162306a36Sopenharmony_ci opcode, task->itt, task->hdr_itt); 28262306a36Sopenharmony_ci return -EACCES; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci case ISCSI_TM_FUNC_ABORT_TASK: 28662306a36Sopenharmony_ci /* 28762306a36Sopenharmony_ci * the caller has already checked if the task 28862306a36Sopenharmony_ci * they want to abort was in the pending queue so if 28962306a36Sopenharmony_ci * we are here the cmd pdu has gone out already, and 29062306a36Sopenharmony_ci * we will only hit this for data-outs 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_ci if (opcode == ISCSI_OP_SCSI_DATA_OUT && 29362306a36Sopenharmony_ci task->hdr_itt == tmf->rtt) { 29462306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, 29562306a36Sopenharmony_ci "Preventing task %x/%x from sending " 29662306a36Sopenharmony_ci "data-out due to abort task in " 29762306a36Sopenharmony_ci "progress\n", task->itt, 29862306a36Sopenharmony_ci task->hdr_itt); 29962306a36Sopenharmony_ci return -EACCES; 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci break; 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci/** 30862306a36Sopenharmony_ci * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu 30962306a36Sopenharmony_ci * @task: iscsi task 31062306a36Sopenharmony_ci * 31162306a36Sopenharmony_ci * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set 31262306a36Sopenharmony_ci * fields like dlength or final based on how much data it sends 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_cistatic int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 31762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 31862306a36Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 31962306a36Sopenharmony_ci struct iscsi_scsi_req *hdr; 32062306a36Sopenharmony_ci unsigned hdrlength, cmd_len, transfer_length; 32162306a36Sopenharmony_ci itt_t itt; 32262306a36Sopenharmony_ci int rc; 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); 32562306a36Sopenharmony_ci if (rc) 32662306a36Sopenharmony_ci return rc; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci if (conn->session->tt->alloc_pdu) { 32962306a36Sopenharmony_ci rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); 33062306a36Sopenharmony_ci if (rc) 33162306a36Sopenharmony_ci return rc; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci hdr = (struct iscsi_scsi_req *)task->hdr; 33462306a36Sopenharmony_ci itt = hdr->itt; 33562306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci if (session->tt->parse_pdu_itt) 33862306a36Sopenharmony_ci hdr->itt = task->hdr_itt = itt; 33962306a36Sopenharmony_ci else 34062306a36Sopenharmony_ci hdr->itt = task->hdr_itt = build_itt(task->itt, 34162306a36Sopenharmony_ci task->conn->session->age); 34262306a36Sopenharmony_ci task->hdr_len = 0; 34362306a36Sopenharmony_ci rc = iscsi_add_hdr(task, sizeof(*hdr)); 34462306a36Sopenharmony_ci if (rc) 34562306a36Sopenharmony_ci return rc; 34662306a36Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_CMD; 34762306a36Sopenharmony_ci hdr->flags = ISCSI_ATTR_SIMPLE; 34862306a36Sopenharmony_ci int_to_scsilun(sc->device->lun, &hdr->lun); 34962306a36Sopenharmony_ci task->lun = hdr->lun; 35062306a36Sopenharmony_ci hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); 35162306a36Sopenharmony_ci cmd_len = sc->cmd_len; 35262306a36Sopenharmony_ci if (cmd_len < ISCSI_CDB_SIZE) 35362306a36Sopenharmony_ci memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len); 35462306a36Sopenharmony_ci else if (cmd_len > ISCSI_CDB_SIZE) { 35562306a36Sopenharmony_ci rc = iscsi_prep_ecdb_ahs(task); 35662306a36Sopenharmony_ci if (rc) 35762306a36Sopenharmony_ci return rc; 35862306a36Sopenharmony_ci cmd_len = ISCSI_CDB_SIZE; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci memcpy(hdr->cdb, sc->cmnd, cmd_len); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci task->imm_count = 0; 36362306a36Sopenharmony_ci if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) 36462306a36Sopenharmony_ci task->protected = true; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci transfer_length = scsi_transfer_length(sc); 36762306a36Sopenharmony_ci hdr->data_length = cpu_to_be32(transfer_length); 36862306a36Sopenharmony_ci if (sc->sc_data_direction == DMA_TO_DEVICE) { 36962306a36Sopenharmony_ci struct iscsi_r2t_info *r2t = &task->unsol_r2t; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_WRITE; 37262306a36Sopenharmony_ci /* 37362306a36Sopenharmony_ci * Write counters: 37462306a36Sopenharmony_ci * 37562306a36Sopenharmony_ci * imm_count bytes to be sent right after 37662306a36Sopenharmony_ci * SCSI PDU Header 37762306a36Sopenharmony_ci * 37862306a36Sopenharmony_ci * unsol_count bytes(as Data-Out) to be sent 37962306a36Sopenharmony_ci * without R2T ack right after 38062306a36Sopenharmony_ci * immediate data 38162306a36Sopenharmony_ci * 38262306a36Sopenharmony_ci * r2t data_length bytes to be sent via R2T ack's 38362306a36Sopenharmony_ci * 38462306a36Sopenharmony_ci * pad_count bytes to be sent as zero-padding 38562306a36Sopenharmony_ci */ 38662306a36Sopenharmony_ci memset(r2t, 0, sizeof(*r2t)); 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci if (session->imm_data_en) { 38962306a36Sopenharmony_ci if (transfer_length >= session->first_burst) 39062306a36Sopenharmony_ci task->imm_count = min(session->first_burst, 39162306a36Sopenharmony_ci conn->max_xmit_dlength); 39262306a36Sopenharmony_ci else 39362306a36Sopenharmony_ci task->imm_count = min(transfer_length, 39462306a36Sopenharmony_ci conn->max_xmit_dlength); 39562306a36Sopenharmony_ci hton24(hdr->dlength, task->imm_count); 39662306a36Sopenharmony_ci } else 39762306a36Sopenharmony_ci zero_data(hdr->dlength); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (!session->initial_r2t_en) { 40062306a36Sopenharmony_ci r2t->data_length = min(session->first_burst, 40162306a36Sopenharmony_ci transfer_length) - 40262306a36Sopenharmony_ci task->imm_count; 40362306a36Sopenharmony_ci r2t->data_offset = task->imm_count; 40462306a36Sopenharmony_ci r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); 40562306a36Sopenharmony_ci r2t->exp_statsn = cpu_to_be32(conn->exp_statsn); 40662306a36Sopenharmony_ci } 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci if (!task->unsol_r2t.data_length) 40962306a36Sopenharmony_ci /* No unsolicit Data-Out's */ 41062306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 41162306a36Sopenharmony_ci } else { 41262306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 41362306a36Sopenharmony_ci zero_data(hdr->dlength); 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci if (sc->sc_data_direction == DMA_FROM_DEVICE) 41662306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_READ; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* calculate size of additional header segments (AHSs) */ 42062306a36Sopenharmony_ci hdrlength = task->hdr_len - sizeof(*hdr); 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); 42362306a36Sopenharmony_ci hdrlength /= ISCSI_PAD_LEN; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci WARN_ON(hdrlength >= 256); 42662306a36Sopenharmony_ci hdr->hlength = hdrlength & 0xFF; 42762306a36Sopenharmony_ci hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci if (session->tt->init_task && session->tt->init_task(task)) 43062306a36Sopenharmony_ci return -EIO; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci task->state = ISCSI_TASK_RUNNING; 43362306a36Sopenharmony_ci session->cmdsn++; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci conn->scsicmd_pdus_cnt++; 43662306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " 43762306a36Sopenharmony_ci "itt 0x%x len %d cmdsn %d win %d]\n", 43862306a36Sopenharmony_ci sc->sc_data_direction == DMA_TO_DEVICE ? 43962306a36Sopenharmony_ci "write" : "read", conn->id, sc, sc->cmnd[0], 44062306a36Sopenharmony_ci task->itt, transfer_length, 44162306a36Sopenharmony_ci session->cmdsn, 44262306a36Sopenharmony_ci session->max_cmdsn - session->exp_cmdsn + 1); 44362306a36Sopenharmony_ci return 0; 44462306a36Sopenharmony_ci} 44562306a36Sopenharmony_ci 44662306a36Sopenharmony_ci/** 44762306a36Sopenharmony_ci * iscsi_free_task - free a task 44862306a36Sopenharmony_ci * @task: iscsi cmd task 44962306a36Sopenharmony_ci * 45062306a36Sopenharmony_ci * Must be called with session back_lock. 45162306a36Sopenharmony_ci * This function returns the scsi command to scsi-ml or cleans 45262306a36Sopenharmony_ci * up mgmt tasks then returns the task to the pool. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic void iscsi_free_task(struct iscsi_task *task) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 45762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 45862306a36Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 45962306a36Sopenharmony_ci int oldstate = task->state; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", 46262306a36Sopenharmony_ci task->itt, task->state, task->sc); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci session->tt->cleanup_task(task); 46562306a36Sopenharmony_ci task->state = ISCSI_TASK_FREE; 46662306a36Sopenharmony_ci task->sc = NULL; 46762306a36Sopenharmony_ci /* 46862306a36Sopenharmony_ci * login task is preallocated so do not free 46962306a36Sopenharmony_ci */ 47062306a36Sopenharmony_ci if (conn->login_task == task) 47162306a36Sopenharmony_ci return; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci if (sc) { 47662306a36Sopenharmony_ci /* SCSI eh reuses commands to verify us */ 47762306a36Sopenharmony_ci iscsi_cmd(sc)->task = NULL; 47862306a36Sopenharmony_ci /* 47962306a36Sopenharmony_ci * queue command may call this to free the task, so 48062306a36Sopenharmony_ci * it will decide how to return sc to scsi-ml. 48162306a36Sopenharmony_ci */ 48262306a36Sopenharmony_ci if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) 48362306a36Sopenharmony_ci scsi_done(sc); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cibool iscsi_get_task(struct iscsi_task *task) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci return refcount_inc_not_zero(&task->refcount); 49062306a36Sopenharmony_ci} 49162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_get_task); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci/** 49462306a36Sopenharmony_ci * __iscsi_put_task - drop the refcount on a task 49562306a36Sopenharmony_ci * @task: iscsi_task to drop the refcount on 49662306a36Sopenharmony_ci * 49762306a36Sopenharmony_ci * The back_lock must be held when calling in case it frees the task. 49862306a36Sopenharmony_ci */ 49962306a36Sopenharmony_civoid __iscsi_put_task(struct iscsi_task *task) 50062306a36Sopenharmony_ci{ 50162306a36Sopenharmony_ci if (refcount_dec_and_test(&task->refcount)) 50262306a36Sopenharmony_ci iscsi_free_task(task); 50362306a36Sopenharmony_ci} 50462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__iscsi_put_task); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_civoid iscsi_put_task(struct iscsi_task *task) 50762306a36Sopenharmony_ci{ 50862306a36Sopenharmony_ci struct iscsi_session *session = task->conn->session; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci if (refcount_dec_and_test(&task->refcount)) { 51162306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 51262306a36Sopenharmony_ci iscsi_free_task(task); 51362306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 51462306a36Sopenharmony_ci } 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_put_task); 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci/** 51962306a36Sopenharmony_ci * iscsi_complete_task - finish a task 52062306a36Sopenharmony_ci * @task: iscsi cmd task 52162306a36Sopenharmony_ci * @state: state to complete task with 52262306a36Sopenharmony_ci * 52362306a36Sopenharmony_ci * Must be called with session back_lock. 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_cistatic void iscsi_complete_task(struct iscsi_task *task, int state) 52662306a36Sopenharmony_ci{ 52762306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, 53062306a36Sopenharmony_ci "complete task itt 0x%x state %d sc %p\n", 53162306a36Sopenharmony_ci task->itt, task->state, task->sc); 53262306a36Sopenharmony_ci if (task->state == ISCSI_TASK_COMPLETED || 53362306a36Sopenharmony_ci task->state == ISCSI_TASK_ABRT_TMF || 53462306a36Sopenharmony_ci task->state == ISCSI_TASK_ABRT_SESS_RECOV || 53562306a36Sopenharmony_ci task->state == ISCSI_TASK_REQUEUE_SCSIQ) 53662306a36Sopenharmony_ci return; 53762306a36Sopenharmony_ci WARN_ON_ONCE(task->state == ISCSI_TASK_FREE); 53862306a36Sopenharmony_ci task->state = state; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci if (READ_ONCE(conn->ping_task) == task) 54162306a36Sopenharmony_ci WRITE_ONCE(conn->ping_task, NULL); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci /* release get from queueing */ 54462306a36Sopenharmony_ci __iscsi_put_task(task); 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/** 54862306a36Sopenharmony_ci * iscsi_complete_scsi_task - finish scsi task normally 54962306a36Sopenharmony_ci * @task: iscsi task for scsi cmd 55062306a36Sopenharmony_ci * @exp_cmdsn: expected cmd sn in cpu format 55162306a36Sopenharmony_ci * @max_cmdsn: max cmd sn in cpu format 55262306a36Sopenharmony_ci * 55362306a36Sopenharmony_ci * This is used when drivers do not need or cannot perform 55462306a36Sopenharmony_ci * lower level pdu processing. 55562306a36Sopenharmony_ci * 55662306a36Sopenharmony_ci * Called with session back_lock 55762306a36Sopenharmony_ci */ 55862306a36Sopenharmony_civoid iscsi_complete_scsi_task(struct iscsi_task *task, 55962306a36Sopenharmony_ci uint32_t exp_cmdsn, uint32_t max_cmdsn) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "[itt 0x%x]\n", task->itt); 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci conn->last_recv = jiffies; 56662306a36Sopenharmony_ci __iscsi_update_cmdsn(conn->session, exp_cmdsn, max_cmdsn); 56762306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 56862306a36Sopenharmony_ci} 56962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_complete_scsi_task); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci/* 57262306a36Sopenharmony_ci * Must be called with back and frwd lock 57362306a36Sopenharmony_ci */ 57462306a36Sopenharmony_cistatic bool cleanup_queued_task(struct iscsi_task *task) 57562306a36Sopenharmony_ci{ 57662306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 57762306a36Sopenharmony_ci bool early_complete = false; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* 58062306a36Sopenharmony_ci * We might have raced where we handled a R2T early and got a response 58162306a36Sopenharmony_ci * but have not yet taken the task off the requeue list, then a TMF or 58262306a36Sopenharmony_ci * recovery happened and so we can still see it here. 58362306a36Sopenharmony_ci */ 58462306a36Sopenharmony_ci if (task->state == ISCSI_TASK_COMPLETED) 58562306a36Sopenharmony_ci early_complete = true; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci if (!list_empty(&task->running)) { 58862306a36Sopenharmony_ci list_del_init(&task->running); 58962306a36Sopenharmony_ci /* 59062306a36Sopenharmony_ci * If it's on a list but still running this could be cleanup 59162306a36Sopenharmony_ci * from a TMF or session recovery. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_ci if (task->state == ISCSI_TASK_RUNNING || 59462306a36Sopenharmony_ci task->state == ISCSI_TASK_COMPLETED) 59562306a36Sopenharmony_ci __iscsi_put_task(task); 59662306a36Sopenharmony_ci } 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (conn->session->running_aborted_task == task) { 59962306a36Sopenharmony_ci conn->session->running_aborted_task = NULL; 60062306a36Sopenharmony_ci __iscsi_put_task(task); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (conn->task == task) { 60462306a36Sopenharmony_ci conn->task = NULL; 60562306a36Sopenharmony_ci __iscsi_put_task(task); 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci return early_complete; 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * session back and frwd lock must be held and if not called for a task that 61362306a36Sopenharmony_ci * is still pending or from the xmit thread, then xmit thread must be suspended 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_cistatic void __fail_scsi_task(struct iscsi_task *task, int err) 61662306a36Sopenharmony_ci{ 61762306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 61862306a36Sopenharmony_ci struct scsi_cmnd *sc; 61962306a36Sopenharmony_ci int state; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (cleanup_queued_task(task)) 62262306a36Sopenharmony_ci return; 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) { 62562306a36Sopenharmony_ci /* 62662306a36Sopenharmony_ci * cmd never made it to the xmit thread, so we should not count 62762306a36Sopenharmony_ci * the cmd in the sequencing 62862306a36Sopenharmony_ci */ 62962306a36Sopenharmony_ci conn->session->queued_cmdsn--; 63062306a36Sopenharmony_ci /* it was never sent so just complete like normal */ 63162306a36Sopenharmony_ci state = ISCSI_TASK_COMPLETED; 63262306a36Sopenharmony_ci } else if (err == DID_TRANSPORT_DISRUPTED) 63362306a36Sopenharmony_ci state = ISCSI_TASK_ABRT_SESS_RECOV; 63462306a36Sopenharmony_ci else 63562306a36Sopenharmony_ci state = ISCSI_TASK_ABRT_TMF; 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci sc = task->sc; 63862306a36Sopenharmony_ci sc->result = err << 16; 63962306a36Sopenharmony_ci scsi_set_resid(sc, scsi_bufflen(sc)); 64062306a36Sopenharmony_ci iscsi_complete_task(task, state); 64162306a36Sopenharmony_ci} 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_cistatic void fail_scsi_task(struct iscsi_task *task, int err) 64462306a36Sopenharmony_ci{ 64562306a36Sopenharmony_ci struct iscsi_session *session = task->conn->session; 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 64862306a36Sopenharmony_ci __fail_scsi_task(task, err); 64962306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic int iscsi_prep_mgmt_task(struct iscsi_conn *conn, 65362306a36Sopenharmony_ci struct iscsi_task *task) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 65662306a36Sopenharmony_ci struct iscsi_hdr *hdr = task->hdr; 65762306a36Sopenharmony_ci struct iscsi_nopout *nop = (struct iscsi_nopout *)hdr; 65862306a36Sopenharmony_ci uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) 66162306a36Sopenharmony_ci return -ENOTCONN; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (opcode != ISCSI_OP_LOGIN && opcode != ISCSI_OP_TEXT) 66462306a36Sopenharmony_ci nop->exp_statsn = cpu_to_be32(conn->exp_statsn); 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * pre-format CmdSN for outgoing PDU. 66762306a36Sopenharmony_ci */ 66862306a36Sopenharmony_ci nop->cmdsn = cpu_to_be32(session->cmdsn); 66962306a36Sopenharmony_ci if (hdr->itt != RESERVED_ITT) { 67062306a36Sopenharmony_ci /* 67162306a36Sopenharmony_ci * TODO: We always use immediate for normal session pdus. 67262306a36Sopenharmony_ci * If we start to send tmfs or nops as non-immediate then 67362306a36Sopenharmony_ci * we should start checking the cmdsn numbers for mgmt tasks. 67462306a36Sopenharmony_ci * 67562306a36Sopenharmony_ci * During discovery sessions iscsid sends TEXT as non immediate, 67662306a36Sopenharmony_ci * but we always only send one PDU at a time. 67762306a36Sopenharmony_ci */ 67862306a36Sopenharmony_ci if (conn->c_stage == ISCSI_CONN_STARTED && 67962306a36Sopenharmony_ci !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { 68062306a36Sopenharmony_ci session->queued_cmdsn++; 68162306a36Sopenharmony_ci session->cmdsn++; 68262306a36Sopenharmony_ci } 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (session->tt->init_task && session->tt->init_task(task)) 68662306a36Sopenharmony_ci return -EIO; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci if ((hdr->opcode & ISCSI_OPCODE_MASK) == ISCSI_OP_LOGOUT) 68962306a36Sopenharmony_ci session->state = ISCSI_STATE_LOGGING_OUT; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci task->state = ISCSI_TASK_RUNNING; 69262306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "mgmtpdu [op 0x%x hdr->itt 0x%x " 69362306a36Sopenharmony_ci "datalen %d]\n", hdr->opcode & ISCSI_OPCODE_MASK, 69462306a36Sopenharmony_ci hdr->itt, task->data_count); 69562306a36Sopenharmony_ci return 0; 69662306a36Sopenharmony_ci} 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci/** 69962306a36Sopenharmony_ci * iscsi_alloc_mgmt_task - allocate and setup a mgmt task. 70062306a36Sopenharmony_ci * @conn: iscsi conn that the task will be sent on. 70162306a36Sopenharmony_ci * @hdr: iscsi pdu that will be sent. 70262306a36Sopenharmony_ci * @data: buffer for data segment if needed. 70362306a36Sopenharmony_ci * @data_size: length of data in bytes. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_cistatic struct iscsi_task * 70662306a36Sopenharmony_ciiscsi_alloc_mgmt_task(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 70762306a36Sopenharmony_ci char *data, uint32_t data_size) 70862306a36Sopenharmony_ci{ 70962306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 71062306a36Sopenharmony_ci uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; 71162306a36Sopenharmony_ci struct iscsi_task *task; 71262306a36Sopenharmony_ci itt_t itt; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE || 71562306a36Sopenharmony_ci !test_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags)) 71662306a36Sopenharmony_ci return NULL; 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ci if (opcode == ISCSI_OP_LOGIN || opcode == ISCSI_OP_TEXT) { 71962306a36Sopenharmony_ci /* 72062306a36Sopenharmony_ci * Login and Text are sent serially, in 72162306a36Sopenharmony_ci * request-followed-by-response sequence. 72262306a36Sopenharmony_ci * Same task can be used. Same ITT must be used. 72362306a36Sopenharmony_ci * Note that login_task is preallocated at conn_create(). 72462306a36Sopenharmony_ci */ 72562306a36Sopenharmony_ci if (conn->login_task->state != ISCSI_TASK_FREE) { 72662306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Login/Text in " 72762306a36Sopenharmony_ci "progress. Cannot start new task.\n"); 72862306a36Sopenharmony_ci return NULL; 72962306a36Sopenharmony_ci } 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_ci if (data_size > ISCSI_DEF_MAX_RECV_SEG_LEN) { 73262306a36Sopenharmony_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); 73362306a36Sopenharmony_ci return NULL; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci task = conn->login_task; 73762306a36Sopenharmony_ci } else { 73862306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) 73962306a36Sopenharmony_ci return NULL; 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci if (data_size != 0) { 74262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Can not send data buffer of len %u for op 0x%x\n", data_size, opcode); 74362306a36Sopenharmony_ci return NULL; 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci BUG_ON(conn->c_stage == ISCSI_CONN_INITIAL_STAGE); 74762306a36Sopenharmony_ci BUG_ON(conn->c_stage == ISCSI_CONN_STOPPED); 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci if (!kfifo_out(&session->cmdpool.queue, 75062306a36Sopenharmony_ci (void*)&task, sizeof(void*))) 75162306a36Sopenharmony_ci return NULL; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci /* 75462306a36Sopenharmony_ci * released in complete pdu for task we expect a response for, and 75562306a36Sopenharmony_ci * released by the lld when it has transmitted the task for 75662306a36Sopenharmony_ci * pdus we do not expect a response for. 75762306a36Sopenharmony_ci */ 75862306a36Sopenharmony_ci refcount_set(&task->refcount, 1); 75962306a36Sopenharmony_ci task->conn = conn; 76062306a36Sopenharmony_ci task->sc = NULL; 76162306a36Sopenharmony_ci INIT_LIST_HEAD(&task->running); 76262306a36Sopenharmony_ci task->state = ISCSI_TASK_PENDING; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (data_size) { 76562306a36Sopenharmony_ci memcpy(task->data, data, data_size); 76662306a36Sopenharmony_ci task->data_count = data_size; 76762306a36Sopenharmony_ci } else 76862306a36Sopenharmony_ci task->data_count = 0; 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_ci if (conn->session->tt->alloc_pdu) { 77162306a36Sopenharmony_ci if (conn->session->tt->alloc_pdu(task, hdr->opcode)) { 77262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not allocate " 77362306a36Sopenharmony_ci "pdu for mgmt task.\n"); 77462306a36Sopenharmony_ci goto free_task; 77562306a36Sopenharmony_ci } 77662306a36Sopenharmony_ci } 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci itt = task->hdr->itt; 77962306a36Sopenharmony_ci task->hdr_len = sizeof(struct iscsi_hdr); 78062306a36Sopenharmony_ci memcpy(task->hdr, hdr, sizeof(struct iscsi_hdr)); 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci if (hdr->itt != RESERVED_ITT) { 78362306a36Sopenharmony_ci if (session->tt->parse_pdu_itt) 78462306a36Sopenharmony_ci task->hdr->itt = itt; 78562306a36Sopenharmony_ci else 78662306a36Sopenharmony_ci task->hdr->itt = build_itt(task->itt, 78762306a36Sopenharmony_ci task->conn->session->age); 78862306a36Sopenharmony_ci } 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci return task; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_cifree_task: 79362306a36Sopenharmony_ci iscsi_put_task(task); 79462306a36Sopenharmony_ci return NULL; 79562306a36Sopenharmony_ci} 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/** 79862306a36Sopenharmony_ci * iscsi_send_mgmt_task - Send task created with iscsi_alloc_mgmt_task. 79962306a36Sopenharmony_ci * @task: iscsi task to send. 80062306a36Sopenharmony_ci * 80162306a36Sopenharmony_ci * On failure this returns a non-zero error code, and the driver must free 80262306a36Sopenharmony_ci * the task with iscsi_put_task; 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_cistatic int iscsi_send_mgmt_task(struct iscsi_task *task) 80562306a36Sopenharmony_ci{ 80662306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 80762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 80862306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(conn->session->host); 80962306a36Sopenharmony_ci int rc = 0; 81062306a36Sopenharmony_ci 81162306a36Sopenharmony_ci if (!ihost->workq) { 81262306a36Sopenharmony_ci rc = iscsi_prep_mgmt_task(conn, task); 81362306a36Sopenharmony_ci if (rc) 81462306a36Sopenharmony_ci return rc; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci rc = session->tt->xmit_task(task); 81762306a36Sopenharmony_ci if (rc) 81862306a36Sopenharmony_ci return rc; 81962306a36Sopenharmony_ci } else { 82062306a36Sopenharmony_ci list_add_tail(&task->running, &conn->mgmtqueue); 82162306a36Sopenharmony_ci iscsi_conn_queue_xmit(conn); 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_ci return 0; 82562306a36Sopenharmony_ci} 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_cistatic int __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 82862306a36Sopenharmony_ci char *data, uint32_t data_size) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct iscsi_task *task; 83162306a36Sopenharmony_ci int rc; 83262306a36Sopenharmony_ci 83362306a36Sopenharmony_ci task = iscsi_alloc_mgmt_task(conn, hdr, data, data_size); 83462306a36Sopenharmony_ci if (!task) 83562306a36Sopenharmony_ci return -ENOMEM; 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci rc = iscsi_send_mgmt_task(task); 83862306a36Sopenharmony_ci if (rc) 83962306a36Sopenharmony_ci iscsi_put_task(task); 84062306a36Sopenharmony_ci return rc; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ciint iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, 84462306a36Sopenharmony_ci char *data, uint32_t data_size) 84562306a36Sopenharmony_ci{ 84662306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 84762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 84862306a36Sopenharmony_ci int err = 0; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 85162306a36Sopenharmony_ci if (__iscsi_conn_send_pdu(conn, hdr, data, data_size)) 85262306a36Sopenharmony_ci err = -EPERM; 85362306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 85462306a36Sopenharmony_ci return err; 85562306a36Sopenharmony_ci} 85662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_send_pdu); 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci/** 85962306a36Sopenharmony_ci * iscsi_scsi_cmd_rsp - SCSI Command Response processing 86062306a36Sopenharmony_ci * @conn: iscsi connection 86162306a36Sopenharmony_ci * @hdr: iscsi header 86262306a36Sopenharmony_ci * @task: scsi command task 86362306a36Sopenharmony_ci * @data: cmd data buffer 86462306a36Sopenharmony_ci * @datalen: len of buffer 86562306a36Sopenharmony_ci * 86662306a36Sopenharmony_ci * iscsi_cmd_rsp sets up the scsi_cmnd fields based on the PDU and 86762306a36Sopenharmony_ci * then completes the command and task. called under back_lock 86862306a36Sopenharmony_ci **/ 86962306a36Sopenharmony_cistatic void iscsi_scsi_cmd_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 87062306a36Sopenharmony_ci struct iscsi_task *task, char *data, 87162306a36Sopenharmony_ci int datalen) 87262306a36Sopenharmony_ci{ 87362306a36Sopenharmony_ci struct iscsi_scsi_rsp *rhdr = (struct iscsi_scsi_rsp *)hdr; 87462306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 87562306a36Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)rhdr); 87862306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci sc->result = (DID_OK << 16) | rhdr->cmd_status; 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (task->protected) { 88362306a36Sopenharmony_ci sector_t sector; 88462306a36Sopenharmony_ci u8 ascq; 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci /** 88762306a36Sopenharmony_ci * Transports that didn't implement check_protection 88862306a36Sopenharmony_ci * callback but still published T10-PI support to scsi-mid 88962306a36Sopenharmony_ci * deserve this BUG_ON. 89062306a36Sopenharmony_ci **/ 89162306a36Sopenharmony_ci BUG_ON(!session->tt->check_protection); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci ascq = session->tt->check_protection(task, §or); 89462306a36Sopenharmony_ci if (ascq) { 89562306a36Sopenharmony_ci scsi_build_sense(sc, 1, ILLEGAL_REQUEST, 0x10, ascq); 89662306a36Sopenharmony_ci scsi_set_sense_information(sc->sense_buffer, 89762306a36Sopenharmony_ci SCSI_SENSE_BUFFERSIZE, 89862306a36Sopenharmony_ci sector); 89962306a36Sopenharmony_ci goto out; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci if (rhdr->response != ISCSI_STATUS_CMD_COMPLETED) { 90462306a36Sopenharmony_ci sc->result = DID_ERROR << 16; 90562306a36Sopenharmony_ci goto out; 90662306a36Sopenharmony_ci } 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci if (rhdr->cmd_status == SAM_STAT_CHECK_CONDITION) { 90962306a36Sopenharmony_ci uint16_t senselen; 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci if (datalen < 2) { 91262306a36Sopenharmony_ciinvalid_datalen: 91362306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 91462306a36Sopenharmony_ci "Got CHECK_CONDITION but invalid data " 91562306a36Sopenharmony_ci "buffer size of %d\n", datalen); 91662306a36Sopenharmony_ci sc->result = DID_BAD_TARGET << 16; 91762306a36Sopenharmony_ci goto out; 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci senselen = get_unaligned_be16(data); 92162306a36Sopenharmony_ci if (datalen < senselen) 92262306a36Sopenharmony_ci goto invalid_datalen; 92362306a36Sopenharmony_ci 92462306a36Sopenharmony_ci memcpy(sc->sense_buffer, data + 2, 92562306a36Sopenharmony_ci min_t(uint16_t, senselen, SCSI_SENSE_BUFFERSIZE)); 92662306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "copied %d bytes of sense\n", 92762306a36Sopenharmony_ci min_t(uint16_t, senselen, 92862306a36Sopenharmony_ci SCSI_SENSE_BUFFERSIZE)); 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_CMD_BIDI_UNDERFLOW | 93262306a36Sopenharmony_ci ISCSI_FLAG_CMD_BIDI_OVERFLOW)) { 93362306a36Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci 93662306a36Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_CMD_UNDERFLOW | 93762306a36Sopenharmony_ci ISCSI_FLAG_CMD_OVERFLOW)) { 93862306a36Sopenharmony_ci int res_count = be32_to_cpu(rhdr->residual_count); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci if (res_count > 0 && 94162306a36Sopenharmony_ci (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || 94262306a36Sopenharmony_ci res_count <= scsi_bufflen(sc))) 94362306a36Sopenharmony_ci /* write side for bidi or uni-io set_resid */ 94462306a36Sopenharmony_ci scsi_set_resid(sc, res_count); 94562306a36Sopenharmony_ci else 94662306a36Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ciout: 94962306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "cmd rsp done [sc %p res %d itt 0x%x]\n", 95062306a36Sopenharmony_ci sc, sc->result, task->itt); 95162306a36Sopenharmony_ci conn->scsirsp_pdus_cnt++; 95262306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 95362306a36Sopenharmony_ci} 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci/** 95662306a36Sopenharmony_ci * iscsi_data_in_rsp - SCSI Data-In Response processing 95762306a36Sopenharmony_ci * @conn: iscsi connection 95862306a36Sopenharmony_ci * @hdr: iscsi pdu 95962306a36Sopenharmony_ci * @task: scsi command task 96062306a36Sopenharmony_ci * 96162306a36Sopenharmony_ci * iscsi_data_in_rsp sets up the scsi_cmnd fields based on the data received 96262306a36Sopenharmony_ci * then completes the command and task. called under back_lock 96362306a36Sopenharmony_ci **/ 96462306a36Sopenharmony_cistatic void 96562306a36Sopenharmony_ciiscsi_data_in_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 96662306a36Sopenharmony_ci struct iscsi_task *task) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci struct iscsi_data_rsp *rhdr = (struct iscsi_data_rsp *)hdr; 96962306a36Sopenharmony_ci struct scsi_cmnd *sc = task->sc; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci if (!(rhdr->flags & ISCSI_FLAG_DATA_STATUS)) 97262306a36Sopenharmony_ci return; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci iscsi_update_cmdsn(conn->session, (struct iscsi_nopin *)hdr); 97562306a36Sopenharmony_ci sc->result = (DID_OK << 16) | rhdr->cmd_status; 97662306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(rhdr->statsn) + 1; 97762306a36Sopenharmony_ci if (rhdr->flags & (ISCSI_FLAG_DATA_UNDERFLOW | 97862306a36Sopenharmony_ci ISCSI_FLAG_DATA_OVERFLOW)) { 97962306a36Sopenharmony_ci int res_count = be32_to_cpu(rhdr->residual_count); 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci if (res_count > 0 && 98262306a36Sopenharmony_ci (rhdr->flags & ISCSI_FLAG_CMD_OVERFLOW || 98362306a36Sopenharmony_ci res_count <= sc->sdb.length)) 98462306a36Sopenharmony_ci scsi_set_resid(sc, res_count); 98562306a36Sopenharmony_ci else 98662306a36Sopenharmony_ci sc->result = (DID_BAD_TARGET << 16) | rhdr->cmd_status; 98762306a36Sopenharmony_ci } 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "data in with status done " 99062306a36Sopenharmony_ci "[sc %p res %d itt 0x%x]\n", 99162306a36Sopenharmony_ci sc, sc->result, task->itt); 99262306a36Sopenharmony_ci conn->scsirsp_pdus_cnt++; 99362306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 99462306a36Sopenharmony_ci} 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_cistatic void iscsi_tmf_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) 99762306a36Sopenharmony_ci{ 99862306a36Sopenharmony_ci struct iscsi_tm_rsp *tmf = (struct iscsi_tm_rsp *)hdr; 99962306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 100262306a36Sopenharmony_ci conn->tmfrsp_pdus_cnt++; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (session->tmf_state != TMF_QUEUED) 100562306a36Sopenharmony_ci return; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (tmf->response == ISCSI_TMF_RSP_COMPLETE) 100862306a36Sopenharmony_ci session->tmf_state = TMF_SUCCESS; 100962306a36Sopenharmony_ci else if (tmf->response == ISCSI_TMF_RSP_NO_TASK) 101062306a36Sopenharmony_ci session->tmf_state = TMF_NOT_FOUND; 101162306a36Sopenharmony_ci else 101262306a36Sopenharmony_ci session->tmf_state = TMF_FAILED; 101362306a36Sopenharmony_ci wake_up(&session->ehwait); 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci struct iscsi_nopout hdr; 101962306a36Sopenharmony_ci struct iscsi_task *task; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (!rhdr) { 102262306a36Sopenharmony_ci if (READ_ONCE(conn->ping_task)) 102362306a36Sopenharmony_ci return -EINVAL; 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci memset(&hdr, 0, sizeof(struct iscsi_nopout)); 102762306a36Sopenharmony_ci hdr.opcode = ISCSI_OP_NOOP_OUT | ISCSI_OP_IMMEDIATE; 102862306a36Sopenharmony_ci hdr.flags = ISCSI_FLAG_CMD_FINAL; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_ci if (rhdr) { 103162306a36Sopenharmony_ci hdr.lun = rhdr->lun; 103262306a36Sopenharmony_ci hdr.ttt = rhdr->ttt; 103362306a36Sopenharmony_ci hdr.itt = RESERVED_ITT; 103462306a36Sopenharmony_ci } else 103562306a36Sopenharmony_ci hdr.ttt = RESERVED_ITT; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci task = iscsi_alloc_mgmt_task(conn, (struct iscsi_hdr *)&hdr, NULL, 0); 103862306a36Sopenharmony_ci if (!task) 103962306a36Sopenharmony_ci return -ENOMEM; 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci if (!rhdr) 104262306a36Sopenharmony_ci WRITE_ONCE(conn->ping_task, task); 104362306a36Sopenharmony_ci 104462306a36Sopenharmony_ci if (iscsi_send_mgmt_task(task)) { 104562306a36Sopenharmony_ci if (!rhdr) 104662306a36Sopenharmony_ci WRITE_ONCE(conn->ping_task, NULL); 104762306a36Sopenharmony_ci iscsi_put_task(task); 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); 105062306a36Sopenharmony_ci return -EIO; 105162306a36Sopenharmony_ci } else if (!rhdr) { 105262306a36Sopenharmony_ci /* only track our nops */ 105362306a36Sopenharmony_ci conn->last_ping = jiffies; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return 0; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci/** 106062306a36Sopenharmony_ci * iscsi_nop_out_rsp - SCSI NOP Response processing 106162306a36Sopenharmony_ci * @task: scsi command task 106262306a36Sopenharmony_ci * @nop: the nop structure 106362306a36Sopenharmony_ci * @data: where to put the data 106462306a36Sopenharmony_ci * @datalen: length of data 106562306a36Sopenharmony_ci * 106662306a36Sopenharmony_ci * iscsi_nop_out_rsp handles nop response from use or 106762306a36Sopenharmony_ci * from user space. called under back_lock 106862306a36Sopenharmony_ci **/ 106962306a36Sopenharmony_cistatic int iscsi_nop_out_rsp(struct iscsi_task *task, 107062306a36Sopenharmony_ci struct iscsi_nopin *nop, char *data, int datalen) 107162306a36Sopenharmony_ci{ 107262306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 107362306a36Sopenharmony_ci int rc = 0; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci if (READ_ONCE(conn->ping_task) != task) { 107662306a36Sopenharmony_ci /* 107762306a36Sopenharmony_ci * If this is not in response to one of our 107862306a36Sopenharmony_ci * nops then it must be from userspace. 107962306a36Sopenharmony_ci */ 108062306a36Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, (struct iscsi_hdr *)nop, 108162306a36Sopenharmony_ci data, datalen)) 108262306a36Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 108362306a36Sopenharmony_ci } else 108462306a36Sopenharmony_ci mod_timer(&conn->transport_timer, jiffies + conn->recv_timeout); 108562306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 108662306a36Sopenharmony_ci return rc; 108762306a36Sopenharmony_ci} 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_cistatic int iscsi_handle_reject(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 109062306a36Sopenharmony_ci char *data, int datalen) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct iscsi_reject *reject = (struct iscsi_reject *)hdr; 109362306a36Sopenharmony_ci struct iscsi_hdr rejected_pdu; 109462306a36Sopenharmony_ci int opcode, rc = 0; 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(reject->statsn) + 1; 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (ntoh24(reject->dlength) > datalen || 109962306a36Sopenharmony_ci ntoh24(reject->dlength) < sizeof(struct iscsi_hdr)) { 110062306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Cannot handle rejected " 110162306a36Sopenharmony_ci "pdu. Invalid data length (pdu dlength " 110262306a36Sopenharmony_ci "%u, datalen %d\n", ntoh24(reject->dlength), 110362306a36Sopenharmony_ci datalen); 110462306a36Sopenharmony_ci return ISCSI_ERR_PROTO; 110562306a36Sopenharmony_ci } 110662306a36Sopenharmony_ci memcpy(&rejected_pdu, data, sizeof(struct iscsi_hdr)); 110762306a36Sopenharmony_ci opcode = rejected_pdu.opcode & ISCSI_OPCODE_MASK; 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_ci switch (reject->reason) { 111062306a36Sopenharmony_ci case ISCSI_REASON_DATA_DIGEST_ERROR: 111162306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 111262306a36Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected " 111362306a36Sopenharmony_ci "due to DataDigest error.\n", 111462306a36Sopenharmony_ci opcode, rejected_pdu.itt); 111562306a36Sopenharmony_ci break; 111662306a36Sopenharmony_ci case ISCSI_REASON_IMM_CMD_REJECT: 111762306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 111862306a36Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected. Too many " 111962306a36Sopenharmony_ci "immediate commands.\n", 112062306a36Sopenharmony_ci opcode, rejected_pdu.itt); 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * We only send one TMF at a time so if the target could not 112362306a36Sopenharmony_ci * handle it, then it should get fixed (RFC mandates that 112462306a36Sopenharmony_ci * a target can handle one immediate TMF per conn). 112562306a36Sopenharmony_ci * 112662306a36Sopenharmony_ci * For nops-outs, we could have sent more than one if 112762306a36Sopenharmony_ci * the target is sending us lots of nop-ins 112862306a36Sopenharmony_ci */ 112962306a36Sopenharmony_ci if (opcode != ISCSI_OP_NOOP_OUT) 113062306a36Sopenharmony_ci return 0; 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_ci if (rejected_pdu.itt == cpu_to_be32(ISCSI_RESERVED_TAG)) { 113362306a36Sopenharmony_ci /* 113462306a36Sopenharmony_ci * nop-out in response to target's nop-out rejected. 113562306a36Sopenharmony_ci * Just resend. 113662306a36Sopenharmony_ci */ 113762306a36Sopenharmony_ci /* In RX path we are under back lock */ 113862306a36Sopenharmony_ci spin_unlock(&conn->session->back_lock); 113962306a36Sopenharmony_ci spin_lock(&conn->session->frwd_lock); 114062306a36Sopenharmony_ci iscsi_send_nopout(conn, 114162306a36Sopenharmony_ci (struct iscsi_nopin*)&rejected_pdu); 114262306a36Sopenharmony_ci spin_unlock(&conn->session->frwd_lock); 114362306a36Sopenharmony_ci spin_lock(&conn->session->back_lock); 114462306a36Sopenharmony_ci } else { 114562306a36Sopenharmony_ci struct iscsi_task *task; 114662306a36Sopenharmony_ci /* 114762306a36Sopenharmony_ci * Our nop as ping got dropped. We know the target 114862306a36Sopenharmony_ci * and transport are ok so just clean up 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci task = iscsi_itt_to_task(conn, rejected_pdu.itt); 115162306a36Sopenharmony_ci if (!task) { 115262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 115362306a36Sopenharmony_ci "Invalid pdu reject. Could " 115462306a36Sopenharmony_ci "not lookup rejected task.\n"); 115562306a36Sopenharmony_ci rc = ISCSI_ERR_BAD_ITT; 115662306a36Sopenharmony_ci } else 115762306a36Sopenharmony_ci rc = iscsi_nop_out_rsp(task, 115862306a36Sopenharmony_ci (struct iscsi_nopin*)&rejected_pdu, 115962306a36Sopenharmony_ci NULL, 0); 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci break; 116262306a36Sopenharmony_ci default: 116362306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 116462306a36Sopenharmony_ci "pdu (op 0x%x itt 0x%x) rejected. Reason " 116562306a36Sopenharmony_ci "code 0x%x\n", rejected_pdu.opcode, 116662306a36Sopenharmony_ci rejected_pdu.itt, reject->reason); 116762306a36Sopenharmony_ci break; 116862306a36Sopenharmony_ci } 116962306a36Sopenharmony_ci return rc; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci/** 117362306a36Sopenharmony_ci * iscsi_itt_to_task - look up task by itt 117462306a36Sopenharmony_ci * @conn: iscsi connection 117562306a36Sopenharmony_ci * @itt: itt 117662306a36Sopenharmony_ci * 117762306a36Sopenharmony_ci * This should be used for mgmt tasks like login and nops, or if 117862306a36Sopenharmony_ci * the LDD's itt space does not include the session age. 117962306a36Sopenharmony_ci * 118062306a36Sopenharmony_ci * The session back_lock must be held. 118162306a36Sopenharmony_ci */ 118262306a36Sopenharmony_cistruct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *conn, itt_t itt) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 118562306a36Sopenharmony_ci int i; 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_ci if (itt == RESERVED_ITT) 118862306a36Sopenharmony_ci return NULL; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (session->tt->parse_pdu_itt) 119162306a36Sopenharmony_ci session->tt->parse_pdu_itt(conn, itt, &i, NULL); 119262306a36Sopenharmony_ci else 119362306a36Sopenharmony_ci i = get_itt(itt); 119462306a36Sopenharmony_ci if (i >= session->cmds_max) 119562306a36Sopenharmony_ci return NULL; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci return session->cmds[i]; 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_itt_to_task); 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci/** 120262306a36Sopenharmony_ci * __iscsi_complete_pdu - complete pdu 120362306a36Sopenharmony_ci * @conn: iscsi conn 120462306a36Sopenharmony_ci * @hdr: iscsi header 120562306a36Sopenharmony_ci * @data: data buffer 120662306a36Sopenharmony_ci * @datalen: len of data buffer 120762306a36Sopenharmony_ci * 120862306a36Sopenharmony_ci * Completes pdu processing by freeing any resources allocated at 120962306a36Sopenharmony_ci * queuecommand or send generic. session back_lock must be held and verify 121062306a36Sopenharmony_ci * itt must have been called. 121162306a36Sopenharmony_ci */ 121262306a36Sopenharmony_ciint __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 121362306a36Sopenharmony_ci char *data, int datalen) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 121662306a36Sopenharmony_ci int opcode = hdr->opcode & ISCSI_OPCODE_MASK, rc = 0; 121762306a36Sopenharmony_ci struct iscsi_task *task; 121862306a36Sopenharmony_ci uint32_t itt; 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci conn->last_recv = jiffies; 122162306a36Sopenharmony_ci rc = iscsi_verify_itt(conn, hdr->itt); 122262306a36Sopenharmony_ci if (rc) 122362306a36Sopenharmony_ci return rc; 122462306a36Sopenharmony_ci 122562306a36Sopenharmony_ci if (hdr->itt != RESERVED_ITT) 122662306a36Sopenharmony_ci itt = get_itt(hdr->itt); 122762306a36Sopenharmony_ci else 122862306a36Sopenharmony_ci itt = ~0U; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "[op 0x%x cid %d itt 0x%x len %d]\n", 123162306a36Sopenharmony_ci opcode, conn->id, itt, datalen); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci if (itt == ~0U) { 123462306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci switch(opcode) { 123762306a36Sopenharmony_ci case ISCSI_OP_NOOP_IN: 123862306a36Sopenharmony_ci if (datalen) { 123962306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 124062306a36Sopenharmony_ci break; 124162306a36Sopenharmony_ci } 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (hdr->ttt == cpu_to_be32(ISCSI_RESERVED_TAG)) 124462306a36Sopenharmony_ci break; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci /* In RX path we are under back lock */ 124762306a36Sopenharmony_ci spin_unlock(&session->back_lock); 124862306a36Sopenharmony_ci spin_lock(&session->frwd_lock); 124962306a36Sopenharmony_ci iscsi_send_nopout(conn, (struct iscsi_nopin*)hdr); 125062306a36Sopenharmony_ci spin_unlock(&session->frwd_lock); 125162306a36Sopenharmony_ci spin_lock(&session->back_lock); 125262306a36Sopenharmony_ci break; 125362306a36Sopenharmony_ci case ISCSI_OP_REJECT: 125462306a36Sopenharmony_ci rc = iscsi_handle_reject(conn, hdr, data, datalen); 125562306a36Sopenharmony_ci break; 125662306a36Sopenharmony_ci case ISCSI_OP_ASYNC_EVENT: 125762306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 125862306a36Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) 125962306a36Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 126062306a36Sopenharmony_ci break; 126162306a36Sopenharmony_ci default: 126262306a36Sopenharmony_ci rc = ISCSI_ERR_BAD_OPCODE; 126362306a36Sopenharmony_ci break; 126462306a36Sopenharmony_ci } 126562306a36Sopenharmony_ci goto out; 126662306a36Sopenharmony_ci } 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci switch(opcode) { 126962306a36Sopenharmony_ci case ISCSI_OP_SCSI_CMD_RSP: 127062306a36Sopenharmony_ci case ISCSI_OP_SCSI_DATA_IN: 127162306a36Sopenharmony_ci task = iscsi_itt_to_ctask(conn, hdr->itt); 127262306a36Sopenharmony_ci if (!task) 127362306a36Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 127462306a36Sopenharmony_ci task->last_xfer = jiffies; 127562306a36Sopenharmony_ci break; 127662306a36Sopenharmony_ci case ISCSI_OP_R2T: 127762306a36Sopenharmony_ci /* 127862306a36Sopenharmony_ci * LLD handles R2Ts if they need to. 127962306a36Sopenharmony_ci */ 128062306a36Sopenharmony_ci return 0; 128162306a36Sopenharmony_ci case ISCSI_OP_LOGOUT_RSP: 128262306a36Sopenharmony_ci case ISCSI_OP_LOGIN_RSP: 128362306a36Sopenharmony_ci case ISCSI_OP_TEXT_RSP: 128462306a36Sopenharmony_ci case ISCSI_OP_SCSI_TMFUNC_RSP: 128562306a36Sopenharmony_ci case ISCSI_OP_NOOP_IN: 128662306a36Sopenharmony_ci task = iscsi_itt_to_task(conn, hdr->itt); 128762306a36Sopenharmony_ci if (!task) 128862306a36Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 128962306a36Sopenharmony_ci break; 129062306a36Sopenharmony_ci default: 129162306a36Sopenharmony_ci return ISCSI_ERR_BAD_OPCODE; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci switch(opcode) { 129562306a36Sopenharmony_ci case ISCSI_OP_SCSI_CMD_RSP: 129662306a36Sopenharmony_ci iscsi_scsi_cmd_rsp(conn, hdr, task, data, datalen); 129762306a36Sopenharmony_ci break; 129862306a36Sopenharmony_ci case ISCSI_OP_SCSI_DATA_IN: 129962306a36Sopenharmony_ci iscsi_data_in_rsp(conn, hdr, task); 130062306a36Sopenharmony_ci break; 130162306a36Sopenharmony_ci case ISCSI_OP_LOGOUT_RSP: 130262306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 130362306a36Sopenharmony_ci if (datalen) { 130462306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 130562306a36Sopenharmony_ci break; 130662306a36Sopenharmony_ci } 130762306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 130862306a36Sopenharmony_ci goto recv_pdu; 130962306a36Sopenharmony_ci case ISCSI_OP_LOGIN_RSP: 131062306a36Sopenharmony_ci case ISCSI_OP_TEXT_RSP: 131162306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 131262306a36Sopenharmony_ci /* 131362306a36Sopenharmony_ci * login related PDU's exp_statsn is handled in 131462306a36Sopenharmony_ci * userspace 131562306a36Sopenharmony_ci */ 131662306a36Sopenharmony_ci goto recv_pdu; 131762306a36Sopenharmony_ci case ISCSI_OP_SCSI_TMFUNC_RSP: 131862306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 131962306a36Sopenharmony_ci if (datalen) { 132062306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 132162306a36Sopenharmony_ci break; 132262306a36Sopenharmony_ci } 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci iscsi_tmf_rsp(conn, hdr); 132562306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 132662306a36Sopenharmony_ci break; 132762306a36Sopenharmony_ci case ISCSI_OP_NOOP_IN: 132862306a36Sopenharmony_ci iscsi_update_cmdsn(session, (struct iscsi_nopin*)hdr); 132962306a36Sopenharmony_ci if (hdr->ttt != cpu_to_be32(ISCSI_RESERVED_TAG) || datalen) { 133062306a36Sopenharmony_ci rc = ISCSI_ERR_PROTO; 133162306a36Sopenharmony_ci break; 133262306a36Sopenharmony_ci } 133362306a36Sopenharmony_ci conn->exp_statsn = be32_to_cpu(hdr->statsn) + 1; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci rc = iscsi_nop_out_rsp(task, (struct iscsi_nopin*)hdr, 133662306a36Sopenharmony_ci data, datalen); 133762306a36Sopenharmony_ci break; 133862306a36Sopenharmony_ci default: 133962306a36Sopenharmony_ci rc = ISCSI_ERR_BAD_OPCODE; 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci 134362306a36Sopenharmony_ciout: 134462306a36Sopenharmony_ci return rc; 134562306a36Sopenharmony_cirecv_pdu: 134662306a36Sopenharmony_ci if (iscsi_recv_pdu(conn->cls_conn, hdr, data, datalen)) 134762306a36Sopenharmony_ci rc = ISCSI_ERR_CONN_FAILED; 134862306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_COMPLETED); 134962306a36Sopenharmony_ci return rc; 135062306a36Sopenharmony_ci} 135162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__iscsi_complete_pdu); 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ciint iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, 135462306a36Sopenharmony_ci char *data, int datalen) 135562306a36Sopenharmony_ci{ 135662306a36Sopenharmony_ci int rc; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci spin_lock(&conn->session->back_lock); 135962306a36Sopenharmony_ci rc = __iscsi_complete_pdu(conn, hdr, data, datalen); 136062306a36Sopenharmony_ci spin_unlock(&conn->session->back_lock); 136162306a36Sopenharmony_ci return rc; 136262306a36Sopenharmony_ci} 136362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_complete_pdu); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ciint iscsi_verify_itt(struct iscsi_conn *conn, itt_t itt) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 136862306a36Sopenharmony_ci int age = 0, i = 0; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci if (itt == RESERVED_ITT) 137162306a36Sopenharmony_ci return 0; 137262306a36Sopenharmony_ci 137362306a36Sopenharmony_ci if (session->tt->parse_pdu_itt) 137462306a36Sopenharmony_ci session->tt->parse_pdu_itt(conn, itt, &i, &age); 137562306a36Sopenharmony_ci else { 137662306a36Sopenharmony_ci i = get_itt(itt); 137762306a36Sopenharmony_ci age = ((__force u32)itt >> ISCSI_AGE_SHIFT) & ISCSI_AGE_MASK; 137862306a36Sopenharmony_ci } 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ci if (age != session->age) { 138162306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 138262306a36Sopenharmony_ci "received itt %x expected session age (%x)\n", 138362306a36Sopenharmony_ci (__force u32)itt, session->age); 138462306a36Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 138562306a36Sopenharmony_ci } 138662306a36Sopenharmony_ci 138762306a36Sopenharmony_ci if (i >= session->cmds_max) { 138862306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 138962306a36Sopenharmony_ci "received invalid itt index %u (max cmds " 139062306a36Sopenharmony_ci "%u.\n", i, session->cmds_max); 139162306a36Sopenharmony_ci return ISCSI_ERR_BAD_ITT; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci return 0; 139462306a36Sopenharmony_ci} 139562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_verify_itt); 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci/** 139862306a36Sopenharmony_ci * iscsi_itt_to_ctask - look up ctask by itt 139962306a36Sopenharmony_ci * @conn: iscsi connection 140062306a36Sopenharmony_ci * @itt: itt 140162306a36Sopenharmony_ci * 140262306a36Sopenharmony_ci * This should be used for cmd tasks. 140362306a36Sopenharmony_ci * 140462306a36Sopenharmony_ci * The session back_lock must be held. 140562306a36Sopenharmony_ci */ 140662306a36Sopenharmony_cistruct iscsi_task *iscsi_itt_to_ctask(struct iscsi_conn *conn, itt_t itt) 140762306a36Sopenharmony_ci{ 140862306a36Sopenharmony_ci struct iscsi_task *task; 140962306a36Sopenharmony_ci 141062306a36Sopenharmony_ci if (iscsi_verify_itt(conn, itt)) 141162306a36Sopenharmony_ci return NULL; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci task = iscsi_itt_to_task(conn, itt); 141462306a36Sopenharmony_ci if (!task || !task->sc) 141562306a36Sopenharmony_ci return NULL; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci if (iscsi_cmd(task->sc)->age != conn->session->age) { 141862306a36Sopenharmony_ci iscsi_session_printk(KERN_ERR, conn->session, 141962306a36Sopenharmony_ci "task's session age %d, expected %d\n", 142062306a36Sopenharmony_ci iscsi_cmd(task->sc)->age, conn->session->age); 142162306a36Sopenharmony_ci return NULL; 142262306a36Sopenharmony_ci } 142362306a36Sopenharmony_ci 142462306a36Sopenharmony_ci return task; 142562306a36Sopenharmony_ci} 142662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_itt_to_ctask); 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_civoid iscsi_session_failure(struct iscsi_session *session, 142962306a36Sopenharmony_ci enum iscsi_err err) 143062306a36Sopenharmony_ci{ 143162306a36Sopenharmony_ci struct iscsi_conn *conn; 143262306a36Sopenharmony_ci 143362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 143462306a36Sopenharmony_ci conn = session->leadconn; 143562306a36Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE || !conn) { 143662306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 143762306a36Sopenharmony_ci return; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 144162306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 144262306a36Sopenharmony_ci /* 144362306a36Sopenharmony_ci * if the host is being removed bypass the connection 144462306a36Sopenharmony_ci * recovery initialization because we are going to kill 144562306a36Sopenharmony_ci * the session. 144662306a36Sopenharmony_ci */ 144762306a36Sopenharmony_ci if (err == ISCSI_ERR_INVALID_HOST) 144862306a36Sopenharmony_ci iscsi_conn_error_event(conn->cls_conn, err); 144962306a36Sopenharmony_ci else 145062306a36Sopenharmony_ci iscsi_conn_failure(conn, err); 145162306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_failure); 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_cistatic bool iscsi_set_conn_failed(struct iscsi_conn *conn) 145662306a36Sopenharmony_ci{ 145762306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci if (session->state == ISCSI_STATE_FAILED) 146062306a36Sopenharmony_ci return false; 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci if (conn->stop_stage == 0) 146362306a36Sopenharmony_ci session->state = ISCSI_STATE_FAILED; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); 146662306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags); 146762306a36Sopenharmony_ci return true; 146862306a36Sopenharmony_ci} 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_civoid iscsi_conn_failure(struct iscsi_conn *conn, enum iscsi_err err) 147162306a36Sopenharmony_ci{ 147262306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 147362306a36Sopenharmony_ci bool needs_evt; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 147662306a36Sopenharmony_ci needs_evt = iscsi_set_conn_failed(conn); 147762306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci if (needs_evt) 148062306a36Sopenharmony_ci iscsi_conn_error_event(conn->cls_conn, err); 148162306a36Sopenharmony_ci} 148262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_failure); 148362306a36Sopenharmony_ci 148462306a36Sopenharmony_cistatic int iscsi_check_cmdsn_window_closed(struct iscsi_conn *conn) 148562306a36Sopenharmony_ci{ 148662306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci /* 148962306a36Sopenharmony_ci * Check for iSCSI window and take care of CmdSN wrap-around 149062306a36Sopenharmony_ci */ 149162306a36Sopenharmony_ci if (!iscsi_sna_lte(session->queued_cmdsn, session->max_cmdsn)) { 149262306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "iSCSI CmdSN closed. ExpCmdSn " 149362306a36Sopenharmony_ci "%u MaxCmdSN %u CmdSN %u/%u\n", 149462306a36Sopenharmony_ci session->exp_cmdsn, session->max_cmdsn, 149562306a36Sopenharmony_ci session->cmdsn, session->queued_cmdsn); 149662306a36Sopenharmony_ci return -ENOSPC; 149762306a36Sopenharmony_ci } 149862306a36Sopenharmony_ci return 0; 149962306a36Sopenharmony_ci} 150062306a36Sopenharmony_ci 150162306a36Sopenharmony_cistatic int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, 150262306a36Sopenharmony_ci bool was_requeue) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci int rc; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci if (!conn->task) { 150762306a36Sopenharmony_ci /* 150862306a36Sopenharmony_ci * Take a ref so we can access it after xmit_task(). 150962306a36Sopenharmony_ci * 151062306a36Sopenharmony_ci * This should never fail because the failure paths will have 151162306a36Sopenharmony_ci * stopped the xmit thread. 151262306a36Sopenharmony_ci */ 151362306a36Sopenharmony_ci if (!iscsi_get_task(task)) { 151462306a36Sopenharmony_ci WARN_ON_ONCE(1); 151562306a36Sopenharmony_ci return 0; 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci } else { 151862306a36Sopenharmony_ci /* Already have a ref from when we failed to send it last call */ 151962306a36Sopenharmony_ci conn->task = NULL; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* 152362306a36Sopenharmony_ci * If this was a requeue for a R2T we have an extra ref on the task in 152462306a36Sopenharmony_ci * case a bad target sends a cmd rsp before we have handled the task. 152562306a36Sopenharmony_ci */ 152662306a36Sopenharmony_ci if (was_requeue) 152762306a36Sopenharmony_ci iscsi_put_task(task); 152862306a36Sopenharmony_ci 152962306a36Sopenharmony_ci /* 153062306a36Sopenharmony_ci * Do this after dropping the extra ref because if this was a requeue 153162306a36Sopenharmony_ci * it's removed from that list and cleanup_queued_task would miss it. 153262306a36Sopenharmony_ci */ 153362306a36Sopenharmony_ci if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) { 153462306a36Sopenharmony_ci /* 153562306a36Sopenharmony_ci * Save the task and ref in case we weren't cleaning up this 153662306a36Sopenharmony_ci * task and get woken up again. 153762306a36Sopenharmony_ci */ 153862306a36Sopenharmony_ci conn->task = task; 153962306a36Sopenharmony_ci return -ENODATA; 154062306a36Sopenharmony_ci } 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 154362306a36Sopenharmony_ci rc = conn->session->tt->xmit_task(task); 154462306a36Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 154562306a36Sopenharmony_ci if (!rc) { 154662306a36Sopenharmony_ci /* done with this task */ 154762306a36Sopenharmony_ci task->last_xfer = jiffies; 154862306a36Sopenharmony_ci } else { 154962306a36Sopenharmony_ci /* 155062306a36Sopenharmony_ci * get an extra ref that is released next time we access it 155162306a36Sopenharmony_ci * as conn->task above. 155262306a36Sopenharmony_ci */ 155362306a36Sopenharmony_ci iscsi_get_task(task); 155462306a36Sopenharmony_ci conn->task = task; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci iscsi_put_task(task); 155862306a36Sopenharmony_ci return rc; 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_ci/** 156262306a36Sopenharmony_ci * iscsi_requeue_task - requeue task to run from session workqueue 156362306a36Sopenharmony_ci * @task: task to requeue 156462306a36Sopenharmony_ci * 156562306a36Sopenharmony_ci * Callers must have taken a ref to the task that is going to be requeued. 156662306a36Sopenharmony_ci */ 156762306a36Sopenharmony_civoid iscsi_requeue_task(struct iscsi_task *task) 156862306a36Sopenharmony_ci{ 156962306a36Sopenharmony_ci struct iscsi_conn *conn = task->conn; 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci /* 157262306a36Sopenharmony_ci * this may be on the requeue list already if the xmit_task callout 157362306a36Sopenharmony_ci * is handling the r2ts while we are adding new ones 157462306a36Sopenharmony_ci */ 157562306a36Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 157662306a36Sopenharmony_ci if (list_empty(&task->running)) { 157762306a36Sopenharmony_ci list_add_tail(&task->running, &conn->requeue); 157862306a36Sopenharmony_ci } else { 157962306a36Sopenharmony_ci /* 158062306a36Sopenharmony_ci * Don't need the extra ref since it's already requeued and 158162306a36Sopenharmony_ci * has a ref. 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ci iscsi_put_task(task); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci iscsi_conn_queue_xmit(conn); 158662306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 158762306a36Sopenharmony_ci} 158862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_requeue_task); 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci/** 159162306a36Sopenharmony_ci * iscsi_data_xmit - xmit any command into the scheduled connection 159262306a36Sopenharmony_ci * @conn: iscsi connection 159362306a36Sopenharmony_ci * 159462306a36Sopenharmony_ci * Notes: 159562306a36Sopenharmony_ci * The function can return -EAGAIN in which case the caller must 159662306a36Sopenharmony_ci * re-schedule it again later or recover. '0' return code means 159762306a36Sopenharmony_ci * successful xmit. 159862306a36Sopenharmony_ci **/ 159962306a36Sopenharmony_cistatic int iscsi_data_xmit(struct iscsi_conn *conn) 160062306a36Sopenharmony_ci{ 160162306a36Sopenharmony_ci struct iscsi_task *task; 160262306a36Sopenharmony_ci int rc = 0; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 160562306a36Sopenharmony_ci if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) { 160662306a36Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, "Tx suspended!\n"); 160762306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 160862306a36Sopenharmony_ci return -ENODATA; 160962306a36Sopenharmony_ci } 161062306a36Sopenharmony_ci 161162306a36Sopenharmony_ci if (conn->task) { 161262306a36Sopenharmony_ci rc = iscsi_xmit_task(conn, conn->task, false); 161362306a36Sopenharmony_ci if (rc) 161462306a36Sopenharmony_ci goto done; 161562306a36Sopenharmony_ci } 161662306a36Sopenharmony_ci 161762306a36Sopenharmony_ci /* 161862306a36Sopenharmony_ci * process mgmt pdus like nops before commands since we should 161962306a36Sopenharmony_ci * only have one nop-out as a ping from us and targets should not 162062306a36Sopenharmony_ci * overflow us with nop-ins 162162306a36Sopenharmony_ci */ 162262306a36Sopenharmony_cicheck_mgmt: 162362306a36Sopenharmony_ci while (!list_empty(&conn->mgmtqueue)) { 162462306a36Sopenharmony_ci task = list_entry(conn->mgmtqueue.next, struct iscsi_task, 162562306a36Sopenharmony_ci running); 162662306a36Sopenharmony_ci list_del_init(&task->running); 162762306a36Sopenharmony_ci if (iscsi_prep_mgmt_task(conn, task)) { 162862306a36Sopenharmony_ci /* regular RX path uses back_lock */ 162962306a36Sopenharmony_ci spin_lock_bh(&conn->session->back_lock); 163062306a36Sopenharmony_ci __iscsi_put_task(task); 163162306a36Sopenharmony_ci spin_unlock_bh(&conn->session->back_lock); 163262306a36Sopenharmony_ci continue; 163362306a36Sopenharmony_ci } 163462306a36Sopenharmony_ci rc = iscsi_xmit_task(conn, task, false); 163562306a36Sopenharmony_ci if (rc) 163662306a36Sopenharmony_ci goto done; 163762306a36Sopenharmony_ci } 163862306a36Sopenharmony_ci 163962306a36Sopenharmony_cicheck_requeue: 164062306a36Sopenharmony_ci while (!list_empty(&conn->requeue)) { 164162306a36Sopenharmony_ci /* 164262306a36Sopenharmony_ci * we always do fastlogout - conn stop code will clean up. 164362306a36Sopenharmony_ci */ 164462306a36Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) 164562306a36Sopenharmony_ci break; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci task = list_entry(conn->requeue.next, struct iscsi_task, 164862306a36Sopenharmony_ci running); 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) 165162306a36Sopenharmony_ci break; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci list_del_init(&task->running); 165462306a36Sopenharmony_ci rc = iscsi_xmit_task(conn, task, true); 165562306a36Sopenharmony_ci if (rc) 165662306a36Sopenharmony_ci goto done; 165762306a36Sopenharmony_ci if (!list_empty(&conn->mgmtqueue)) 165862306a36Sopenharmony_ci goto check_mgmt; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_ci /* process pending command queue */ 166262306a36Sopenharmony_ci while (!list_empty(&conn->cmdqueue)) { 166362306a36Sopenharmony_ci task = list_entry(conn->cmdqueue.next, struct iscsi_task, 166462306a36Sopenharmony_ci running); 166562306a36Sopenharmony_ci list_del_init(&task->running); 166662306a36Sopenharmony_ci if (conn->session->state == ISCSI_STATE_LOGGING_OUT) { 166762306a36Sopenharmony_ci fail_scsi_task(task, DID_IMM_RETRY); 166862306a36Sopenharmony_ci continue; 166962306a36Sopenharmony_ci } 167062306a36Sopenharmony_ci rc = iscsi_prep_scsi_cmd_pdu(task); 167162306a36Sopenharmony_ci if (rc) { 167262306a36Sopenharmony_ci if (rc == -ENOMEM || rc == -EACCES) 167362306a36Sopenharmony_ci fail_scsi_task(task, DID_IMM_RETRY); 167462306a36Sopenharmony_ci else 167562306a36Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 167662306a36Sopenharmony_ci continue; 167762306a36Sopenharmony_ci } 167862306a36Sopenharmony_ci rc = iscsi_xmit_task(conn, task, false); 167962306a36Sopenharmony_ci if (rc) 168062306a36Sopenharmony_ci goto done; 168162306a36Sopenharmony_ci /* 168262306a36Sopenharmony_ci * we could continuously get new task requests so 168362306a36Sopenharmony_ci * we need to check the mgmt queue for nops that need to 168462306a36Sopenharmony_ci * be sent to aviod starvation 168562306a36Sopenharmony_ci */ 168662306a36Sopenharmony_ci if (!list_empty(&conn->mgmtqueue)) 168762306a36Sopenharmony_ci goto check_mgmt; 168862306a36Sopenharmony_ci if (!list_empty(&conn->requeue)) 168962306a36Sopenharmony_ci goto check_requeue; 169062306a36Sopenharmony_ci } 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 169362306a36Sopenharmony_ci return -ENODATA; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_cidone: 169662306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 169762306a36Sopenharmony_ci return rc; 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_cistatic void iscsi_xmitworker(struct work_struct *work) 170162306a36Sopenharmony_ci{ 170262306a36Sopenharmony_ci struct iscsi_conn *conn = 170362306a36Sopenharmony_ci container_of(work, struct iscsi_conn, xmitwork); 170462306a36Sopenharmony_ci int rc; 170562306a36Sopenharmony_ci /* 170662306a36Sopenharmony_ci * serialize Xmit worker on a per-connection basis. 170762306a36Sopenharmony_ci */ 170862306a36Sopenharmony_ci do { 170962306a36Sopenharmony_ci rc = iscsi_data_xmit(conn); 171062306a36Sopenharmony_ci } while (rc >= 0 || rc == -EAGAIN); 171162306a36Sopenharmony_ci} 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, 171462306a36Sopenharmony_ci struct scsi_cmnd *sc) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci struct iscsi_task *task; 171762306a36Sopenharmony_ci 171862306a36Sopenharmony_ci if (!kfifo_out(&conn->session->cmdpool.queue, 171962306a36Sopenharmony_ci (void *) &task, sizeof(void *))) 172062306a36Sopenharmony_ci return NULL; 172162306a36Sopenharmony_ci 172262306a36Sopenharmony_ci iscsi_cmd(sc)->age = conn->session->age; 172362306a36Sopenharmony_ci iscsi_cmd(sc)->task = task; 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci refcount_set(&task->refcount, 1); 172662306a36Sopenharmony_ci task->state = ISCSI_TASK_PENDING; 172762306a36Sopenharmony_ci task->conn = conn; 172862306a36Sopenharmony_ci task->sc = sc; 172962306a36Sopenharmony_ci task->have_checked_conn = false; 173062306a36Sopenharmony_ci task->last_timeout = jiffies; 173162306a36Sopenharmony_ci task->last_xfer = jiffies; 173262306a36Sopenharmony_ci task->protected = false; 173362306a36Sopenharmony_ci INIT_LIST_HEAD(&task->running); 173462306a36Sopenharmony_ci return task; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cienum { 173862306a36Sopenharmony_ci FAILURE_BAD_HOST = 1, 173962306a36Sopenharmony_ci FAILURE_SESSION_FAILED, 174062306a36Sopenharmony_ci FAILURE_SESSION_FREED, 174162306a36Sopenharmony_ci FAILURE_WINDOW_CLOSED, 174262306a36Sopenharmony_ci FAILURE_OOM, 174362306a36Sopenharmony_ci FAILURE_SESSION_TERMINATE, 174462306a36Sopenharmony_ci FAILURE_SESSION_IN_RECOVERY, 174562306a36Sopenharmony_ci FAILURE_SESSION_RECOVERY_TIMEOUT, 174662306a36Sopenharmony_ci FAILURE_SESSION_LOGGING_OUT, 174762306a36Sopenharmony_ci FAILURE_SESSION_NOT_READY, 174862306a36Sopenharmony_ci}; 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ciint iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) 175162306a36Sopenharmony_ci{ 175262306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 175362306a36Sopenharmony_ci struct iscsi_host *ihost; 175462306a36Sopenharmony_ci int reason = 0; 175562306a36Sopenharmony_ci struct iscsi_session *session; 175662306a36Sopenharmony_ci struct iscsi_conn *conn; 175762306a36Sopenharmony_ci struct iscsi_task *task = NULL; 175862306a36Sopenharmony_ci 175962306a36Sopenharmony_ci sc->result = 0; 176062306a36Sopenharmony_ci iscsi_cmd(sc)->task = NULL; 176162306a36Sopenharmony_ci 176262306a36Sopenharmony_ci ihost = shost_priv(host); 176362306a36Sopenharmony_ci 176462306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 176562306a36Sopenharmony_ci session = cls_session->dd_data; 176662306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci reason = iscsi_session_chkready(cls_session); 176962306a36Sopenharmony_ci if (reason) { 177062306a36Sopenharmony_ci sc->result = reason; 177162306a36Sopenharmony_ci goto fault; 177262306a36Sopenharmony_ci } 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 177562306a36Sopenharmony_ci /* 177662306a36Sopenharmony_ci * to handle the race between when we set the recovery state 177762306a36Sopenharmony_ci * and block the session we requeue here (commands could 177862306a36Sopenharmony_ci * be entering our queuecommand while a block is starting 177962306a36Sopenharmony_ci * up because the block code is not locked) 178062306a36Sopenharmony_ci */ 178162306a36Sopenharmony_ci switch (session->state) { 178262306a36Sopenharmony_ci case ISCSI_STATE_FAILED: 178362306a36Sopenharmony_ci /* 178462306a36Sopenharmony_ci * cmds should fail during shutdown, if the session 178562306a36Sopenharmony_ci * state is bad, allowing completion to happen 178662306a36Sopenharmony_ci */ 178762306a36Sopenharmony_ci if (unlikely(system_state != SYSTEM_RUNNING)) { 178862306a36Sopenharmony_ci reason = FAILURE_SESSION_FAILED; 178962306a36Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 179062306a36Sopenharmony_ci break; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci fallthrough; 179362306a36Sopenharmony_ci case ISCSI_STATE_IN_RECOVERY: 179462306a36Sopenharmony_ci reason = FAILURE_SESSION_IN_RECOVERY; 179562306a36Sopenharmony_ci sc->result = DID_IMM_RETRY << 16; 179662306a36Sopenharmony_ci break; 179762306a36Sopenharmony_ci case ISCSI_STATE_LOGGING_OUT: 179862306a36Sopenharmony_ci reason = FAILURE_SESSION_LOGGING_OUT; 179962306a36Sopenharmony_ci sc->result = DID_IMM_RETRY << 16; 180062306a36Sopenharmony_ci break; 180162306a36Sopenharmony_ci case ISCSI_STATE_RECOVERY_FAILED: 180262306a36Sopenharmony_ci reason = FAILURE_SESSION_RECOVERY_TIMEOUT; 180362306a36Sopenharmony_ci sc->result = DID_TRANSPORT_FAILFAST << 16; 180462306a36Sopenharmony_ci break; 180562306a36Sopenharmony_ci case ISCSI_STATE_TERMINATE: 180662306a36Sopenharmony_ci reason = FAILURE_SESSION_TERMINATE; 180762306a36Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 180862306a36Sopenharmony_ci break; 180962306a36Sopenharmony_ci default: 181062306a36Sopenharmony_ci reason = FAILURE_SESSION_FREED; 181162306a36Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 181262306a36Sopenharmony_ci } 181362306a36Sopenharmony_ci goto fault; 181462306a36Sopenharmony_ci } 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci conn = session->leadconn; 181762306a36Sopenharmony_ci if (!conn) { 181862306a36Sopenharmony_ci reason = FAILURE_SESSION_FREED; 181962306a36Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 182062306a36Sopenharmony_ci goto fault; 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci if (test_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags)) { 182462306a36Sopenharmony_ci reason = FAILURE_SESSION_IN_RECOVERY; 182562306a36Sopenharmony_ci sc->result = DID_REQUEUE << 16; 182662306a36Sopenharmony_ci goto fault; 182762306a36Sopenharmony_ci } 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (iscsi_check_cmdsn_window_closed(conn)) { 183062306a36Sopenharmony_ci reason = FAILURE_WINDOW_CLOSED; 183162306a36Sopenharmony_ci goto reject; 183262306a36Sopenharmony_ci } 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci task = iscsi_alloc_task(conn, sc); 183562306a36Sopenharmony_ci if (!task) { 183662306a36Sopenharmony_ci reason = FAILURE_OOM; 183762306a36Sopenharmony_ci goto reject; 183862306a36Sopenharmony_ci } 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci if (!ihost->workq) { 184162306a36Sopenharmony_ci reason = iscsi_prep_scsi_cmd_pdu(task); 184262306a36Sopenharmony_ci if (reason) { 184362306a36Sopenharmony_ci if (reason == -ENOMEM || reason == -EACCES) { 184462306a36Sopenharmony_ci reason = FAILURE_OOM; 184562306a36Sopenharmony_ci goto prepd_reject; 184662306a36Sopenharmony_ci } else { 184762306a36Sopenharmony_ci sc->result = DID_ABORT << 16; 184862306a36Sopenharmony_ci goto prepd_fault; 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci } 185162306a36Sopenharmony_ci if (session->tt->xmit_task(task)) { 185262306a36Sopenharmony_ci session->cmdsn--; 185362306a36Sopenharmony_ci reason = FAILURE_SESSION_NOT_READY; 185462306a36Sopenharmony_ci goto prepd_reject; 185562306a36Sopenharmony_ci } 185662306a36Sopenharmony_ci } else { 185762306a36Sopenharmony_ci list_add_tail(&task->running, &conn->cmdqueue); 185862306a36Sopenharmony_ci iscsi_conn_queue_xmit(conn); 185962306a36Sopenharmony_ci } 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci session->queued_cmdsn++; 186262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 186362306a36Sopenharmony_ci return 0; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ciprepd_reject: 186662306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 186762306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); 186862306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 186962306a36Sopenharmony_cireject: 187062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 187162306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "cmd 0x%x rejected (%d)\n", 187262306a36Sopenharmony_ci sc->cmnd[0], reason); 187362306a36Sopenharmony_ci return SCSI_MLQUEUE_TARGET_BUSY; 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ciprepd_fault: 187662306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 187762306a36Sopenharmony_ci iscsi_complete_task(task, ISCSI_TASK_REQUEUE_SCSIQ); 187862306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 187962306a36Sopenharmony_cifault: 188062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 188162306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "iscsi: cmd 0x%x is not queued (%d)\n", 188262306a36Sopenharmony_ci sc->cmnd[0], reason); 188362306a36Sopenharmony_ci scsi_set_resid(sc, scsi_bufflen(sc)); 188462306a36Sopenharmony_ci scsi_done(sc); 188562306a36Sopenharmony_ci return 0; 188662306a36Sopenharmony_ci} 188762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_queuecommand); 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ciint iscsi_target_alloc(struct scsi_target *starget) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct iscsi_cls_session *cls_session = starget_to_session(starget); 189262306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 189362306a36Sopenharmony_ci 189462306a36Sopenharmony_ci starget->can_queue = session->scsi_cmds_max; 189562306a36Sopenharmony_ci return 0; 189662306a36Sopenharmony_ci} 189762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_target_alloc); 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_cistatic void iscsi_tmf_timedout(struct timer_list *t) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci struct iscsi_session *session = from_timer(session, t, tmf_timer); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci spin_lock(&session->frwd_lock); 190462306a36Sopenharmony_ci if (session->tmf_state == TMF_QUEUED) { 190562306a36Sopenharmony_ci session->tmf_state = TMF_TIMEDOUT; 190662306a36Sopenharmony_ci ISCSI_DBG_EH(session, "tmf timedout\n"); 190762306a36Sopenharmony_ci /* unblock eh_abort() */ 190862306a36Sopenharmony_ci wake_up(&session->ehwait); 190962306a36Sopenharmony_ci } 191062306a36Sopenharmony_ci spin_unlock(&session->frwd_lock); 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_cistatic int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, 191462306a36Sopenharmony_ci struct iscsi_tm *hdr, int age, 191562306a36Sopenharmony_ci int timeout) 191662306a36Sopenharmony_ci __must_hold(&session->frwd_lock) 191762306a36Sopenharmony_ci{ 191862306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci if (__iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0)) { 192162306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 192262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n"); 192362306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); 192462306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 192562306a36Sopenharmony_ci return -EPERM; 192662306a36Sopenharmony_ci } 192762306a36Sopenharmony_ci conn->tmfcmd_pdus_cnt++; 192862306a36Sopenharmony_ci session->tmf_timer.expires = timeout * HZ + jiffies; 192962306a36Sopenharmony_ci add_timer(&session->tmf_timer); 193062306a36Sopenharmony_ci ISCSI_DBG_EH(session, "tmf set timeout\n"); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 193362306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci /* 193662306a36Sopenharmony_ci * block eh thread until: 193762306a36Sopenharmony_ci * 193862306a36Sopenharmony_ci * 1) tmf response 193962306a36Sopenharmony_ci * 2) tmf timeout 194062306a36Sopenharmony_ci * 3) session is terminated or restarted or userspace has 194162306a36Sopenharmony_ci * given up on recovery 194262306a36Sopenharmony_ci */ 194362306a36Sopenharmony_ci wait_event_interruptible(session->ehwait, age != session->age || 194462306a36Sopenharmony_ci session->state != ISCSI_STATE_LOGGED_IN || 194562306a36Sopenharmony_ci session->tmf_state != TMF_QUEUED); 194662306a36Sopenharmony_ci if (signal_pending(current)) 194762306a36Sopenharmony_ci flush_signals(current); 194862306a36Sopenharmony_ci del_timer_sync(&session->tmf_timer); 194962306a36Sopenharmony_ci 195062306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 195162306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 195262306a36Sopenharmony_ci /* if the session drops it will clean up the task */ 195362306a36Sopenharmony_ci if (age != session->age || 195462306a36Sopenharmony_ci session->state != ISCSI_STATE_LOGGED_IN) 195562306a36Sopenharmony_ci return -ENOTCONN; 195662306a36Sopenharmony_ci return 0; 195762306a36Sopenharmony_ci} 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci/* 196062306a36Sopenharmony_ci * Fail commands. session frwd lock held and xmit thread flushed. 196162306a36Sopenharmony_ci */ 196262306a36Sopenharmony_cistatic void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) 196362306a36Sopenharmony_ci{ 196462306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 196562306a36Sopenharmony_ci struct iscsi_task *task; 196662306a36Sopenharmony_ci int i; 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cirestart_cmd_loop: 196962306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 197062306a36Sopenharmony_ci for (i = 0; i < session->cmds_max; i++) { 197162306a36Sopenharmony_ci task = session->cmds[i]; 197262306a36Sopenharmony_ci if (!task->sc || task->state == ISCSI_TASK_FREE) 197362306a36Sopenharmony_ci continue; 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_ci if (lun != -1 && lun != task->sc->device->lun) 197662306a36Sopenharmony_ci continue; 197762306a36Sopenharmony_ci /* 197862306a36Sopenharmony_ci * The cmd is completing but if this is called from an eh 197962306a36Sopenharmony_ci * callout path then when we return scsi-ml owns the cmd. Wait 198062306a36Sopenharmony_ci * for the completion path to finish freeing the cmd. 198162306a36Sopenharmony_ci */ 198262306a36Sopenharmony_ci if (!iscsi_get_task(task)) { 198362306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 198462306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 198562306a36Sopenharmony_ci udelay(ISCSI_CMD_COMPL_WAIT); 198662306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 198762306a36Sopenharmony_ci goto restart_cmd_loop; 198862306a36Sopenharmony_ci } 198962306a36Sopenharmony_ci 199062306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, 199162306a36Sopenharmony_ci "failing sc %p itt 0x%x state %d\n", 199262306a36Sopenharmony_ci task->sc, task->itt, task->state); 199362306a36Sopenharmony_ci __fail_scsi_task(task, error); 199462306a36Sopenharmony_ci __iscsi_put_task(task); 199562306a36Sopenharmony_ci } 199662306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 199762306a36Sopenharmony_ci} 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci/** 200062306a36Sopenharmony_ci * iscsi_suspend_queue - suspend iscsi_queuecommand 200162306a36Sopenharmony_ci * @conn: iscsi conn to stop queueing IO on 200262306a36Sopenharmony_ci * 200362306a36Sopenharmony_ci * This grabs the session frwd_lock to make sure no one is in 200462306a36Sopenharmony_ci * xmit_task/queuecommand, and then sets suspend to prevent 200562306a36Sopenharmony_ci * new commands from being queued. This only needs to be called 200662306a36Sopenharmony_ci * by offload drivers that need to sync a path like ep disconnect 200762306a36Sopenharmony_ci * with the iscsi_queuecommand/xmit_task. To start IO again libiscsi 200862306a36Sopenharmony_ci * will call iscsi_start_tx and iscsi_unblock_session when in FFP. 200962306a36Sopenharmony_ci */ 201062306a36Sopenharmony_civoid iscsi_suspend_queue(struct iscsi_conn *conn) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci spin_lock_bh(&conn->session->frwd_lock); 201362306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); 201462306a36Sopenharmony_ci spin_unlock_bh(&conn->session->frwd_lock); 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_suspend_queue); 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci/** 201962306a36Sopenharmony_ci * iscsi_suspend_tx - suspend iscsi_data_xmit 202062306a36Sopenharmony_ci * @conn: iscsi conn to stop processing IO on. 202162306a36Sopenharmony_ci * 202262306a36Sopenharmony_ci * This function sets the suspend bit to prevent iscsi_data_xmit 202362306a36Sopenharmony_ci * from sending new IO, and if work is queued on the xmit thread 202462306a36Sopenharmony_ci * it will wait for it to be completed. 202562306a36Sopenharmony_ci */ 202662306a36Sopenharmony_civoid iscsi_suspend_tx(struct iscsi_conn *conn) 202762306a36Sopenharmony_ci{ 202862306a36Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 202962306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 203062306a36Sopenharmony_ci 203162306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); 203262306a36Sopenharmony_ci if (ihost->workq) 203362306a36Sopenharmony_ci flush_work(&conn->xmitwork); 203462306a36Sopenharmony_ci} 203562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_suspend_tx); 203662306a36Sopenharmony_ci 203762306a36Sopenharmony_cistatic void iscsi_start_tx(struct iscsi_conn *conn) 203862306a36Sopenharmony_ci{ 203962306a36Sopenharmony_ci clear_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); 204062306a36Sopenharmony_ci iscsi_conn_queue_xmit(conn); 204162306a36Sopenharmony_ci} 204262306a36Sopenharmony_ci 204362306a36Sopenharmony_ci/** 204462306a36Sopenharmony_ci * iscsi_suspend_rx - Prevent recvwork from running again. 204562306a36Sopenharmony_ci * @conn: iscsi conn to stop. 204662306a36Sopenharmony_ci */ 204762306a36Sopenharmony_civoid iscsi_suspend_rx(struct iscsi_conn *conn) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci struct Scsi_Host *shost = conn->session->host; 205062306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags); 205362306a36Sopenharmony_ci if (ihost->workq) 205462306a36Sopenharmony_ci flush_work(&conn->recvwork); 205562306a36Sopenharmony_ci} 205662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_suspend_rx); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci/* 205962306a36Sopenharmony_ci * We want to make sure a ping is in flight. It has timed out. 206062306a36Sopenharmony_ci * And we are not busy processing a pdu that is making 206162306a36Sopenharmony_ci * progress but got started before the ping and is taking a while 206262306a36Sopenharmony_ci * to complete so the ping is just stuck behind it in a queue. 206362306a36Sopenharmony_ci */ 206462306a36Sopenharmony_cistatic int iscsi_has_ping_timed_out(struct iscsi_conn *conn) 206562306a36Sopenharmony_ci{ 206662306a36Sopenharmony_ci if (READ_ONCE(conn->ping_task) && 206762306a36Sopenharmony_ci time_before_eq(conn->last_recv + (conn->recv_timeout * HZ) + 206862306a36Sopenharmony_ci (conn->ping_timeout * HZ), jiffies)) 206962306a36Sopenharmony_ci return 1; 207062306a36Sopenharmony_ci else 207162306a36Sopenharmony_ci return 0; 207262306a36Sopenharmony_ci} 207362306a36Sopenharmony_ci 207462306a36Sopenharmony_cienum scsi_timeout_action iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) 207562306a36Sopenharmony_ci{ 207662306a36Sopenharmony_ci enum scsi_timeout_action rc = SCSI_EH_NOT_HANDLED; 207762306a36Sopenharmony_ci struct iscsi_task *task = NULL, *running_task; 207862306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 207962306a36Sopenharmony_ci struct iscsi_session *session; 208062306a36Sopenharmony_ci struct iscsi_conn *conn; 208162306a36Sopenharmony_ci int i; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 208462306a36Sopenharmony_ci session = cls_session->dd_data; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc); 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 208962306a36Sopenharmony_ci spin_lock(&session->back_lock); 209062306a36Sopenharmony_ci task = iscsi_cmd(sc)->task; 209162306a36Sopenharmony_ci if (!task) { 209262306a36Sopenharmony_ci /* 209362306a36Sopenharmony_ci * Raced with completion. Blk layer has taken ownership 209462306a36Sopenharmony_ci * so let timeout code complete it now. 209562306a36Sopenharmony_ci */ 209662306a36Sopenharmony_ci rc = SCSI_EH_NOT_HANDLED; 209762306a36Sopenharmony_ci spin_unlock(&session->back_lock); 209862306a36Sopenharmony_ci goto done; 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci if (!iscsi_get_task(task)) { 210162306a36Sopenharmony_ci /* 210262306a36Sopenharmony_ci * Racing with the completion path right now, so give it more 210362306a36Sopenharmony_ci * time so that path can complete it like normal. 210462306a36Sopenharmony_ci */ 210562306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 210662306a36Sopenharmony_ci task = NULL; 210762306a36Sopenharmony_ci spin_unlock(&session->back_lock); 210862306a36Sopenharmony_ci goto done; 210962306a36Sopenharmony_ci } 211062306a36Sopenharmony_ci spin_unlock(&session->back_lock); 211162306a36Sopenharmony_ci 211262306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 211362306a36Sopenharmony_ci /* 211462306a36Sopenharmony_ci * During shutdown, if session is prematurely disconnected, 211562306a36Sopenharmony_ci * recovery won't happen and there will be hung cmds. Not 211662306a36Sopenharmony_ci * handling cmds would trigger EH, also bad in this case. 211762306a36Sopenharmony_ci * Instead, handle cmd, allow completion to happen and let 211862306a36Sopenharmony_ci * upper layer to deal with the result. 211962306a36Sopenharmony_ci */ 212062306a36Sopenharmony_ci if (unlikely(system_state != SYSTEM_RUNNING)) { 212162306a36Sopenharmony_ci sc->result = DID_NO_CONNECT << 16; 212262306a36Sopenharmony_ci ISCSI_DBG_EH(session, "sc on shutdown, handled\n"); 212362306a36Sopenharmony_ci rc = SCSI_EH_NOT_HANDLED; 212462306a36Sopenharmony_ci goto done; 212562306a36Sopenharmony_ci } 212662306a36Sopenharmony_ci /* 212762306a36Sopenharmony_ci * We are probably in the middle of iscsi recovery so let 212862306a36Sopenharmony_ci * that complete and handle the error. 212962306a36Sopenharmony_ci */ 213062306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 213162306a36Sopenharmony_ci goto done; 213262306a36Sopenharmony_ci } 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci conn = session->leadconn; 213562306a36Sopenharmony_ci if (!conn) { 213662306a36Sopenharmony_ci /* In the middle of shuting down */ 213762306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 213862306a36Sopenharmony_ci goto done; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci /* 214262306a36Sopenharmony_ci * If we have sent (at least queued to the network layer) a pdu or 214362306a36Sopenharmony_ci * recvd one for the task since the last timeout ask for 214462306a36Sopenharmony_ci * more time. If on the next timeout we have not made progress 214562306a36Sopenharmony_ci * we can check if it is the task or connection when we send the 214662306a36Sopenharmony_ci * nop as a ping. 214762306a36Sopenharmony_ci */ 214862306a36Sopenharmony_ci if (time_after(task->last_xfer, task->last_timeout)) { 214962306a36Sopenharmony_ci ISCSI_DBG_EH(session, "Command making progress. Asking " 215062306a36Sopenharmony_ci "scsi-ml for more time to complete. " 215162306a36Sopenharmony_ci "Last data xfer at %lu. Last timeout was at " 215262306a36Sopenharmony_ci "%lu\n.", task->last_xfer, task->last_timeout); 215362306a36Sopenharmony_ci task->have_checked_conn = false; 215462306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 215562306a36Sopenharmony_ci goto done; 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci 215862306a36Sopenharmony_ci if (!conn->recv_timeout && !conn->ping_timeout) 215962306a36Sopenharmony_ci goto done; 216062306a36Sopenharmony_ci /* 216162306a36Sopenharmony_ci * if the ping timedout then we are in the middle of cleaning up 216262306a36Sopenharmony_ci * and can let the iscsi eh handle it 216362306a36Sopenharmony_ci */ 216462306a36Sopenharmony_ci if (iscsi_has_ping_timed_out(conn)) { 216562306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 216662306a36Sopenharmony_ci goto done; 216762306a36Sopenharmony_ci } 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci spin_lock(&session->back_lock); 217062306a36Sopenharmony_ci for (i = 0; i < conn->session->cmds_max; i++) { 217162306a36Sopenharmony_ci running_task = conn->session->cmds[i]; 217262306a36Sopenharmony_ci if (!running_task->sc || running_task == task || 217362306a36Sopenharmony_ci running_task->state != ISCSI_TASK_RUNNING) 217462306a36Sopenharmony_ci continue; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci /* 217762306a36Sopenharmony_ci * Only check if cmds started before this one have made 217862306a36Sopenharmony_ci * progress, or this could never fail 217962306a36Sopenharmony_ci */ 218062306a36Sopenharmony_ci if (time_after(running_task->sc->jiffies_at_alloc, 218162306a36Sopenharmony_ci task->sc->jiffies_at_alloc)) 218262306a36Sopenharmony_ci continue; 218362306a36Sopenharmony_ci 218462306a36Sopenharmony_ci if (time_after(running_task->last_xfer, task->last_timeout)) { 218562306a36Sopenharmony_ci /* 218662306a36Sopenharmony_ci * This task has not made progress, but a task 218762306a36Sopenharmony_ci * started before us has transferred data since 218862306a36Sopenharmony_ci * we started/last-checked. We could be queueing 218962306a36Sopenharmony_ci * too many tasks or the LU is bad. 219062306a36Sopenharmony_ci * 219162306a36Sopenharmony_ci * If the device is bad the cmds ahead of us on 219262306a36Sopenharmony_ci * other devs will complete, and this loop will 219362306a36Sopenharmony_ci * eventually fail starting the scsi eh. 219462306a36Sopenharmony_ci */ 219562306a36Sopenharmony_ci ISCSI_DBG_EH(session, "Command has not made progress " 219662306a36Sopenharmony_ci "but commands ahead of it have. " 219762306a36Sopenharmony_ci "Asking scsi-ml for more time to " 219862306a36Sopenharmony_ci "complete. Our last xfer vs running task " 219962306a36Sopenharmony_ci "last xfer %lu/%lu. Last check %lu.\n", 220062306a36Sopenharmony_ci task->last_xfer, running_task->last_xfer, 220162306a36Sopenharmony_ci task->last_timeout); 220262306a36Sopenharmony_ci spin_unlock(&session->back_lock); 220362306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 220462306a36Sopenharmony_ci goto done; 220562306a36Sopenharmony_ci } 220662306a36Sopenharmony_ci } 220762306a36Sopenharmony_ci spin_unlock(&session->back_lock); 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci /* Assumes nop timeout is shorter than scsi cmd timeout */ 221062306a36Sopenharmony_ci if (task->have_checked_conn) 221162306a36Sopenharmony_ci goto done; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_ci /* 221462306a36Sopenharmony_ci * Checking the transport already or nop from a cmd timeout still 221562306a36Sopenharmony_ci * running 221662306a36Sopenharmony_ci */ 221762306a36Sopenharmony_ci if (READ_ONCE(conn->ping_task)) { 221862306a36Sopenharmony_ci task->have_checked_conn = true; 221962306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 222062306a36Sopenharmony_ci goto done; 222162306a36Sopenharmony_ci } 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci /* Make sure there is a transport check done */ 222462306a36Sopenharmony_ci iscsi_send_nopout(conn, NULL); 222562306a36Sopenharmony_ci task->have_checked_conn = true; 222662306a36Sopenharmony_ci rc = SCSI_EH_RESET_TIMER; 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_cidone: 222962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci if (task) { 223262306a36Sopenharmony_ci task->last_timeout = jiffies; 223362306a36Sopenharmony_ci iscsi_put_task(task); 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci ISCSI_DBG_EH(session, "return %s\n", rc == SCSI_EH_RESET_TIMER ? 223662306a36Sopenharmony_ci "timer reset" : "shutdown or nh"); 223762306a36Sopenharmony_ci return rc; 223862306a36Sopenharmony_ci} 223962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_cmd_timed_out); 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_cistatic void iscsi_check_transport_timeouts(struct timer_list *t) 224262306a36Sopenharmony_ci{ 224362306a36Sopenharmony_ci struct iscsi_conn *conn = from_timer(conn, t, transport_timer); 224462306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 224562306a36Sopenharmony_ci unsigned long recv_timeout, next_timeout = 0, last_recv; 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_ci spin_lock(&session->frwd_lock); 224862306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) 224962306a36Sopenharmony_ci goto done; 225062306a36Sopenharmony_ci 225162306a36Sopenharmony_ci recv_timeout = conn->recv_timeout; 225262306a36Sopenharmony_ci if (!recv_timeout) 225362306a36Sopenharmony_ci goto done; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci recv_timeout *= HZ; 225662306a36Sopenharmony_ci last_recv = conn->last_recv; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci if (iscsi_has_ping_timed_out(conn)) { 225962306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "ping timeout of %d secs " 226062306a36Sopenharmony_ci "expired, recv timeout %d, last rx %lu, " 226162306a36Sopenharmony_ci "last ping %lu, now %lu\n", 226262306a36Sopenharmony_ci conn->ping_timeout, conn->recv_timeout, 226362306a36Sopenharmony_ci last_recv, conn->last_ping, jiffies); 226462306a36Sopenharmony_ci spin_unlock(&session->frwd_lock); 226562306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_NOP_TIMEDOUT); 226662306a36Sopenharmony_ci return; 226762306a36Sopenharmony_ci } 226862306a36Sopenharmony_ci 226962306a36Sopenharmony_ci if (time_before_eq(last_recv + recv_timeout, jiffies)) { 227062306a36Sopenharmony_ci /* send a ping to try to provoke some traffic */ 227162306a36Sopenharmony_ci ISCSI_DBG_CONN(conn, "Sending nopout as ping\n"); 227262306a36Sopenharmony_ci if (iscsi_send_nopout(conn, NULL)) 227362306a36Sopenharmony_ci next_timeout = jiffies + (1 * HZ); 227462306a36Sopenharmony_ci else 227562306a36Sopenharmony_ci next_timeout = conn->last_ping + (conn->ping_timeout * HZ); 227662306a36Sopenharmony_ci } else 227762306a36Sopenharmony_ci next_timeout = last_recv + recv_timeout; 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci ISCSI_DBG_CONN(conn, "Setting next tmo %lu\n", next_timeout); 228062306a36Sopenharmony_ci mod_timer(&conn->transport_timer, next_timeout); 228162306a36Sopenharmony_cidone: 228262306a36Sopenharmony_ci spin_unlock(&session->frwd_lock); 228362306a36Sopenharmony_ci} 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci/** 228662306a36Sopenharmony_ci * iscsi_conn_unbind - prevent queueing to conn. 228762306a36Sopenharmony_ci * @cls_conn: iscsi conn ep is bound to. 228862306a36Sopenharmony_ci * @is_active: is the conn in use for boot or is this for EH/termination 228962306a36Sopenharmony_ci * 229062306a36Sopenharmony_ci * This must be called by drivers implementing the ep_disconnect callout. 229162306a36Sopenharmony_ci * It disables queueing to the connection from libiscsi in preparation for 229262306a36Sopenharmony_ci * an ep_disconnect call. 229362306a36Sopenharmony_ci */ 229462306a36Sopenharmony_civoid iscsi_conn_unbind(struct iscsi_cls_conn *cls_conn, bool is_active) 229562306a36Sopenharmony_ci{ 229662306a36Sopenharmony_ci struct iscsi_session *session; 229762306a36Sopenharmony_ci struct iscsi_conn *conn; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci if (!cls_conn) 230062306a36Sopenharmony_ci return; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci conn = cls_conn->dd_data; 230362306a36Sopenharmony_ci session = conn->session; 230462306a36Sopenharmony_ci /* 230562306a36Sopenharmony_ci * Wait for iscsi_eh calls to exit. We don't wait for the tmf to 230662306a36Sopenharmony_ci * complete or timeout. The caller just wants to know what's running 230762306a36Sopenharmony_ci * is everything that needs to be cleaned up, and no cmds will be 230862306a36Sopenharmony_ci * queued. 230962306a36Sopenharmony_ci */ 231062306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 231162306a36Sopenharmony_ci 231262306a36Sopenharmony_ci iscsi_suspend_queue(conn); 231362306a36Sopenharmony_ci iscsi_suspend_tx(conn); 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 231662306a36Sopenharmony_ci clear_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci if (!is_active) { 231962306a36Sopenharmony_ci /* 232062306a36Sopenharmony_ci * if logout timed out before userspace could even send a PDU 232162306a36Sopenharmony_ci * the state might still be in ISCSI_STATE_LOGGED_IN and 232262306a36Sopenharmony_ci * allowing new cmds and TMFs. 232362306a36Sopenharmony_ci */ 232462306a36Sopenharmony_ci if (session->state == ISCSI_STATE_LOGGED_IN) 232562306a36Sopenharmony_ci iscsi_set_conn_failed(conn); 232662306a36Sopenharmony_ci } 232762306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 232862306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 232962306a36Sopenharmony_ci} 233062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_unbind); 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_cistatic void iscsi_prep_abort_task_pdu(struct iscsi_task *task, 233362306a36Sopenharmony_ci struct iscsi_tm *hdr) 233462306a36Sopenharmony_ci{ 233562306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 233662306a36Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 233762306a36Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_ABORT_TASK & ISCSI_FLAG_TM_FUNC_MASK; 233862306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 233962306a36Sopenharmony_ci hdr->lun = task->lun; 234062306a36Sopenharmony_ci hdr->rtt = task->hdr_itt; 234162306a36Sopenharmony_ci hdr->refcmdsn = task->cmdsn; 234262306a36Sopenharmony_ci} 234362306a36Sopenharmony_ci 234462306a36Sopenharmony_ciint iscsi_eh_abort(struct scsi_cmnd *sc) 234562306a36Sopenharmony_ci{ 234662306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 234762306a36Sopenharmony_ci struct iscsi_session *session; 234862306a36Sopenharmony_ci struct iscsi_conn *conn; 234962306a36Sopenharmony_ci struct iscsi_task *task; 235062306a36Sopenharmony_ci struct iscsi_tm *hdr; 235162306a36Sopenharmony_ci int age; 235262306a36Sopenharmony_ci 235362306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 235462306a36Sopenharmony_ci session = cls_session->dd_data; 235562306a36Sopenharmony_ci 235662306a36Sopenharmony_ci ISCSI_DBG_EH(session, "aborting sc %p\n", sc); 235762306a36Sopenharmony_ci 235862306a36Sopenharmony_cicompletion_check: 235962306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 236062306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 236162306a36Sopenharmony_ci /* 236262306a36Sopenharmony_ci * if session was ISCSI_STATE_IN_RECOVERY then we may not have 236362306a36Sopenharmony_ci * got the command. 236462306a36Sopenharmony_ci */ 236562306a36Sopenharmony_ci if (!iscsi_cmd(sc)->task) { 236662306a36Sopenharmony_ci ISCSI_DBG_EH(session, "sc never reached iscsi layer or " 236762306a36Sopenharmony_ci "it completed.\n"); 236862306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 236962306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 237062306a36Sopenharmony_ci return SUCCESS; 237162306a36Sopenharmony_ci } 237262306a36Sopenharmony_ci 237362306a36Sopenharmony_ci /* 237462306a36Sopenharmony_ci * If we are not logged in or we have started a new session 237562306a36Sopenharmony_ci * then let the host reset code handle this 237662306a36Sopenharmony_ci */ 237762306a36Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN || 237862306a36Sopenharmony_ci iscsi_cmd(sc)->age != session->age) { 237962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 238062306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 238162306a36Sopenharmony_ci ISCSI_DBG_EH(session, "failing abort due to dropped " 238262306a36Sopenharmony_ci "session.\n"); 238362306a36Sopenharmony_ci return FAILED; 238462306a36Sopenharmony_ci } 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci spin_lock(&session->back_lock); 238762306a36Sopenharmony_ci task = iscsi_cmd(sc)->task; 238862306a36Sopenharmony_ci if (!task || !task->sc) { 238962306a36Sopenharmony_ci /* task completed before time out */ 239062306a36Sopenharmony_ci ISCSI_DBG_EH(session, "sc completed while abort in progress\n"); 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci spin_unlock(&session->back_lock); 239362306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 239462306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 239562306a36Sopenharmony_ci return SUCCESS; 239662306a36Sopenharmony_ci } 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (!iscsi_get_task(task)) { 239962306a36Sopenharmony_ci spin_unlock(&session->back_lock); 240062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 240162306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 240262306a36Sopenharmony_ci /* We are just about to call iscsi_free_task so wait for it. */ 240362306a36Sopenharmony_ci udelay(ISCSI_CMD_COMPL_WAIT); 240462306a36Sopenharmony_ci goto completion_check; 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt); 240862306a36Sopenharmony_ci conn = session->leadconn; 240962306a36Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 241062306a36Sopenharmony_ci conn->eh_abort_cnt++; 241162306a36Sopenharmony_ci age = session->age; 241262306a36Sopenharmony_ci spin_unlock(&session->back_lock); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) { 241562306a36Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 241662306a36Sopenharmony_ci goto success; 241762306a36Sopenharmony_ci } 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci /* only have one tmf outstanding at a time */ 242062306a36Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 242162306a36Sopenharmony_ci goto failed; 242262306a36Sopenharmony_ci session->tmf_state = TMF_QUEUED; 242362306a36Sopenharmony_ci 242462306a36Sopenharmony_ci hdr = &session->tmhdr; 242562306a36Sopenharmony_ci iscsi_prep_abort_task_pdu(task, hdr); 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, age, session->abort_timeout)) 242862306a36Sopenharmony_ci goto failed; 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci switch (session->tmf_state) { 243162306a36Sopenharmony_ci case TMF_SUCCESS: 243262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 243362306a36Sopenharmony_ci /* 243462306a36Sopenharmony_ci * stop tx side incase the target had sent a abort rsp but 243562306a36Sopenharmony_ci * the initiator was still writing out data. 243662306a36Sopenharmony_ci */ 243762306a36Sopenharmony_ci iscsi_suspend_tx(conn); 243862306a36Sopenharmony_ci /* 243962306a36Sopenharmony_ci * we do not stop the recv side because targets have been 244062306a36Sopenharmony_ci * good and have never sent us a successful tmf response 244162306a36Sopenharmony_ci * then sent more data for the cmd. 244262306a36Sopenharmony_ci */ 244362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 244462306a36Sopenharmony_ci fail_scsi_task(task, DID_ABORT); 244562306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 244662306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 244762306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 244862306a36Sopenharmony_ci iscsi_start_tx(conn); 244962306a36Sopenharmony_ci goto success_unlocked; 245062306a36Sopenharmony_ci case TMF_TIMEDOUT: 245162306a36Sopenharmony_ci session->running_aborted_task = task; 245262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 245362306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 245462306a36Sopenharmony_ci goto failed_unlocked; 245562306a36Sopenharmony_ci case TMF_NOT_FOUND: 245662306a36Sopenharmony_ci if (iscsi_task_is_completed(task)) { 245762306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 245862306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 245962306a36Sopenharmony_ci /* task completed before tmf abort response */ 246062306a36Sopenharmony_ci ISCSI_DBG_EH(session, "sc completed while abort in " 246162306a36Sopenharmony_ci "progress\n"); 246262306a36Sopenharmony_ci goto success; 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci fallthrough; 246562306a36Sopenharmony_ci default: 246662306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 246762306a36Sopenharmony_ci goto failed; 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_cisuccess: 247162306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 247262306a36Sopenharmony_cisuccess_unlocked: 247362306a36Sopenharmony_ci ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n", 247462306a36Sopenharmony_ci sc, task->itt); 247562306a36Sopenharmony_ci iscsi_put_task(task); 247662306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 247762306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 247862306a36Sopenharmony_ci return SUCCESS; 247962306a36Sopenharmony_ci 248062306a36Sopenharmony_cifailed: 248162306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 248262306a36Sopenharmony_cifailed_unlocked: 248362306a36Sopenharmony_ci ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc, 248462306a36Sopenharmony_ci task ? task->itt : 0); 248562306a36Sopenharmony_ci /* 248662306a36Sopenharmony_ci * The driver might be accessing the task so hold the ref. The conn 248762306a36Sopenharmony_ci * stop cleanup will drop the ref after ep_disconnect so we know the 248862306a36Sopenharmony_ci * driver's no longer touching the task. 248962306a36Sopenharmony_ci */ 249062306a36Sopenharmony_ci if (!session->running_aborted_task) 249162306a36Sopenharmony_ci iscsi_put_task(task); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 249462306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 249562306a36Sopenharmony_ci return FAILED; 249662306a36Sopenharmony_ci} 249762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_abort); 249862306a36Sopenharmony_ci 249962306a36Sopenharmony_cistatic void iscsi_prep_lun_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) 250062306a36Sopenharmony_ci{ 250162306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 250262306a36Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 250362306a36Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_LOGICAL_UNIT_RESET & ISCSI_FLAG_TM_FUNC_MASK; 250462306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 250562306a36Sopenharmony_ci int_to_scsilun(sc->device->lun, &hdr->lun); 250662306a36Sopenharmony_ci hdr->rtt = RESERVED_ITT; 250762306a36Sopenharmony_ci} 250862306a36Sopenharmony_ci 250962306a36Sopenharmony_ciint iscsi_eh_device_reset(struct scsi_cmnd *sc) 251062306a36Sopenharmony_ci{ 251162306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 251262306a36Sopenharmony_ci struct iscsi_session *session; 251362306a36Sopenharmony_ci struct iscsi_conn *conn; 251462306a36Sopenharmony_ci struct iscsi_tm *hdr; 251562306a36Sopenharmony_ci int rc = FAILED; 251662306a36Sopenharmony_ci 251762306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 251862306a36Sopenharmony_ci session = cls_session->dd_data; 251962306a36Sopenharmony_ci 252062306a36Sopenharmony_ci ISCSI_DBG_EH(session, "LU Reset [sc %p lun %llu]\n", sc, 252162306a36Sopenharmony_ci sc->device->lun); 252262306a36Sopenharmony_ci 252362306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 252462306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 252562306a36Sopenharmony_ci /* 252662306a36Sopenharmony_ci * Just check if we are not logged in. We cannot check for 252762306a36Sopenharmony_ci * the phase because the reset could come from a ioctl. 252862306a36Sopenharmony_ci */ 252962306a36Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) 253062306a36Sopenharmony_ci goto unlock; 253162306a36Sopenharmony_ci conn = session->leadconn; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci /* only have one tmf outstanding at a time */ 253462306a36Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 253562306a36Sopenharmony_ci goto unlock; 253662306a36Sopenharmony_ci session->tmf_state = TMF_QUEUED; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci hdr = &session->tmhdr; 253962306a36Sopenharmony_ci iscsi_prep_lun_reset_pdu(sc, hdr); 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, 254262306a36Sopenharmony_ci session->lu_reset_timeout)) { 254362306a36Sopenharmony_ci rc = FAILED; 254462306a36Sopenharmony_ci goto unlock; 254562306a36Sopenharmony_ci } 254662306a36Sopenharmony_ci 254762306a36Sopenharmony_ci switch (session->tmf_state) { 254862306a36Sopenharmony_ci case TMF_SUCCESS: 254962306a36Sopenharmony_ci break; 255062306a36Sopenharmony_ci case TMF_TIMEDOUT: 255162306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 255262306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 255362306a36Sopenharmony_ci goto done; 255462306a36Sopenharmony_ci default: 255562306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 255662306a36Sopenharmony_ci goto unlock; 255762306a36Sopenharmony_ci } 255862306a36Sopenharmony_ci 255962306a36Sopenharmony_ci rc = SUCCESS; 256062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci iscsi_suspend_tx(conn); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 256562306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 256662306a36Sopenharmony_ci fail_scsi_tasks(conn, sc->device->lun, DID_ERROR); 256762306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 256862306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci iscsi_start_tx(conn); 257162306a36Sopenharmony_ci goto done; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ciunlock: 257462306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 257562306a36Sopenharmony_cidone: 257662306a36Sopenharmony_ci ISCSI_DBG_EH(session, "dev reset result = %s\n", 257762306a36Sopenharmony_ci rc == SUCCESS ? "SUCCESS" : "FAILED"); 257862306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 257962306a36Sopenharmony_ci return rc; 258062306a36Sopenharmony_ci} 258162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_device_reset); 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_civoid iscsi_session_recovery_timedout(struct iscsi_cls_session *cls_session) 258462306a36Sopenharmony_ci{ 258562306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 258662306a36Sopenharmony_ci 258762306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 258862306a36Sopenharmony_ci if (session->state != ISCSI_STATE_LOGGED_IN) { 258962306a36Sopenharmony_ci session->state = ISCSI_STATE_RECOVERY_FAILED; 259062306a36Sopenharmony_ci wake_up(&session->ehwait); 259162306a36Sopenharmony_ci } 259262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 259362306a36Sopenharmony_ci} 259462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_recovery_timedout); 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci/** 259762306a36Sopenharmony_ci * iscsi_eh_session_reset - drop session and attempt relogin 259862306a36Sopenharmony_ci * @sc: scsi command 259962306a36Sopenharmony_ci * 260062306a36Sopenharmony_ci * This function will wait for a relogin, session termination from 260162306a36Sopenharmony_ci * userspace, or a recovery/replacement timeout. 260262306a36Sopenharmony_ci */ 260362306a36Sopenharmony_ciint iscsi_eh_session_reset(struct scsi_cmnd *sc) 260462306a36Sopenharmony_ci{ 260562306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 260662306a36Sopenharmony_ci struct iscsi_session *session; 260762306a36Sopenharmony_ci struct iscsi_conn *conn; 260862306a36Sopenharmony_ci 260962306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 261062306a36Sopenharmony_ci session = cls_session->dd_data; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 261362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 261462306a36Sopenharmony_ci if (session->state == ISCSI_STATE_TERMINATE) { 261562306a36Sopenharmony_cifailed: 261662306a36Sopenharmony_ci ISCSI_DBG_EH(session, 261762306a36Sopenharmony_ci "failing session reset: Could not log back into " 261862306a36Sopenharmony_ci "%s [age %d]\n", session->targetname, 261962306a36Sopenharmony_ci session->age); 262062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 262162306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 262262306a36Sopenharmony_ci return FAILED; 262362306a36Sopenharmony_ci } 262462306a36Sopenharmony_ci 262562306a36Sopenharmony_ci conn = session->leadconn; 262662306a36Sopenharmony_ci iscsi_get_conn(conn->cls_conn); 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 262962306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 263262306a36Sopenharmony_ci iscsi_put_conn(conn->cls_conn); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci ISCSI_DBG_EH(session, "wait for relogin\n"); 263562306a36Sopenharmony_ci wait_event_interruptible(session->ehwait, 263662306a36Sopenharmony_ci session->state == ISCSI_STATE_TERMINATE || 263762306a36Sopenharmony_ci session->state == ISCSI_STATE_LOGGED_IN || 263862306a36Sopenharmony_ci session->state == ISCSI_STATE_RECOVERY_FAILED); 263962306a36Sopenharmony_ci if (signal_pending(current)) 264062306a36Sopenharmony_ci flush_signals(current); 264162306a36Sopenharmony_ci 264262306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 264362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 264462306a36Sopenharmony_ci if (session->state == ISCSI_STATE_LOGGED_IN) { 264562306a36Sopenharmony_ci ISCSI_DBG_EH(session, 264662306a36Sopenharmony_ci "session reset succeeded for %s,%s\n", 264762306a36Sopenharmony_ci session->targetname, conn->persistent_address); 264862306a36Sopenharmony_ci } else 264962306a36Sopenharmony_ci goto failed; 265062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 265162306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 265262306a36Sopenharmony_ci return SUCCESS; 265362306a36Sopenharmony_ci} 265462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_session_reset); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_cistatic void iscsi_prep_tgt_reset_pdu(struct scsi_cmnd *sc, struct iscsi_tm *hdr) 265762306a36Sopenharmony_ci{ 265862306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 265962306a36Sopenharmony_ci hdr->opcode = ISCSI_OP_SCSI_TMFUNC | ISCSI_OP_IMMEDIATE; 266062306a36Sopenharmony_ci hdr->flags = ISCSI_TM_FUNC_TARGET_WARM_RESET & ISCSI_FLAG_TM_FUNC_MASK; 266162306a36Sopenharmony_ci hdr->flags |= ISCSI_FLAG_CMD_FINAL; 266262306a36Sopenharmony_ci hdr->rtt = RESERVED_ITT; 266362306a36Sopenharmony_ci} 266462306a36Sopenharmony_ci 266562306a36Sopenharmony_ci/** 266662306a36Sopenharmony_ci * iscsi_eh_target_reset - reset target 266762306a36Sopenharmony_ci * @sc: scsi command 266862306a36Sopenharmony_ci * 266962306a36Sopenharmony_ci * This will attempt to send a warm target reset. 267062306a36Sopenharmony_ci */ 267162306a36Sopenharmony_cistatic int iscsi_eh_target_reset(struct scsi_cmnd *sc) 267262306a36Sopenharmony_ci{ 267362306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 267462306a36Sopenharmony_ci struct iscsi_session *session; 267562306a36Sopenharmony_ci struct iscsi_conn *conn; 267662306a36Sopenharmony_ci struct iscsi_tm *hdr; 267762306a36Sopenharmony_ci int rc = FAILED; 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci cls_session = starget_to_session(scsi_target(sc->device)); 268062306a36Sopenharmony_ci session = cls_session->dd_data; 268162306a36Sopenharmony_ci 268262306a36Sopenharmony_ci ISCSI_DBG_EH(session, "tgt Reset [sc %p tgt %s]\n", sc, 268362306a36Sopenharmony_ci session->targetname); 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 268662306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 268762306a36Sopenharmony_ci /* 268862306a36Sopenharmony_ci * Just check if we are not logged in. We cannot check for 268962306a36Sopenharmony_ci * the phase because the reset could come from a ioctl. 269062306a36Sopenharmony_ci */ 269162306a36Sopenharmony_ci if (!session->leadconn || session->state != ISCSI_STATE_LOGGED_IN) 269262306a36Sopenharmony_ci goto unlock; 269362306a36Sopenharmony_ci conn = session->leadconn; 269462306a36Sopenharmony_ci 269562306a36Sopenharmony_ci /* only have one tmf outstanding at a time */ 269662306a36Sopenharmony_ci if (session->tmf_state != TMF_INITIAL) 269762306a36Sopenharmony_ci goto unlock; 269862306a36Sopenharmony_ci session->tmf_state = TMF_QUEUED; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci hdr = &session->tmhdr; 270162306a36Sopenharmony_ci iscsi_prep_tgt_reset_pdu(sc, hdr); 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci if (iscsi_exec_task_mgmt_fn(conn, hdr, session->age, 270462306a36Sopenharmony_ci session->tgt_reset_timeout)) { 270562306a36Sopenharmony_ci rc = FAILED; 270662306a36Sopenharmony_ci goto unlock; 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_ci switch (session->tmf_state) { 271062306a36Sopenharmony_ci case TMF_SUCCESS: 271162306a36Sopenharmony_ci break; 271262306a36Sopenharmony_ci case TMF_TIMEDOUT: 271362306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 271462306a36Sopenharmony_ci iscsi_conn_failure(conn, ISCSI_ERR_SCSI_EH_SESSION_RST); 271562306a36Sopenharmony_ci goto done; 271662306a36Sopenharmony_ci default: 271762306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 271862306a36Sopenharmony_ci goto unlock; 271962306a36Sopenharmony_ci } 272062306a36Sopenharmony_ci 272162306a36Sopenharmony_ci rc = SUCCESS; 272262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 272362306a36Sopenharmony_ci 272462306a36Sopenharmony_ci iscsi_suspend_tx(conn); 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 272762306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 272862306a36Sopenharmony_ci fail_scsi_tasks(conn, -1, DID_ERROR); 272962306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 273062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 273162306a36Sopenharmony_ci 273262306a36Sopenharmony_ci iscsi_start_tx(conn); 273362306a36Sopenharmony_ci goto done; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ciunlock: 273662306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 273762306a36Sopenharmony_cidone: 273862306a36Sopenharmony_ci ISCSI_DBG_EH(session, "tgt %s reset result = %s\n", session->targetname, 273962306a36Sopenharmony_ci rc == SUCCESS ? "SUCCESS" : "FAILED"); 274062306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 274162306a36Sopenharmony_ci return rc; 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci/** 274562306a36Sopenharmony_ci * iscsi_eh_recover_target - reset target and possibly the session 274662306a36Sopenharmony_ci * @sc: scsi command 274762306a36Sopenharmony_ci * 274862306a36Sopenharmony_ci * This will attempt to send a warm target reset. If that fails, 274962306a36Sopenharmony_ci * we will escalate to ERL0 session recovery. 275062306a36Sopenharmony_ci */ 275162306a36Sopenharmony_ciint iscsi_eh_recover_target(struct scsi_cmnd *sc) 275262306a36Sopenharmony_ci{ 275362306a36Sopenharmony_ci int rc; 275462306a36Sopenharmony_ci 275562306a36Sopenharmony_ci rc = iscsi_eh_target_reset(sc); 275662306a36Sopenharmony_ci if (rc == FAILED) 275762306a36Sopenharmony_ci rc = iscsi_eh_session_reset(sc); 275862306a36Sopenharmony_ci return rc; 275962306a36Sopenharmony_ci} 276062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_eh_recover_target); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci/* 276362306a36Sopenharmony_ci * Pre-allocate a pool of @max items of @item_size. By default, the pool 276462306a36Sopenharmony_ci * should be accessed via kfifo_{get,put} on q->queue. 276562306a36Sopenharmony_ci * Optionally, the caller can obtain the array of object pointers 276662306a36Sopenharmony_ci * by passing in a non-NULL @items pointer 276762306a36Sopenharmony_ci */ 276862306a36Sopenharmony_ciint 276962306a36Sopenharmony_ciiscsi_pool_init(struct iscsi_pool *q, int max, void ***items, int item_size) 277062306a36Sopenharmony_ci{ 277162306a36Sopenharmony_ci int i, num_arrays = 1; 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_ci memset(q, 0, sizeof(*q)); 277462306a36Sopenharmony_ci 277562306a36Sopenharmony_ci q->max = max; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci /* If the user passed an items pointer, he wants a copy of 277862306a36Sopenharmony_ci * the array. */ 277962306a36Sopenharmony_ci if (items) 278062306a36Sopenharmony_ci num_arrays++; 278162306a36Sopenharmony_ci q->pool = kvcalloc(num_arrays * max, sizeof(void *), GFP_KERNEL); 278262306a36Sopenharmony_ci if (q->pool == NULL) 278362306a36Sopenharmony_ci return -ENOMEM; 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci kfifo_init(&q->queue, (void*)q->pool, max * sizeof(void*)); 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci for (i = 0; i < max; i++) { 278862306a36Sopenharmony_ci q->pool[i] = kzalloc(item_size, GFP_KERNEL); 278962306a36Sopenharmony_ci if (q->pool[i] == NULL) { 279062306a36Sopenharmony_ci q->max = i; 279162306a36Sopenharmony_ci goto enomem; 279262306a36Sopenharmony_ci } 279362306a36Sopenharmony_ci kfifo_in(&q->queue, (void*)&q->pool[i], sizeof(void*)); 279462306a36Sopenharmony_ci } 279562306a36Sopenharmony_ci 279662306a36Sopenharmony_ci if (items) { 279762306a36Sopenharmony_ci *items = q->pool + max; 279862306a36Sopenharmony_ci memcpy(*items, q->pool, max * sizeof(void *)); 279962306a36Sopenharmony_ci } 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci return 0; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cienomem: 280462306a36Sopenharmony_ci iscsi_pool_free(q); 280562306a36Sopenharmony_ci return -ENOMEM; 280662306a36Sopenharmony_ci} 280762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_pool_init); 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_civoid iscsi_pool_free(struct iscsi_pool *q) 281062306a36Sopenharmony_ci{ 281162306a36Sopenharmony_ci int i; 281262306a36Sopenharmony_ci 281362306a36Sopenharmony_ci for (i = 0; i < q->max; i++) 281462306a36Sopenharmony_ci kfree(q->pool[i]); 281562306a36Sopenharmony_ci kvfree(q->pool); 281662306a36Sopenharmony_ci} 281762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_pool_free); 281862306a36Sopenharmony_ci 281962306a36Sopenharmony_ciint iscsi_host_get_max_scsi_cmds(struct Scsi_Host *shost, 282062306a36Sopenharmony_ci uint16_t requested_cmds_max) 282162306a36Sopenharmony_ci{ 282262306a36Sopenharmony_ci int scsi_cmds, total_cmds = requested_cmds_max; 282362306a36Sopenharmony_ci 282462306a36Sopenharmony_cicheck: 282562306a36Sopenharmony_ci if (!total_cmds) 282662306a36Sopenharmony_ci total_cmds = ISCSI_DEF_XMIT_CMDS_MAX; 282762306a36Sopenharmony_ci /* 282862306a36Sopenharmony_ci * The iscsi layer needs some tasks for nop handling and tmfs, 282962306a36Sopenharmony_ci * so the cmds_max must at least be greater than ISCSI_MGMT_CMDS_MAX 283062306a36Sopenharmony_ci * + 1 command for scsi IO. 283162306a36Sopenharmony_ci */ 283262306a36Sopenharmony_ci if (total_cmds < ISCSI_TOTAL_CMDS_MIN) { 283362306a36Sopenharmony_ci printk(KERN_ERR "iscsi: invalid max cmds of %d. Must be a power of two that is at least %d.\n", 283462306a36Sopenharmony_ci total_cmds, ISCSI_TOTAL_CMDS_MIN); 283562306a36Sopenharmony_ci return -EINVAL; 283662306a36Sopenharmony_ci } 283762306a36Sopenharmony_ci 283862306a36Sopenharmony_ci if (total_cmds > ISCSI_TOTAL_CMDS_MAX) { 283962306a36Sopenharmony_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", 284062306a36Sopenharmony_ci requested_cmds_max, ISCSI_TOTAL_CMDS_MAX, 284162306a36Sopenharmony_ci ISCSI_TOTAL_CMDS_MAX); 284262306a36Sopenharmony_ci total_cmds = ISCSI_TOTAL_CMDS_MAX; 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci if (!is_power_of_2(total_cmds)) { 284662306a36Sopenharmony_ci total_cmds = rounddown_pow_of_two(total_cmds); 284762306a36Sopenharmony_ci if (total_cmds < ISCSI_TOTAL_CMDS_MIN) { 284862306a36Sopenharmony_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); 284962306a36Sopenharmony_ci return -EINVAL; 285062306a36Sopenharmony_ci } 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci printk(KERN_INFO "iscsi: invalid max cmds %d. Must be a power of 2. Rounding max cmds down to %d.\n", 285362306a36Sopenharmony_ci requested_cmds_max, total_cmds); 285462306a36Sopenharmony_ci } 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_ci scsi_cmds = total_cmds - ISCSI_MGMT_CMDS_MAX; 285762306a36Sopenharmony_ci if (shost->can_queue && scsi_cmds > shost->can_queue) { 285862306a36Sopenharmony_ci total_cmds = shost->can_queue; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci printk(KERN_INFO "iscsi: requested max cmds %u is higher than driver limit. Using driver limit %u\n", 286162306a36Sopenharmony_ci requested_cmds_max, shost->can_queue); 286262306a36Sopenharmony_ci goto check; 286362306a36Sopenharmony_ci } 286462306a36Sopenharmony_ci 286562306a36Sopenharmony_ci return scsi_cmds; 286662306a36Sopenharmony_ci} 286762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_get_max_scsi_cmds); 286862306a36Sopenharmony_ci 286962306a36Sopenharmony_ci/** 287062306a36Sopenharmony_ci * iscsi_host_add - add host to system 287162306a36Sopenharmony_ci * @shost: scsi host 287262306a36Sopenharmony_ci * @pdev: parent device 287362306a36Sopenharmony_ci * 287462306a36Sopenharmony_ci * This should be called by partial offload and software iscsi drivers 287562306a36Sopenharmony_ci * to add a host to the system. 287662306a36Sopenharmony_ci */ 287762306a36Sopenharmony_ciint iscsi_host_add(struct Scsi_Host *shost, struct device *pdev) 287862306a36Sopenharmony_ci{ 287962306a36Sopenharmony_ci if (!shost->can_queue) 288062306a36Sopenharmony_ci shost->can_queue = ISCSI_DEF_XMIT_CMDS_MAX; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci if (!shost->cmd_per_lun) 288362306a36Sopenharmony_ci shost->cmd_per_lun = ISCSI_DEF_CMD_PER_LUN; 288462306a36Sopenharmony_ci 288562306a36Sopenharmony_ci return scsi_add_host(shost, pdev); 288662306a36Sopenharmony_ci} 288762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_add); 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci/** 289062306a36Sopenharmony_ci * iscsi_host_alloc - allocate a host and driver data 289162306a36Sopenharmony_ci * @sht: scsi host template 289262306a36Sopenharmony_ci * @dd_data_size: driver host data size 289362306a36Sopenharmony_ci * @xmit_can_sleep: bool indicating if LLD will queue IO from a work queue 289462306a36Sopenharmony_ci * 289562306a36Sopenharmony_ci * This should be called by partial offload and software iscsi drivers. 289662306a36Sopenharmony_ci * To access the driver specific memory use the iscsi_host_priv() macro. 289762306a36Sopenharmony_ci */ 289862306a36Sopenharmony_cistruct Scsi_Host *iscsi_host_alloc(const struct scsi_host_template *sht, 289962306a36Sopenharmony_ci int dd_data_size, bool xmit_can_sleep) 290062306a36Sopenharmony_ci{ 290162306a36Sopenharmony_ci struct Scsi_Host *shost; 290262306a36Sopenharmony_ci struct iscsi_host *ihost; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci shost = scsi_host_alloc(sht, sizeof(struct iscsi_host) + dd_data_size); 290562306a36Sopenharmony_ci if (!shost) 290662306a36Sopenharmony_ci return NULL; 290762306a36Sopenharmony_ci ihost = shost_priv(shost); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci if (xmit_can_sleep) { 291062306a36Sopenharmony_ci ihost->workq = alloc_workqueue("iscsi_q_%d", 291162306a36Sopenharmony_ci WQ_SYSFS | __WQ_LEGACY | WQ_MEM_RECLAIM | WQ_UNBOUND, 291262306a36Sopenharmony_ci 1, shost->host_no); 291362306a36Sopenharmony_ci if (!ihost->workq) 291462306a36Sopenharmony_ci goto free_host; 291562306a36Sopenharmony_ci } 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_ci spin_lock_init(&ihost->lock); 291862306a36Sopenharmony_ci ihost->state = ISCSI_HOST_SETUP; 291962306a36Sopenharmony_ci ihost->num_sessions = 0; 292062306a36Sopenharmony_ci init_waitqueue_head(&ihost->session_removal_wq); 292162306a36Sopenharmony_ci return shost; 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_cifree_host: 292462306a36Sopenharmony_ci scsi_host_put(shost); 292562306a36Sopenharmony_ci return NULL; 292662306a36Sopenharmony_ci} 292762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_alloc); 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_cistatic void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session) 293062306a36Sopenharmony_ci{ 293162306a36Sopenharmony_ci iscsi_session_failure(cls_session->dd_data, ISCSI_ERR_INVALID_HOST); 293262306a36Sopenharmony_ci} 293362306a36Sopenharmony_ci 293462306a36Sopenharmony_ci/** 293562306a36Sopenharmony_ci * iscsi_host_remove - remove host and sessions 293662306a36Sopenharmony_ci * @shost: scsi host 293762306a36Sopenharmony_ci * @is_shutdown: true if called from a driver shutdown callout 293862306a36Sopenharmony_ci * 293962306a36Sopenharmony_ci * If there are any sessions left, this will initiate the removal and wait 294062306a36Sopenharmony_ci * for the completion. 294162306a36Sopenharmony_ci */ 294262306a36Sopenharmony_civoid iscsi_host_remove(struct Scsi_Host *shost, bool is_shutdown) 294362306a36Sopenharmony_ci{ 294462306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 294562306a36Sopenharmony_ci unsigned long flags; 294662306a36Sopenharmony_ci 294762306a36Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 294862306a36Sopenharmony_ci ihost->state = ISCSI_HOST_REMOVED; 294962306a36Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_ci if (!is_shutdown) 295262306a36Sopenharmony_ci iscsi_host_for_each_session(shost, iscsi_notify_host_removed); 295362306a36Sopenharmony_ci else 295462306a36Sopenharmony_ci iscsi_host_for_each_session(shost, iscsi_force_destroy_session); 295562306a36Sopenharmony_ci 295662306a36Sopenharmony_ci wait_event_interruptible(ihost->session_removal_wq, 295762306a36Sopenharmony_ci ihost->num_sessions == 0); 295862306a36Sopenharmony_ci if (signal_pending(current)) 295962306a36Sopenharmony_ci flush_signals(current); 296062306a36Sopenharmony_ci 296162306a36Sopenharmony_ci scsi_remove_host(shost); 296262306a36Sopenharmony_ci} 296362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_remove); 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_civoid iscsi_host_free(struct Scsi_Host *shost) 296662306a36Sopenharmony_ci{ 296762306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 296862306a36Sopenharmony_ci 296962306a36Sopenharmony_ci if (ihost->workq) 297062306a36Sopenharmony_ci destroy_workqueue(ihost->workq); 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci kfree(ihost->netdev); 297362306a36Sopenharmony_ci kfree(ihost->hwaddress); 297462306a36Sopenharmony_ci kfree(ihost->initiatorname); 297562306a36Sopenharmony_ci scsi_host_put(shost); 297662306a36Sopenharmony_ci} 297762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_free); 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_cistatic void iscsi_host_dec_session_cnt(struct Scsi_Host *shost) 298062306a36Sopenharmony_ci{ 298162306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 298262306a36Sopenharmony_ci unsigned long flags; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci shost = scsi_host_get(shost); 298562306a36Sopenharmony_ci if (!shost) { 298662306a36Sopenharmony_ci printk(KERN_ERR "Invalid state. Cannot notify host removal " 298762306a36Sopenharmony_ci "of session teardown event because host already " 298862306a36Sopenharmony_ci "removed.\n"); 298962306a36Sopenharmony_ci return; 299062306a36Sopenharmony_ci } 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 299362306a36Sopenharmony_ci ihost->num_sessions--; 299462306a36Sopenharmony_ci if (ihost->num_sessions == 0) 299562306a36Sopenharmony_ci wake_up(&ihost->session_removal_wq); 299662306a36Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 299762306a36Sopenharmony_ci scsi_host_put(shost); 299862306a36Sopenharmony_ci} 299962306a36Sopenharmony_ci 300062306a36Sopenharmony_ci/** 300162306a36Sopenharmony_ci * iscsi_session_setup - create iscsi cls session and host and session 300262306a36Sopenharmony_ci * @iscsit: iscsi transport template 300362306a36Sopenharmony_ci * @shost: scsi host 300462306a36Sopenharmony_ci * @cmds_max: session can queue 300562306a36Sopenharmony_ci * @dd_size: private driver data size, added to session allocation size 300662306a36Sopenharmony_ci * @cmd_task_size: LLD task private data size 300762306a36Sopenharmony_ci * @initial_cmdsn: initial CmdSN 300862306a36Sopenharmony_ci * @id: target ID to add to this session 300962306a36Sopenharmony_ci * 301062306a36Sopenharmony_ci * This can be used by software iscsi_transports that allocate 301162306a36Sopenharmony_ci * a session per scsi host. 301262306a36Sopenharmony_ci * 301362306a36Sopenharmony_ci * Callers should set cmds_max to the largest total numer (mgmt + scsi) of 301462306a36Sopenharmony_ci * tasks they support. The iscsi layer reserves ISCSI_MGMT_CMDS_MAX tasks 301562306a36Sopenharmony_ci * for nop handling and login/logout requests. 301662306a36Sopenharmony_ci */ 301762306a36Sopenharmony_cistruct iscsi_cls_session * 301862306a36Sopenharmony_ciiscsi_session_setup(struct iscsi_transport *iscsit, struct Scsi_Host *shost, 301962306a36Sopenharmony_ci uint16_t cmds_max, int dd_size, int cmd_task_size, 302062306a36Sopenharmony_ci uint32_t initial_cmdsn, unsigned int id) 302162306a36Sopenharmony_ci{ 302262306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 302362306a36Sopenharmony_ci struct iscsi_session *session; 302462306a36Sopenharmony_ci struct iscsi_cls_session *cls_session; 302562306a36Sopenharmony_ci int cmd_i, scsi_cmds; 302662306a36Sopenharmony_ci unsigned long flags; 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci spin_lock_irqsave(&ihost->lock, flags); 302962306a36Sopenharmony_ci if (ihost->state == ISCSI_HOST_REMOVED) { 303062306a36Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 303162306a36Sopenharmony_ci return NULL; 303262306a36Sopenharmony_ci } 303362306a36Sopenharmony_ci ihost->num_sessions++; 303462306a36Sopenharmony_ci spin_unlock_irqrestore(&ihost->lock, flags); 303562306a36Sopenharmony_ci 303662306a36Sopenharmony_ci scsi_cmds = iscsi_host_get_max_scsi_cmds(shost, cmds_max); 303762306a36Sopenharmony_ci if (scsi_cmds < 0) 303862306a36Sopenharmony_ci goto dec_session_count; 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_ci cls_session = iscsi_alloc_session(shost, iscsit, 304162306a36Sopenharmony_ci sizeof(struct iscsi_session) + 304262306a36Sopenharmony_ci dd_size); 304362306a36Sopenharmony_ci if (!cls_session) 304462306a36Sopenharmony_ci goto dec_session_count; 304562306a36Sopenharmony_ci session = cls_session->dd_data; 304662306a36Sopenharmony_ci session->cls_session = cls_session; 304762306a36Sopenharmony_ci session->host = shost; 304862306a36Sopenharmony_ci session->state = ISCSI_STATE_FREE; 304962306a36Sopenharmony_ci session->fast_abort = 1; 305062306a36Sopenharmony_ci session->tgt_reset_timeout = 30; 305162306a36Sopenharmony_ci session->lu_reset_timeout = 15; 305262306a36Sopenharmony_ci session->abort_timeout = 10; 305362306a36Sopenharmony_ci session->scsi_cmds_max = scsi_cmds; 305462306a36Sopenharmony_ci session->cmds_max = scsi_cmds + ISCSI_MGMT_CMDS_MAX; 305562306a36Sopenharmony_ci session->queued_cmdsn = session->cmdsn = initial_cmdsn; 305662306a36Sopenharmony_ci session->exp_cmdsn = initial_cmdsn + 1; 305762306a36Sopenharmony_ci session->max_cmdsn = initial_cmdsn + 1; 305862306a36Sopenharmony_ci session->max_r2t = 1; 305962306a36Sopenharmony_ci session->tt = iscsit; 306062306a36Sopenharmony_ci session->dd_data = cls_session->dd_data + sizeof(*session); 306162306a36Sopenharmony_ci 306262306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 306362306a36Sopenharmony_ci timer_setup(&session->tmf_timer, iscsi_tmf_timedout, 0); 306462306a36Sopenharmony_ci mutex_init(&session->eh_mutex); 306562306a36Sopenharmony_ci init_waitqueue_head(&session->ehwait); 306662306a36Sopenharmony_ci 306762306a36Sopenharmony_ci spin_lock_init(&session->frwd_lock); 306862306a36Sopenharmony_ci spin_lock_init(&session->back_lock); 306962306a36Sopenharmony_ci 307062306a36Sopenharmony_ci /* initialize SCSI PDU commands pool */ 307162306a36Sopenharmony_ci if (iscsi_pool_init(&session->cmdpool, session->cmds_max, 307262306a36Sopenharmony_ci (void***)&session->cmds, 307362306a36Sopenharmony_ci cmd_task_size + sizeof(struct iscsi_task))) 307462306a36Sopenharmony_ci goto cmdpool_alloc_fail; 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci /* pre-format cmds pool with ITT */ 307762306a36Sopenharmony_ci for (cmd_i = 0; cmd_i < session->cmds_max; cmd_i++) { 307862306a36Sopenharmony_ci struct iscsi_task *task = session->cmds[cmd_i]; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci if (cmd_task_size) 308162306a36Sopenharmony_ci task->dd_data = &task[1]; 308262306a36Sopenharmony_ci task->itt = cmd_i; 308362306a36Sopenharmony_ci task->state = ISCSI_TASK_FREE; 308462306a36Sopenharmony_ci INIT_LIST_HEAD(&task->running); 308562306a36Sopenharmony_ci } 308662306a36Sopenharmony_ci 308762306a36Sopenharmony_ci if (!try_module_get(iscsit->owner)) 308862306a36Sopenharmony_ci goto module_get_fail; 308962306a36Sopenharmony_ci 309062306a36Sopenharmony_ci if (iscsi_add_session(cls_session, id)) 309162306a36Sopenharmony_ci goto cls_session_fail; 309262306a36Sopenharmony_ci 309362306a36Sopenharmony_ci return cls_session; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_cicls_session_fail: 309662306a36Sopenharmony_ci module_put(iscsit->owner); 309762306a36Sopenharmony_cimodule_get_fail: 309862306a36Sopenharmony_ci iscsi_pool_free(&session->cmdpool); 309962306a36Sopenharmony_cicmdpool_alloc_fail: 310062306a36Sopenharmony_ci iscsi_free_session(cls_session); 310162306a36Sopenharmony_cidec_session_count: 310262306a36Sopenharmony_ci iscsi_host_dec_session_cnt(shost); 310362306a36Sopenharmony_ci return NULL; 310462306a36Sopenharmony_ci} 310562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_setup); 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci/* 310862306a36Sopenharmony_ci * issi_session_remove - Remove session from iSCSI class. 310962306a36Sopenharmony_ci */ 311062306a36Sopenharmony_civoid iscsi_session_remove(struct iscsi_cls_session *cls_session) 311162306a36Sopenharmony_ci{ 311262306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 311362306a36Sopenharmony_ci struct Scsi_Host *shost = session->host; 311462306a36Sopenharmony_ci 311562306a36Sopenharmony_ci iscsi_remove_session(cls_session); 311662306a36Sopenharmony_ci /* 311762306a36Sopenharmony_ci * host removal only has to wait for its children to be removed from 311862306a36Sopenharmony_ci * sysfs, and iscsi_tcp needs to do iscsi_host_remove before freeing 311962306a36Sopenharmony_ci * the session, so drop the session count here. 312062306a36Sopenharmony_ci */ 312162306a36Sopenharmony_ci iscsi_host_dec_session_cnt(shost); 312262306a36Sopenharmony_ci} 312362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_remove); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci/** 312662306a36Sopenharmony_ci * iscsi_session_free - Free iscsi session and it's resources 312762306a36Sopenharmony_ci * @cls_session: iscsi session 312862306a36Sopenharmony_ci */ 312962306a36Sopenharmony_civoid iscsi_session_free(struct iscsi_cls_session *cls_session) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 313262306a36Sopenharmony_ci struct module *owner = cls_session->transport->owner; 313362306a36Sopenharmony_ci 313462306a36Sopenharmony_ci iscsi_pool_free(&session->cmdpool); 313562306a36Sopenharmony_ci kfree(session->password); 313662306a36Sopenharmony_ci kfree(session->password_in); 313762306a36Sopenharmony_ci kfree(session->username); 313862306a36Sopenharmony_ci kfree(session->username_in); 313962306a36Sopenharmony_ci kfree(session->targetname); 314062306a36Sopenharmony_ci kfree(session->targetalias); 314162306a36Sopenharmony_ci kfree(session->initiatorname); 314262306a36Sopenharmony_ci kfree(session->boot_root); 314362306a36Sopenharmony_ci kfree(session->boot_nic); 314462306a36Sopenharmony_ci kfree(session->boot_target); 314562306a36Sopenharmony_ci kfree(session->ifacename); 314662306a36Sopenharmony_ci kfree(session->portal_type); 314762306a36Sopenharmony_ci kfree(session->discovery_parent_type); 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci iscsi_free_session(cls_session); 315062306a36Sopenharmony_ci module_put(owner); 315162306a36Sopenharmony_ci} 315262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_free); 315362306a36Sopenharmony_ci 315462306a36Sopenharmony_ci/** 315562306a36Sopenharmony_ci * iscsi_session_teardown - destroy session and cls_session 315662306a36Sopenharmony_ci * @cls_session: iscsi session 315762306a36Sopenharmony_ci */ 315862306a36Sopenharmony_civoid iscsi_session_teardown(struct iscsi_cls_session *cls_session) 315962306a36Sopenharmony_ci{ 316062306a36Sopenharmony_ci iscsi_session_remove(cls_session); 316162306a36Sopenharmony_ci iscsi_session_free(cls_session); 316262306a36Sopenharmony_ci} 316362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_teardown); 316462306a36Sopenharmony_ci 316562306a36Sopenharmony_ci/** 316662306a36Sopenharmony_ci * iscsi_conn_setup - create iscsi_cls_conn and iscsi_conn 316762306a36Sopenharmony_ci * @cls_session: iscsi_cls_session 316862306a36Sopenharmony_ci * @dd_size: private driver data size 316962306a36Sopenharmony_ci * @conn_idx: cid 317062306a36Sopenharmony_ci */ 317162306a36Sopenharmony_cistruct iscsi_cls_conn * 317262306a36Sopenharmony_ciiscsi_conn_setup(struct iscsi_cls_session *cls_session, int dd_size, 317362306a36Sopenharmony_ci uint32_t conn_idx) 317462306a36Sopenharmony_ci{ 317562306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 317662306a36Sopenharmony_ci struct iscsi_conn *conn; 317762306a36Sopenharmony_ci struct iscsi_cls_conn *cls_conn; 317862306a36Sopenharmony_ci char *data; 317962306a36Sopenharmony_ci int err; 318062306a36Sopenharmony_ci 318162306a36Sopenharmony_ci cls_conn = iscsi_alloc_conn(cls_session, sizeof(*conn) + dd_size, 318262306a36Sopenharmony_ci conn_idx); 318362306a36Sopenharmony_ci if (!cls_conn) 318462306a36Sopenharmony_ci return NULL; 318562306a36Sopenharmony_ci conn = cls_conn->dd_data; 318662306a36Sopenharmony_ci 318762306a36Sopenharmony_ci conn->dd_data = cls_conn->dd_data + sizeof(*conn); 318862306a36Sopenharmony_ci conn->session = session; 318962306a36Sopenharmony_ci conn->cls_conn = cls_conn; 319062306a36Sopenharmony_ci conn->c_stage = ISCSI_CONN_INITIAL_STAGE; 319162306a36Sopenharmony_ci conn->id = conn_idx; 319262306a36Sopenharmony_ci conn->exp_statsn = 0; 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_ci timer_setup(&conn->transport_timer, iscsi_check_transport_timeouts, 0); 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ci INIT_LIST_HEAD(&conn->mgmtqueue); 319762306a36Sopenharmony_ci INIT_LIST_HEAD(&conn->cmdqueue); 319862306a36Sopenharmony_ci INIT_LIST_HEAD(&conn->requeue); 319962306a36Sopenharmony_ci INIT_WORK(&conn->xmitwork, iscsi_xmitworker); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci /* allocate login_task used for the login/text sequences */ 320262306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 320362306a36Sopenharmony_ci if (!kfifo_out(&session->cmdpool.queue, 320462306a36Sopenharmony_ci (void*)&conn->login_task, 320562306a36Sopenharmony_ci sizeof(void*))) { 320662306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 320762306a36Sopenharmony_ci goto login_task_alloc_fail; 320862306a36Sopenharmony_ci } 320962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 321062306a36Sopenharmony_ci 321162306a36Sopenharmony_ci data = (char *) __get_free_pages(GFP_KERNEL, 321262306a36Sopenharmony_ci get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); 321362306a36Sopenharmony_ci if (!data) 321462306a36Sopenharmony_ci goto login_task_data_alloc_fail; 321562306a36Sopenharmony_ci conn->login_task->data = conn->data = data; 321662306a36Sopenharmony_ci 321762306a36Sopenharmony_ci err = iscsi_add_conn(cls_conn); 321862306a36Sopenharmony_ci if (err) 321962306a36Sopenharmony_ci goto login_task_add_dev_fail; 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci return cls_conn; 322262306a36Sopenharmony_ci 322362306a36Sopenharmony_cilogin_task_add_dev_fail: 322462306a36Sopenharmony_ci free_pages((unsigned long) conn->data, 322562306a36Sopenharmony_ci get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_cilogin_task_data_alloc_fail: 322862306a36Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, 322962306a36Sopenharmony_ci sizeof(void*)); 323062306a36Sopenharmony_cilogin_task_alloc_fail: 323162306a36Sopenharmony_ci iscsi_put_conn(cls_conn); 323262306a36Sopenharmony_ci return NULL; 323362306a36Sopenharmony_ci} 323462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_setup); 323562306a36Sopenharmony_ci 323662306a36Sopenharmony_ci/** 323762306a36Sopenharmony_ci * iscsi_conn_teardown - teardown iscsi connection 323862306a36Sopenharmony_ci * @cls_conn: iscsi class connection 323962306a36Sopenharmony_ci * 324062306a36Sopenharmony_ci * TODO: we may need to make this into a two step process 324162306a36Sopenharmony_ci * like scsi-mls remove + put host 324262306a36Sopenharmony_ci */ 324362306a36Sopenharmony_civoid iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) 324462306a36Sopenharmony_ci{ 324562306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 324662306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 324762306a36Sopenharmony_ci 324862306a36Sopenharmony_ci iscsi_remove_conn(cls_conn); 324962306a36Sopenharmony_ci 325062306a36Sopenharmony_ci del_timer_sync(&conn->transport_timer); 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 325362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 325462306a36Sopenharmony_ci conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; 325562306a36Sopenharmony_ci if (session->leadconn == conn) { 325662306a36Sopenharmony_ci /* 325762306a36Sopenharmony_ci * leading connection? then give up on recovery. 325862306a36Sopenharmony_ci */ 325962306a36Sopenharmony_ci session->state = ISCSI_STATE_TERMINATE; 326062306a36Sopenharmony_ci wake_up(&session->ehwait); 326162306a36Sopenharmony_ci } 326262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 326362306a36Sopenharmony_ci 326462306a36Sopenharmony_ci /* flush queued up work because we free the connection below */ 326562306a36Sopenharmony_ci iscsi_suspend_tx(conn); 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 326862306a36Sopenharmony_ci free_pages((unsigned long) conn->data, 326962306a36Sopenharmony_ci get_order(ISCSI_DEF_MAX_RECV_SEG_LEN)); 327062306a36Sopenharmony_ci kfree(conn->persistent_address); 327162306a36Sopenharmony_ci kfree(conn->local_ipaddr); 327262306a36Sopenharmony_ci /* regular RX path uses back_lock */ 327362306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 327462306a36Sopenharmony_ci kfifo_in(&session->cmdpool.queue, (void*)&conn->login_task, 327562306a36Sopenharmony_ci sizeof(void*)); 327662306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 327762306a36Sopenharmony_ci if (session->leadconn == conn) 327862306a36Sopenharmony_ci session->leadconn = NULL; 327962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 328062306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_ci iscsi_put_conn(cls_conn); 328362306a36Sopenharmony_ci} 328462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_teardown); 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ciint iscsi_conn_start(struct iscsi_cls_conn *cls_conn) 328762306a36Sopenharmony_ci{ 328862306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 328962306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 329062306a36Sopenharmony_ci 329162306a36Sopenharmony_ci if (!session) { 329262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, 329362306a36Sopenharmony_ci "can't start unbound connection\n"); 329462306a36Sopenharmony_ci return -EPERM; 329562306a36Sopenharmony_ci } 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci if ((session->imm_data_en || !session->initial_r2t_en) && 329862306a36Sopenharmony_ci session->first_burst > session->max_burst) { 329962306a36Sopenharmony_ci iscsi_conn_printk(KERN_INFO, conn, "invalid burst lengths: " 330062306a36Sopenharmony_ci "first_burst %d max_burst %d\n", 330162306a36Sopenharmony_ci session->first_burst, session->max_burst); 330262306a36Sopenharmony_ci return -EINVAL; 330362306a36Sopenharmony_ci } 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if (conn->ping_timeout && !conn->recv_timeout) { 330662306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "invalid recv timeout of " 330762306a36Sopenharmony_ci "zero. Using 5 seconds\n."); 330862306a36Sopenharmony_ci conn->recv_timeout = 5; 330962306a36Sopenharmony_ci } 331062306a36Sopenharmony_ci 331162306a36Sopenharmony_ci if (conn->recv_timeout && !conn->ping_timeout) { 331262306a36Sopenharmony_ci iscsi_conn_printk(KERN_ERR, conn, "invalid ping timeout of " 331362306a36Sopenharmony_ci "zero. Using 5 seconds.\n"); 331462306a36Sopenharmony_ci conn->ping_timeout = 5; 331562306a36Sopenharmony_ci } 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 331862306a36Sopenharmony_ci conn->c_stage = ISCSI_CONN_STARTED; 331962306a36Sopenharmony_ci session->state = ISCSI_STATE_LOGGED_IN; 332062306a36Sopenharmony_ci session->queued_cmdsn = session->cmdsn; 332162306a36Sopenharmony_ci 332262306a36Sopenharmony_ci conn->last_recv = jiffies; 332362306a36Sopenharmony_ci conn->last_ping = jiffies; 332462306a36Sopenharmony_ci if (conn->recv_timeout && conn->ping_timeout) 332562306a36Sopenharmony_ci mod_timer(&conn->transport_timer, 332662306a36Sopenharmony_ci jiffies + (conn->recv_timeout * HZ)); 332762306a36Sopenharmony_ci 332862306a36Sopenharmony_ci switch(conn->stop_stage) { 332962306a36Sopenharmony_ci case STOP_CONN_RECOVER: 333062306a36Sopenharmony_ci /* 333162306a36Sopenharmony_ci * unblock eh_abort() if it is blocked. re-try all 333262306a36Sopenharmony_ci * commands after successful recovery 333362306a36Sopenharmony_ci */ 333462306a36Sopenharmony_ci conn->stop_stage = 0; 333562306a36Sopenharmony_ci session->tmf_state = TMF_INITIAL; 333662306a36Sopenharmony_ci session->age++; 333762306a36Sopenharmony_ci if (session->age == 16) 333862306a36Sopenharmony_ci session->age = 0; 333962306a36Sopenharmony_ci break; 334062306a36Sopenharmony_ci case STOP_CONN_TERM: 334162306a36Sopenharmony_ci conn->stop_stage = 0; 334262306a36Sopenharmony_ci break; 334362306a36Sopenharmony_ci default: 334462306a36Sopenharmony_ci break; 334562306a36Sopenharmony_ci } 334662306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 334762306a36Sopenharmony_ci 334862306a36Sopenharmony_ci iscsi_unblock_session(session->cls_session); 334962306a36Sopenharmony_ci wake_up(&session->ehwait); 335062306a36Sopenharmony_ci return 0; 335162306a36Sopenharmony_ci} 335262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_start); 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_cistatic void 335562306a36Sopenharmony_cifail_mgmt_tasks(struct iscsi_session *session, struct iscsi_conn *conn) 335662306a36Sopenharmony_ci{ 335762306a36Sopenharmony_ci struct iscsi_task *task; 335862306a36Sopenharmony_ci int i, state; 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci for (i = 0; i < conn->session->cmds_max; i++) { 336162306a36Sopenharmony_ci task = conn->session->cmds[i]; 336262306a36Sopenharmony_ci if (task->sc) 336362306a36Sopenharmony_ci continue; 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci if (task->state == ISCSI_TASK_FREE) 336662306a36Sopenharmony_ci continue; 336762306a36Sopenharmony_ci 336862306a36Sopenharmony_ci ISCSI_DBG_SESSION(conn->session, 336962306a36Sopenharmony_ci "failing mgmt itt 0x%x state %d\n", 337062306a36Sopenharmony_ci task->itt, task->state); 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 337362306a36Sopenharmony_ci if (cleanup_queued_task(task)) { 337462306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 337562306a36Sopenharmony_ci continue; 337662306a36Sopenharmony_ci } 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci state = ISCSI_TASK_ABRT_SESS_RECOV; 337962306a36Sopenharmony_ci if (task->state == ISCSI_TASK_PENDING) 338062306a36Sopenharmony_ci state = ISCSI_TASK_COMPLETED; 338162306a36Sopenharmony_ci iscsi_complete_task(task, state); 338262306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 338362306a36Sopenharmony_ci } 338462306a36Sopenharmony_ci} 338562306a36Sopenharmony_ci 338662306a36Sopenharmony_civoid iscsi_conn_stop(struct iscsi_cls_conn *cls_conn, int flag) 338762306a36Sopenharmony_ci{ 338862306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 338962306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 339062306a36Sopenharmony_ci int old_stop_stage; 339162306a36Sopenharmony_ci 339262306a36Sopenharmony_ci mutex_lock(&session->eh_mutex); 339362306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 339462306a36Sopenharmony_ci if (conn->stop_stage == STOP_CONN_TERM) { 339562306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 339662306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 339762306a36Sopenharmony_ci return; 339862306a36Sopenharmony_ci } 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ci /* 340162306a36Sopenharmony_ci * When this is called for the in_login state, we only want to clean 340262306a36Sopenharmony_ci * up the login task and connection. We do not need to block and set 340362306a36Sopenharmony_ci * the recovery state again 340462306a36Sopenharmony_ci */ 340562306a36Sopenharmony_ci if (flag == STOP_CONN_TERM) 340662306a36Sopenharmony_ci session->state = ISCSI_STATE_TERMINATE; 340762306a36Sopenharmony_ci else if (conn->stop_stage != STOP_CONN_RECOVER) 340862306a36Sopenharmony_ci session->state = ISCSI_STATE_IN_RECOVERY; 340962306a36Sopenharmony_ci 341062306a36Sopenharmony_ci old_stop_stage = conn->stop_stage; 341162306a36Sopenharmony_ci conn->stop_stage = flag; 341262306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 341362306a36Sopenharmony_ci 341462306a36Sopenharmony_ci del_timer_sync(&conn->transport_timer); 341562306a36Sopenharmony_ci iscsi_suspend_tx(conn); 341662306a36Sopenharmony_ci 341762306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 341862306a36Sopenharmony_ci conn->c_stage = ISCSI_CONN_STOPPED; 341962306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci /* 342262306a36Sopenharmony_ci * for connection level recovery we should not calculate 342362306a36Sopenharmony_ci * header digest. conn->hdr_size used for optimization 342462306a36Sopenharmony_ci * in hdr_extract() and will be re-negotiated at 342562306a36Sopenharmony_ci * set_param() time. 342662306a36Sopenharmony_ci */ 342762306a36Sopenharmony_ci if (flag == STOP_CONN_RECOVER) { 342862306a36Sopenharmony_ci conn->hdrdgst_en = 0; 342962306a36Sopenharmony_ci conn->datadgst_en = 0; 343062306a36Sopenharmony_ci if (session->state == ISCSI_STATE_IN_RECOVERY && 343162306a36Sopenharmony_ci old_stop_stage != STOP_CONN_RECOVER) { 343262306a36Sopenharmony_ci ISCSI_DBG_SESSION(session, "blocking session\n"); 343362306a36Sopenharmony_ci iscsi_block_session(session->cls_session); 343462306a36Sopenharmony_ci } 343562306a36Sopenharmony_ci } 343662306a36Sopenharmony_ci 343762306a36Sopenharmony_ci /* 343862306a36Sopenharmony_ci * flush queues. 343962306a36Sopenharmony_ci */ 344062306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 344162306a36Sopenharmony_ci fail_scsi_tasks(conn, -1, DID_TRANSPORT_DISRUPTED); 344262306a36Sopenharmony_ci fail_mgmt_tasks(session, conn); 344362306a36Sopenharmony_ci memset(&session->tmhdr, 0, sizeof(session->tmhdr)); 344462306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 344562306a36Sopenharmony_ci mutex_unlock(&session->eh_mutex); 344662306a36Sopenharmony_ci} 344762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_stop); 344862306a36Sopenharmony_ci 344962306a36Sopenharmony_ciint iscsi_conn_bind(struct iscsi_cls_session *cls_session, 345062306a36Sopenharmony_ci struct iscsi_cls_conn *cls_conn, int is_leading) 345162306a36Sopenharmony_ci{ 345262306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 345362306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci spin_lock_bh(&session->frwd_lock); 345662306a36Sopenharmony_ci if (is_leading) 345762306a36Sopenharmony_ci session->leadconn = conn; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci set_bit(ISCSI_CONN_FLAG_BOUND, &conn->flags); 346062306a36Sopenharmony_ci spin_unlock_bh(&session->frwd_lock); 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci /* 346362306a36Sopenharmony_ci * The target could have reduced it's window size between logins, so 346462306a36Sopenharmony_ci * we have to reset max/exp cmdsn so we can see the new values. 346562306a36Sopenharmony_ci */ 346662306a36Sopenharmony_ci spin_lock_bh(&session->back_lock); 346762306a36Sopenharmony_ci session->max_cmdsn = session->exp_cmdsn = session->cmdsn + 1; 346862306a36Sopenharmony_ci spin_unlock_bh(&session->back_lock); 346962306a36Sopenharmony_ci /* 347062306a36Sopenharmony_ci * Unblock xmitworker(), Login Phase will pass through. 347162306a36Sopenharmony_ci */ 347262306a36Sopenharmony_ci clear_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags); 347362306a36Sopenharmony_ci clear_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); 347462306a36Sopenharmony_ci return 0; 347562306a36Sopenharmony_ci} 347662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_bind); 347762306a36Sopenharmony_ci 347862306a36Sopenharmony_ciint iscsi_switch_str_param(char **param, char *new_val_buf) 347962306a36Sopenharmony_ci{ 348062306a36Sopenharmony_ci char *new_val; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci if (*param) { 348362306a36Sopenharmony_ci if (!strcmp(*param, new_val_buf)) 348462306a36Sopenharmony_ci return 0; 348562306a36Sopenharmony_ci } 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci new_val = kstrdup(new_val_buf, GFP_NOIO); 348862306a36Sopenharmony_ci if (!new_val) 348962306a36Sopenharmony_ci return -ENOMEM; 349062306a36Sopenharmony_ci 349162306a36Sopenharmony_ci kfree(*param); 349262306a36Sopenharmony_ci *param = new_val; 349362306a36Sopenharmony_ci return 0; 349462306a36Sopenharmony_ci} 349562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_switch_str_param); 349662306a36Sopenharmony_ci 349762306a36Sopenharmony_ciint iscsi_set_param(struct iscsi_cls_conn *cls_conn, 349862306a36Sopenharmony_ci enum iscsi_param param, char *buf, int buflen) 349962306a36Sopenharmony_ci{ 350062306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 350162306a36Sopenharmony_ci struct iscsi_session *session = conn->session; 350262306a36Sopenharmony_ci int val; 350362306a36Sopenharmony_ci 350462306a36Sopenharmony_ci switch(param) { 350562306a36Sopenharmony_ci case ISCSI_PARAM_FAST_ABORT: 350662306a36Sopenharmony_ci sscanf(buf, "%d", &session->fast_abort); 350762306a36Sopenharmony_ci break; 350862306a36Sopenharmony_ci case ISCSI_PARAM_ABORT_TMO: 350962306a36Sopenharmony_ci sscanf(buf, "%d", &session->abort_timeout); 351062306a36Sopenharmony_ci break; 351162306a36Sopenharmony_ci case ISCSI_PARAM_LU_RESET_TMO: 351262306a36Sopenharmony_ci sscanf(buf, "%d", &session->lu_reset_timeout); 351362306a36Sopenharmony_ci break; 351462306a36Sopenharmony_ci case ISCSI_PARAM_TGT_RESET_TMO: 351562306a36Sopenharmony_ci sscanf(buf, "%d", &session->tgt_reset_timeout); 351662306a36Sopenharmony_ci break; 351762306a36Sopenharmony_ci case ISCSI_PARAM_PING_TMO: 351862306a36Sopenharmony_ci sscanf(buf, "%d", &conn->ping_timeout); 351962306a36Sopenharmony_ci break; 352062306a36Sopenharmony_ci case ISCSI_PARAM_RECV_TMO: 352162306a36Sopenharmony_ci sscanf(buf, "%d", &conn->recv_timeout); 352262306a36Sopenharmony_ci break; 352362306a36Sopenharmony_ci case ISCSI_PARAM_MAX_RECV_DLENGTH: 352462306a36Sopenharmony_ci sscanf(buf, "%d", &conn->max_recv_dlength); 352562306a36Sopenharmony_ci break; 352662306a36Sopenharmony_ci case ISCSI_PARAM_MAX_XMIT_DLENGTH: 352762306a36Sopenharmony_ci sscanf(buf, "%d", &conn->max_xmit_dlength); 352862306a36Sopenharmony_ci break; 352962306a36Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 353062306a36Sopenharmony_ci sscanf(buf, "%d", &conn->hdrdgst_en); 353162306a36Sopenharmony_ci break; 353262306a36Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 353362306a36Sopenharmony_ci sscanf(buf, "%d", &conn->datadgst_en); 353462306a36Sopenharmony_ci break; 353562306a36Sopenharmony_ci case ISCSI_PARAM_INITIAL_R2T_EN: 353662306a36Sopenharmony_ci sscanf(buf, "%d", &session->initial_r2t_en); 353762306a36Sopenharmony_ci break; 353862306a36Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 353962306a36Sopenharmony_ci sscanf(buf, "%hu", &session->max_r2t); 354062306a36Sopenharmony_ci break; 354162306a36Sopenharmony_ci case ISCSI_PARAM_IMM_DATA_EN: 354262306a36Sopenharmony_ci sscanf(buf, "%d", &session->imm_data_en); 354362306a36Sopenharmony_ci break; 354462306a36Sopenharmony_ci case ISCSI_PARAM_FIRST_BURST: 354562306a36Sopenharmony_ci sscanf(buf, "%d", &session->first_burst); 354662306a36Sopenharmony_ci break; 354762306a36Sopenharmony_ci case ISCSI_PARAM_MAX_BURST: 354862306a36Sopenharmony_ci sscanf(buf, "%d", &session->max_burst); 354962306a36Sopenharmony_ci break; 355062306a36Sopenharmony_ci case ISCSI_PARAM_PDU_INORDER_EN: 355162306a36Sopenharmony_ci sscanf(buf, "%d", &session->pdu_inorder_en); 355262306a36Sopenharmony_ci break; 355362306a36Sopenharmony_ci case ISCSI_PARAM_DATASEQ_INORDER_EN: 355462306a36Sopenharmony_ci sscanf(buf, "%d", &session->dataseq_inorder_en); 355562306a36Sopenharmony_ci break; 355662306a36Sopenharmony_ci case ISCSI_PARAM_ERL: 355762306a36Sopenharmony_ci sscanf(buf, "%d", &session->erl); 355862306a36Sopenharmony_ci break; 355962306a36Sopenharmony_ci case ISCSI_PARAM_EXP_STATSN: 356062306a36Sopenharmony_ci sscanf(buf, "%u", &conn->exp_statsn); 356162306a36Sopenharmony_ci break; 356262306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME: 356362306a36Sopenharmony_ci return iscsi_switch_str_param(&session->username, buf); 356462306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME_IN: 356562306a36Sopenharmony_ci return iscsi_switch_str_param(&session->username_in, buf); 356662306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD: 356762306a36Sopenharmony_ci return iscsi_switch_str_param(&session->password, buf); 356862306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD_IN: 356962306a36Sopenharmony_ci return iscsi_switch_str_param(&session->password_in, buf); 357062306a36Sopenharmony_ci case ISCSI_PARAM_TARGET_NAME: 357162306a36Sopenharmony_ci return iscsi_switch_str_param(&session->targetname, buf); 357262306a36Sopenharmony_ci case ISCSI_PARAM_TARGET_ALIAS: 357362306a36Sopenharmony_ci return iscsi_switch_str_param(&session->targetalias, buf); 357462306a36Sopenharmony_ci case ISCSI_PARAM_TPGT: 357562306a36Sopenharmony_ci sscanf(buf, "%d", &session->tpgt); 357662306a36Sopenharmony_ci break; 357762306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_PORT: 357862306a36Sopenharmony_ci sscanf(buf, "%d", &conn->persistent_port); 357962306a36Sopenharmony_ci break; 358062306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_ADDRESS: 358162306a36Sopenharmony_ci return iscsi_switch_str_param(&conn->persistent_address, buf); 358262306a36Sopenharmony_ci case ISCSI_PARAM_IFACE_NAME: 358362306a36Sopenharmony_ci return iscsi_switch_str_param(&session->ifacename, buf); 358462306a36Sopenharmony_ci case ISCSI_PARAM_INITIATOR_NAME: 358562306a36Sopenharmony_ci return iscsi_switch_str_param(&session->initiatorname, buf); 358662306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_ROOT: 358762306a36Sopenharmony_ci return iscsi_switch_str_param(&session->boot_root, buf); 358862306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_NIC: 358962306a36Sopenharmony_ci return iscsi_switch_str_param(&session->boot_nic, buf); 359062306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_TARGET: 359162306a36Sopenharmony_ci return iscsi_switch_str_param(&session->boot_target, buf); 359262306a36Sopenharmony_ci case ISCSI_PARAM_PORTAL_TYPE: 359362306a36Sopenharmony_ci return iscsi_switch_str_param(&session->portal_type, buf); 359462306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: 359562306a36Sopenharmony_ci return iscsi_switch_str_param(&session->discovery_parent_type, 359662306a36Sopenharmony_ci buf); 359762306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_SESS: 359862306a36Sopenharmony_ci sscanf(buf, "%d", &val); 359962306a36Sopenharmony_ci session->discovery_sess = !!val; 360062306a36Sopenharmony_ci break; 360162306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_IPADDR: 360262306a36Sopenharmony_ci return iscsi_switch_str_param(&conn->local_ipaddr, buf); 360362306a36Sopenharmony_ci default: 360462306a36Sopenharmony_ci return -ENOSYS; 360562306a36Sopenharmony_ci } 360662306a36Sopenharmony_ci 360762306a36Sopenharmony_ci return 0; 360862306a36Sopenharmony_ci} 360962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_set_param); 361062306a36Sopenharmony_ci 361162306a36Sopenharmony_ciint iscsi_session_get_param(struct iscsi_cls_session *cls_session, 361262306a36Sopenharmony_ci enum iscsi_param param, char *buf) 361362306a36Sopenharmony_ci{ 361462306a36Sopenharmony_ci struct iscsi_session *session = cls_session->dd_data; 361562306a36Sopenharmony_ci int len; 361662306a36Sopenharmony_ci 361762306a36Sopenharmony_ci switch(param) { 361862306a36Sopenharmony_ci case ISCSI_PARAM_FAST_ABORT: 361962306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->fast_abort); 362062306a36Sopenharmony_ci break; 362162306a36Sopenharmony_ci case ISCSI_PARAM_ABORT_TMO: 362262306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->abort_timeout); 362362306a36Sopenharmony_ci break; 362462306a36Sopenharmony_ci case ISCSI_PARAM_LU_RESET_TMO: 362562306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->lu_reset_timeout); 362662306a36Sopenharmony_ci break; 362762306a36Sopenharmony_ci case ISCSI_PARAM_TGT_RESET_TMO: 362862306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->tgt_reset_timeout); 362962306a36Sopenharmony_ci break; 363062306a36Sopenharmony_ci case ISCSI_PARAM_INITIAL_R2T_EN: 363162306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->initial_r2t_en); 363262306a36Sopenharmony_ci break; 363362306a36Sopenharmony_ci case ISCSI_PARAM_MAX_R2T: 363462306a36Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", session->max_r2t); 363562306a36Sopenharmony_ci break; 363662306a36Sopenharmony_ci case ISCSI_PARAM_IMM_DATA_EN: 363762306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->imm_data_en); 363862306a36Sopenharmony_ci break; 363962306a36Sopenharmony_ci case ISCSI_PARAM_FIRST_BURST: 364062306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->first_burst); 364162306a36Sopenharmony_ci break; 364262306a36Sopenharmony_ci case ISCSI_PARAM_MAX_BURST: 364362306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->max_burst); 364462306a36Sopenharmony_ci break; 364562306a36Sopenharmony_ci case ISCSI_PARAM_PDU_INORDER_EN: 364662306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->pdu_inorder_en); 364762306a36Sopenharmony_ci break; 364862306a36Sopenharmony_ci case ISCSI_PARAM_DATASEQ_INORDER_EN: 364962306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->dataseq_inorder_en); 365062306a36Sopenharmony_ci break; 365162306a36Sopenharmony_ci case ISCSI_PARAM_DEF_TASKMGMT_TMO: 365262306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->def_taskmgmt_tmo); 365362306a36Sopenharmony_ci break; 365462306a36Sopenharmony_ci case ISCSI_PARAM_ERL: 365562306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->erl); 365662306a36Sopenharmony_ci break; 365762306a36Sopenharmony_ci case ISCSI_PARAM_TARGET_NAME: 365862306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->targetname); 365962306a36Sopenharmony_ci break; 366062306a36Sopenharmony_ci case ISCSI_PARAM_TARGET_ALIAS: 366162306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->targetalias); 366262306a36Sopenharmony_ci break; 366362306a36Sopenharmony_ci case ISCSI_PARAM_TPGT: 366462306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->tpgt); 366562306a36Sopenharmony_ci break; 366662306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME: 366762306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->username); 366862306a36Sopenharmony_ci break; 366962306a36Sopenharmony_ci case ISCSI_PARAM_USERNAME_IN: 367062306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->username_in); 367162306a36Sopenharmony_ci break; 367262306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD: 367362306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->password); 367462306a36Sopenharmony_ci break; 367562306a36Sopenharmony_ci case ISCSI_PARAM_PASSWORD_IN: 367662306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->password_in); 367762306a36Sopenharmony_ci break; 367862306a36Sopenharmony_ci case ISCSI_PARAM_IFACE_NAME: 367962306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->ifacename); 368062306a36Sopenharmony_ci break; 368162306a36Sopenharmony_ci case ISCSI_PARAM_INITIATOR_NAME: 368262306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->initiatorname); 368362306a36Sopenharmony_ci break; 368462306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_ROOT: 368562306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_root); 368662306a36Sopenharmony_ci break; 368762306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_NIC: 368862306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_nic); 368962306a36Sopenharmony_ci break; 369062306a36Sopenharmony_ci case ISCSI_PARAM_BOOT_TARGET: 369162306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->boot_target); 369262306a36Sopenharmony_ci break; 369362306a36Sopenharmony_ci case ISCSI_PARAM_AUTO_SND_TGT_DISABLE: 369462306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->auto_snd_tgt_disable); 369562306a36Sopenharmony_ci break; 369662306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_SESS: 369762306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_sess); 369862306a36Sopenharmony_ci break; 369962306a36Sopenharmony_ci case ISCSI_PARAM_PORTAL_TYPE: 370062306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", session->portal_type); 370162306a36Sopenharmony_ci break; 370262306a36Sopenharmony_ci case ISCSI_PARAM_CHAP_AUTH_EN: 370362306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->chap_auth_en); 370462306a36Sopenharmony_ci break; 370562306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_LOGOUT_EN: 370662306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_logout_en); 370762306a36Sopenharmony_ci break; 370862306a36Sopenharmony_ci case ISCSI_PARAM_BIDI_CHAP_EN: 370962306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->bidi_chap_en); 371062306a36Sopenharmony_ci break; 371162306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_AUTH_OPTIONAL: 371262306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_auth_optional); 371362306a36Sopenharmony_ci break; 371462306a36Sopenharmony_ci case ISCSI_PARAM_DEF_TIME2WAIT: 371562306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->time2wait); 371662306a36Sopenharmony_ci break; 371762306a36Sopenharmony_ci case ISCSI_PARAM_DEF_TIME2RETAIN: 371862306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", session->time2retain); 371962306a36Sopenharmony_ci break; 372062306a36Sopenharmony_ci case ISCSI_PARAM_TSID: 372162306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->tsid); 372262306a36Sopenharmony_ci break; 372362306a36Sopenharmony_ci case ISCSI_PARAM_ISID: 372462306a36Sopenharmony_ci len = sysfs_emit(buf, "%02x%02x%02x%02x%02x%02x\n", 372562306a36Sopenharmony_ci session->isid[0], session->isid[1], 372662306a36Sopenharmony_ci session->isid[2], session->isid[3], 372762306a36Sopenharmony_ci session->isid[4], session->isid[5]); 372862306a36Sopenharmony_ci break; 372962306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_IDX: 373062306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", session->discovery_parent_idx); 373162306a36Sopenharmony_ci break; 373262306a36Sopenharmony_ci case ISCSI_PARAM_DISCOVERY_PARENT_TYPE: 373362306a36Sopenharmony_ci if (session->discovery_parent_type) 373462306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", 373562306a36Sopenharmony_ci session->discovery_parent_type); 373662306a36Sopenharmony_ci else 373762306a36Sopenharmony_ci len = sysfs_emit(buf, "\n"); 373862306a36Sopenharmony_ci break; 373962306a36Sopenharmony_ci default: 374062306a36Sopenharmony_ci return -ENOSYS; 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci return len; 374462306a36Sopenharmony_ci} 374562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_session_get_param); 374662306a36Sopenharmony_ci 374762306a36Sopenharmony_ciint iscsi_conn_get_addr_param(struct sockaddr_storage *addr, 374862306a36Sopenharmony_ci enum iscsi_param param, char *buf) 374962306a36Sopenharmony_ci{ 375062306a36Sopenharmony_ci struct sockaddr_in6 *sin6 = NULL; 375162306a36Sopenharmony_ci struct sockaddr_in *sin = NULL; 375262306a36Sopenharmony_ci int len; 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci switch (addr->ss_family) { 375562306a36Sopenharmony_ci case AF_INET: 375662306a36Sopenharmony_ci sin = (struct sockaddr_in *)addr; 375762306a36Sopenharmony_ci break; 375862306a36Sopenharmony_ci case AF_INET6: 375962306a36Sopenharmony_ci sin6 = (struct sockaddr_in6 *)addr; 376062306a36Sopenharmony_ci break; 376162306a36Sopenharmony_ci default: 376262306a36Sopenharmony_ci return -EINVAL; 376362306a36Sopenharmony_ci } 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci switch (param) { 376662306a36Sopenharmony_ci case ISCSI_PARAM_CONN_ADDRESS: 376762306a36Sopenharmony_ci case ISCSI_HOST_PARAM_IPADDRESS: 376862306a36Sopenharmony_ci if (sin) 376962306a36Sopenharmony_ci len = sysfs_emit(buf, "%pI4\n", &sin->sin_addr.s_addr); 377062306a36Sopenharmony_ci else 377162306a36Sopenharmony_ci len = sysfs_emit(buf, "%pI6\n", &sin6->sin6_addr); 377262306a36Sopenharmony_ci break; 377362306a36Sopenharmony_ci case ISCSI_PARAM_CONN_PORT: 377462306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 377562306a36Sopenharmony_ci if (sin) 377662306a36Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", be16_to_cpu(sin->sin_port)); 377762306a36Sopenharmony_ci else 377862306a36Sopenharmony_ci len = sysfs_emit(buf, "%hu\n", 377962306a36Sopenharmony_ci be16_to_cpu(sin6->sin6_port)); 378062306a36Sopenharmony_ci break; 378162306a36Sopenharmony_ci default: 378262306a36Sopenharmony_ci return -EINVAL; 378362306a36Sopenharmony_ci } 378462306a36Sopenharmony_ci 378562306a36Sopenharmony_ci return len; 378662306a36Sopenharmony_ci} 378762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_get_addr_param); 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_ciint iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, 379062306a36Sopenharmony_ci enum iscsi_param param, char *buf) 379162306a36Sopenharmony_ci{ 379262306a36Sopenharmony_ci struct iscsi_conn *conn = cls_conn->dd_data; 379362306a36Sopenharmony_ci int len; 379462306a36Sopenharmony_ci 379562306a36Sopenharmony_ci switch(param) { 379662306a36Sopenharmony_ci case ISCSI_PARAM_PING_TMO: 379762306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ping_timeout); 379862306a36Sopenharmony_ci break; 379962306a36Sopenharmony_ci case ISCSI_PARAM_RECV_TMO: 380062306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->recv_timeout); 380162306a36Sopenharmony_ci break; 380262306a36Sopenharmony_ci case ISCSI_PARAM_MAX_RECV_DLENGTH: 380362306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_recv_dlength); 380462306a36Sopenharmony_ci break; 380562306a36Sopenharmony_ci case ISCSI_PARAM_MAX_XMIT_DLENGTH: 380662306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_xmit_dlength); 380762306a36Sopenharmony_ci break; 380862306a36Sopenharmony_ci case ISCSI_PARAM_HDRDGST_EN: 380962306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->hdrdgst_en); 381062306a36Sopenharmony_ci break; 381162306a36Sopenharmony_ci case ISCSI_PARAM_DATADGST_EN: 381262306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->datadgst_en); 381362306a36Sopenharmony_ci break; 381462306a36Sopenharmony_ci case ISCSI_PARAM_IFMARKER_EN: 381562306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->ifmarker_en); 381662306a36Sopenharmony_ci break; 381762306a36Sopenharmony_ci case ISCSI_PARAM_OFMARKER_EN: 381862306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->ofmarker_en); 381962306a36Sopenharmony_ci break; 382062306a36Sopenharmony_ci case ISCSI_PARAM_EXP_STATSN: 382162306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->exp_statsn); 382262306a36Sopenharmony_ci break; 382362306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_PORT: 382462306a36Sopenharmony_ci len = sysfs_emit(buf, "%d\n", conn->persistent_port); 382562306a36Sopenharmony_ci break; 382662306a36Sopenharmony_ci case ISCSI_PARAM_PERSISTENT_ADDRESS: 382762306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", conn->persistent_address); 382862306a36Sopenharmony_ci break; 382962306a36Sopenharmony_ci case ISCSI_PARAM_STATSN: 383062306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->statsn); 383162306a36Sopenharmony_ci break; 383262306a36Sopenharmony_ci case ISCSI_PARAM_MAX_SEGMENT_SIZE: 383362306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->max_segment_size); 383462306a36Sopenharmony_ci break; 383562306a36Sopenharmony_ci case ISCSI_PARAM_KEEPALIVE_TMO: 383662306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->keepalive_tmo); 383762306a36Sopenharmony_ci break; 383862306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_PORT: 383962306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->local_port); 384062306a36Sopenharmony_ci break; 384162306a36Sopenharmony_ci case ISCSI_PARAM_TCP_TIMESTAMP_STAT: 384262306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_stat); 384362306a36Sopenharmony_ci break; 384462306a36Sopenharmony_ci case ISCSI_PARAM_TCP_NAGLE_DISABLE: 384562306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_nagle_disable); 384662306a36Sopenharmony_ci break; 384762306a36Sopenharmony_ci case ISCSI_PARAM_TCP_WSF_DISABLE: 384862306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_wsf_disable); 384962306a36Sopenharmony_ci break; 385062306a36Sopenharmony_ci case ISCSI_PARAM_TCP_TIMER_SCALE: 385162306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timer_scale); 385262306a36Sopenharmony_ci break; 385362306a36Sopenharmony_ci case ISCSI_PARAM_TCP_TIMESTAMP_EN: 385462306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_timestamp_en); 385562306a36Sopenharmony_ci break; 385662306a36Sopenharmony_ci case ISCSI_PARAM_IP_FRAGMENT_DISABLE: 385762306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->fragment_disable); 385862306a36Sopenharmony_ci break; 385962306a36Sopenharmony_ci case ISCSI_PARAM_IPV4_TOS: 386062306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv4_tos); 386162306a36Sopenharmony_ci break; 386262306a36Sopenharmony_ci case ISCSI_PARAM_IPV6_TC: 386362306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv6_traffic_class); 386462306a36Sopenharmony_ci break; 386562306a36Sopenharmony_ci case ISCSI_PARAM_IPV6_FLOW_LABEL: 386662306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->ipv6_flow_label); 386762306a36Sopenharmony_ci break; 386862306a36Sopenharmony_ci case ISCSI_PARAM_IS_FW_ASSIGNED_IPV6: 386962306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->is_fw_assigned_ipv6); 387062306a36Sopenharmony_ci break; 387162306a36Sopenharmony_ci case ISCSI_PARAM_TCP_XMIT_WSF: 387262306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_xmit_wsf); 387362306a36Sopenharmony_ci break; 387462306a36Sopenharmony_ci case ISCSI_PARAM_TCP_RECV_WSF: 387562306a36Sopenharmony_ci len = sysfs_emit(buf, "%u\n", conn->tcp_recv_wsf); 387662306a36Sopenharmony_ci break; 387762306a36Sopenharmony_ci case ISCSI_PARAM_LOCAL_IPADDR: 387862306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", conn->local_ipaddr); 387962306a36Sopenharmony_ci break; 388062306a36Sopenharmony_ci default: 388162306a36Sopenharmony_ci return -ENOSYS; 388262306a36Sopenharmony_ci } 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci return len; 388562306a36Sopenharmony_ci} 388662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_conn_get_param); 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ciint iscsi_host_get_param(struct Scsi_Host *shost, enum iscsi_host_param param, 388962306a36Sopenharmony_ci char *buf) 389062306a36Sopenharmony_ci{ 389162306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 389262306a36Sopenharmony_ci int len; 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci switch (param) { 389562306a36Sopenharmony_ci case ISCSI_HOST_PARAM_NETDEV_NAME: 389662306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->netdev); 389762306a36Sopenharmony_ci break; 389862306a36Sopenharmony_ci case ISCSI_HOST_PARAM_HWADDRESS: 389962306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->hwaddress); 390062306a36Sopenharmony_ci break; 390162306a36Sopenharmony_ci case ISCSI_HOST_PARAM_INITIATOR_NAME: 390262306a36Sopenharmony_ci len = sysfs_emit(buf, "%s\n", ihost->initiatorname); 390362306a36Sopenharmony_ci break; 390462306a36Sopenharmony_ci default: 390562306a36Sopenharmony_ci return -ENOSYS; 390662306a36Sopenharmony_ci } 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_ci return len; 390962306a36Sopenharmony_ci} 391062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_get_param); 391162306a36Sopenharmony_ci 391262306a36Sopenharmony_ciint iscsi_host_set_param(struct Scsi_Host *shost, enum iscsi_host_param param, 391362306a36Sopenharmony_ci char *buf, int buflen) 391462306a36Sopenharmony_ci{ 391562306a36Sopenharmony_ci struct iscsi_host *ihost = shost_priv(shost); 391662306a36Sopenharmony_ci 391762306a36Sopenharmony_ci switch (param) { 391862306a36Sopenharmony_ci case ISCSI_HOST_PARAM_NETDEV_NAME: 391962306a36Sopenharmony_ci return iscsi_switch_str_param(&ihost->netdev, buf); 392062306a36Sopenharmony_ci case ISCSI_HOST_PARAM_HWADDRESS: 392162306a36Sopenharmony_ci return iscsi_switch_str_param(&ihost->hwaddress, buf); 392262306a36Sopenharmony_ci case ISCSI_HOST_PARAM_INITIATOR_NAME: 392362306a36Sopenharmony_ci return iscsi_switch_str_param(&ihost->initiatorname, buf); 392462306a36Sopenharmony_ci default: 392562306a36Sopenharmony_ci return -ENOSYS; 392662306a36Sopenharmony_ci } 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_ci return 0; 392962306a36Sopenharmony_ci} 393062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(iscsi_host_set_param); 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ciMODULE_AUTHOR("Mike Christie"); 393362306a36Sopenharmony_ciMODULE_DESCRIPTION("iSCSI library functions"); 393462306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 3935