// SPDX-License-Identifier: GPL-2.0-or-later/* * iSCSI lib functions * * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * Copyright (C) 2004 - 2006 Mike Christie * Copyright (C) 2004 - 2005 Dmitry Yusupov * Copyright (C) 2004 - 2005 Alex Aizman * maintained by open-iscsi@googlegroups.com */#include <linux/types.h>#include <linux/kfifo.h>#include <linux/delay.h>#include <linux/log2.h>#include <linux/slab.h>#include <linux/sched/signal.h>#include <linux/module.h>#include <linux/unaligned.h>#include <net/tcp.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_device.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_tcq.h>#include <scsi/scsi_host.h>#include <scsi/scsi.h>#include <scsi/iscsi_proto.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_transport_iscsi.h>#include <scsi/libiscsi.h>#include <trace/events/iscsi.h> static int iscsi_dbg_lib_conn;module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(debug_libiscsi_conn, "Turn on debugging for connections in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); static int iscsi_dbg_lib_session;module_param_named(debug_libiscsi_session, iscsi_dbg_lib_session, int, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(debug_libiscsi_session, "Turn on debugging for sessions in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); static int iscsi_dbg_lib_eh;module_param_named(debug_libiscsi_eh, iscsi_dbg_lib_eh, int, S_IRUGO | S_IWUSR);MODULE_PARM_DESC(debug_libiscsi_eh, "Turn on debugging for error handling in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); #define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_conn) \ iscsi_conn_printk(KERN_INFO, _conn, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_conn, \ &(_conn)->cls_conn->dev, \ "%s " dbg_fmt, __func__, ##arg);\ } while (0); #define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_session) \ iscsi_session_printk(KERN_INFO, _session, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_session, \ &(_session)->cls_session->dev, \ "%s " dbg_fmt, __func__, ##arg); \ } while (0); #define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_eh) \ iscsi_session_printk(KERN_INFO, _session, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_eh, \ &(_session)->cls_session->dev, \ "%s " dbg_fmt, __func__, ##arg); \ } while (0); #define ISCSI_CMD_COMPL_WAIT 5 inline void iscsi_conn_queue_xmit(struct iscsi_conn *conn){ struct Scsi_Host *shost = conn->session->host; struct iscsi_host *ihost = shost_priv(shost); if (ihost->workq) queue_work(ihost->workq, &conn->xmitwork);}EXPORT_SYMBOL_GPL(iscsi_conn_queue_xmit); inline void iscsi_conn_queue_recv(struct iscsi_conn *conn){ struct Scsi_Host *shost = conn->session->host; struct iscsi_host *ihost = shost_priv(shost); if (ihost->workq && !test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags)) queue_work(ihost->workq, &conn->recvwork);}EXPORT_SYMBOL_GPL(iscsi_conn_queue_recv); static void __iscsi_update_cmdsn(struct iscsi_session *session, uint32_t exp_cmdsn, uint32_t max_cmdsn){ /* * standard specifies this check for when to update expected and * max sequence numbers */ if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1)) return; if (exp_cmdsn != session->exp_cmdsn && !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn)) session->exp_cmdsn = exp_cmdsn; if (max_cmdsn != session->max_cmdsn && !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) session->max_cmdsn = max_cmdsn;} void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr){ __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), be32_to_cpu(hdr->max_cmdsn));}EXPORT_SYMBOL_GPL(iscsi_update_cmdsn); /** * iscsi_prep_data_out_pdu - initialize Data-Out * @task: scsi command task * @r2t: R2T info * @hdr: iscsi data in pdu * * Notes: * Initialize Data-Out within this R2T sequence and finds * proper data_offset within this SCSI command. * * This function is called with connection lock taken. **/void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t, struct iscsi_data *hdr){ struct iscsi_conn *conn = task->conn; unsigned int left = r2t->data_length - r2t->sent; task->hdr_len = sizeof(struct iscsi_data); memset(hdr, 0, sizeof(struct iscsi_data)); hdr->ttt = r2t->ttt; hdr->datasn = cpu_to_be32(r2t->datasn); r2t->datasn++; hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; hdr->lun = task->lun; hdr->itt = task->hdr_itt; hdr->exp_statsn = r2t->exp_statsn; hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent); if (left > conn->max_xmit_dlength) { hton24(hdr->dlength, conn->max_xmit_dlength); r2t->data_count = conn->max_xmit_dlength; hdr->flags = 0; } else { hton24(hdr->dlength, left); r2t->data_count = left; hdr->flags = ISCSI_FLAG_CMD_FINAL; } conn->dataout_pdus_cnt++;}EXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu); static int iscsi_add_hdr(struct iscsi_task *task, unsigned len){ unsigned exp_len = task->hdr_len + len; if (exp_len > task->hdr_max) { WARN_ON(1); return -EINVAL; } WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ task->hdr_len = exp_len; return 0;} /* * make an extended cdb AHS */static int iscsi_prep_ecdb_ahs(struct iscsi_task *task){ struct scsi_cmnd *cmd = task->sc; unsigned rlen, pad_len; unsigned short ahslength; struct iscsi_ecdb_ahdr *ecdb_ahdr; int rc; ecdb_ahdr = iscsi_next_hdr(task); rlen = cmd->cmd_len - ISCSI_CDB_SIZE; BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb)); ahslength = rlen + sizeof(ecdb_ahdr->reserved); pad_len = iscsi_padding(rlen); rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) + sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len); if (rc) return rc; if (pad_len) memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len); ecdb_ahdr->ahslength = cpu_to_be16(ahslength); ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB; ecdb_ahdr->reserved = 0; memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen); ISCSI_DBG_SESSION(task->conn->session, "iscsi_prep_ecdb_ahs: varlen_cdb_len %d " "rlen %d pad_len %d ahs_length %d iscsi_headers_size " "%u\n", cmd->cmd_len, rlen, pad_len, ahslength, task->hdr_len); return 0;} /** * iscsi_check_tmf_restrictions - check if a task is affected by TMF * @task: iscsi task * @opcode: opcode to check for * * During TMF a task has to be checked if it's affected. * All unrelated I/O can be passed through, but I/O to the * affected LUN should be restricted. * If 'fast_abort' is set we won't be sending any I/O to the * affected LUN. * Otherwise the target is waiting for all TTTs to be completed, * so we have to send all outstanding Data-Out PDUs to the target. */static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode){ struct iscsi_session *session = task->conn->session; struct iscsi_tm *tmf = &session->tmhdr; u64 hdr_lun; if (session->tmf_state == TMF_INITIAL) return 0; if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) return 0; switch (ISCSI_TM_FUNC_VALUE(tmf)) { case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: /* * Allow PDUs for unrelated LUNs */ hdr_lun = scsilun_to_int(&tmf->lun); if (hdr_lun != task->sc->device->lun) return 0; fallthrough; case ISCSI_TM_FUNC_TARGET_WARM_RESET: /* * Fail all SCSI cmd PDUs */ if (opcode != ISCSI_OP_SCSI_DATA_OUT) { iscsi_session_printk(KERN_INFO, session, "task [op %x itt 0x%x/0x%x] rejected.\n", opcode, task->itt, task->hdr_itt); return -EACCES; } /* * And also all data-out PDUs in response to R2T * if fast_abort is set. */ if (session->fast_abort) { iscsi_session_printk(KERN_INFO, session, "task [op %x itt 0x%x/0x%x] fast abort.\n", opcode, task->itt, task->hdr_itt); return -EACCES; } break; case ISCSI_TM_FUNC_ABORT_TASK: /* * the caller has already checked if the task * they want to abort was in the pending queue so if * we are here the cmd pdu has gone out already, and * we will only hit this for data-outs */ if (opcode == ISCSI_OP_SCSI_DATA_OUT && task->hdr_itt == tmf->rtt) { ISCSI_DBG_SESSION(session, "Preventing task %x/%x from sending " "data-out due to abort task in " "progress\n", task->itt, task->hdr_itt); return -EACCES; } break; } return 0;} /** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @task: iscsi task * * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set * fields like dlength or final based on how much data it sends */static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task){ struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; struct iscsi_scsi_req *hdr; unsigned hdrlength, cmd_len, transfer_length; itt_t itt; int rc; rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); if (rc) return rc; if (conn->session->tt->alloc_pdu) { rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); if (rc) return rc; } hdr = (struct iscsi_scsi_req *)task->hdr; itt = hdr->itt; memset(hdr, 0, sizeof(*hdr)); if (session->tt->parse_pdu_itt) hdr->itt = task->hdr_itt = itt; else hdr->itt = task->hdr_itt = build_itt(task->itt, task->conn->session->age); task->hdr_len = 0; rc = iscsi_add_hdr(task, sizeof(*hdr)); if (rc) return rc; hdr->opcode = ISCSI_OP_SCSI_CMD; hdr->flags = ISCSI_ATTR_SIMPLE; int_to_scsilun(sc->device->lun, &hdr->lun); task->lun = hdr->lun; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); cmd_len = sc->cmd_len; if (cmd_len < ISCSI_CDB_SIZE) memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len); else if (cmd_len > ISCSI_CDB_SIZE) { rc = iscsi_prep_ecdb_ahs(task); if (rc) return rc; cmd_len = ISCSI_CDB_SIZE; } memcpy(hdr->cdb, sc->cmnd, cmd_len); task->imm_count = 0; if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) task->protected = true; transfer_length = scsi_transfer_length(sc); hdr->data_length = cpu_to_be32(transfer_length); if (sc->sc_data_direction == DMA_TO_DEVICE) { struct iscsi_r2t_info *r2t = &task->unsol_r2t; hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* * Write counters: * * imm_count bytes to be sent right after * SCSI PDU Header * * unsol_count bytes(as Data-Out) to be sent * without R2T ack right after * immediate data * * r2t data_length bytes to be sent via R2T ack's * * pad_count bytes to be sent as zero-padding */ memset(r2t, 0, sizeof(*r2t)); if (session->imm_data_en) { if (transfer_length >= session->first_burst) task->imm_count = min(session->first_burst, conn->max_xmit_dlength); else task->imm_count = min(transfer_length, conn->max_xmit_dlength); hton24(hdr->dlength, task->imm_count); } else zero_data(hdr->dlength); if (!session->initial_r2t_en) { r2t->data_length = min(session->first_burst, transfer_length) - task->imm_count; r2t->data_offset = task->imm_count; r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); r2t->exp_statsn = cpu_to_be32(conn->exp_statsn); } if (!task->unsol_r2t.data_length) /* No unsolicit Data-Out's */ hdr->flags |= ISCSI_FLAG_CMD_FINAL; } else { hdr->flags |= ISCSI_FLAG_CMD_FINAL; zero_data(hdr->dlength); if (sc->sc_data_direction == DMA_FROM_DEVICE) hdr->flags |= ISCSI_FLAG_CMD_READ; } /* calculate size of additional header segments (AHSs) */ hdrlength = task->hdr_len - sizeof(*hdr); WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); hdrlength /= ISCSI_PAD_LEN; WARN_ON(hdrlength >= 256); hdr->hlength = hdrlength & 0xFF; hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); if (session->tt->init_task && session->tt->init_task(task)) return -EIO; task->state = ISCSI_TASK_RUNNING; session->cmdsn++; conn->scsicmd_pdus_cnt++; ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " "itt 0x%x len %d cmdsn %d win %d]\n", sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", conn->id, sc, sc->cmnd[0], task->itt, transfer_length, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); return 0;} /** * iscsi_free_task - free a task * @task: iscsi cmd task * * Must be called with session back_lock. * This function returns the scsi command to scsi-ml or cleans * up mgmt tasks then returns the task to the pool. */static void iscsi_free_task(struct iscsi_task *task){ struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; int oldstate = task->state; ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", task->itt, task->state, task->sc); session->tt->cleanup_task(task); task->state = ISCSI_TASK_FREE; task->sc = NULL; /* * login task is preallocated so do not free */ if (conn->login_task == task) return; kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); if (sc) { /* SCSI eh reuses commands to verify us */ iscsi_cmd(sc)->task = NULL; /* * queue command may call this to free the task, so * it will decide how to return sc to scsi-ml. */ if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) scsi_done(sc); }} bool iscsi_get_task(struct iscsi_task *task){ return refcount_inc_not_zero(&task->refcount);}EXPORT_SYMBOL_GPL(iscsi_get_task); /** * __iscsi_put_task - drop the refcount on a task * @task: iscsi_task to drop the refcount on * * The back_lock must be held when calling in case it frees the task. */void __iscsi_put_task(struct iscsi_task *task){Lines 1–500 of 3937. Load more
// SPDX-License-Identifier: GPL-2.0-or-later /* * iSCSI lib functions * * Copyright (C) 2006 Red Hat, Inc. All rights reserved. * Copyright (C) 2004 - 2006 Mike Christie * Copyright (C) 2004 - 2005 Dmitry Yusupov * Copyright (C) 2004 - 2005 Alex Aizman * maintained by open-iscsi@googlegroups.com */ #include <linux/types.h> #include <linux/kfifo.h> #include <linux/delay.h> #include <linux/log2.h> #include <linux/slab.h> #include <linux/sched/signal.h> #include <linux/module.h> #include <linux/unaligned.h> #include <net/tcp.h> #include <scsi/scsi_cmnd.h> #include <scsi/scsi_device.h> #include <scsi/scsi_eh.h> #include <scsi/scsi_tcq.h> #include <scsi/scsi_host.h> #include <scsi/scsi.h> #include <scsi/iscsi_proto.h> #include <scsi/scsi_transport.h> #include <scsi/scsi_transport_iscsi.h> #include <scsi/libiscsi.h> #include <trace/events/iscsi.h> static int iscsi_dbg_lib_conn; module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug_libiscsi_conn, "Turn on debugging for connections in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); static int iscsi_dbg_lib_session; module_param_named(debug_libiscsi_session, iscsi_dbg_lib_session, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug_libiscsi_session, "Turn on debugging for sessions in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); static int iscsi_dbg_lib_eh; module_param_named(debug_libiscsi_eh, iscsi_dbg_lib_eh, int, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug_libiscsi_eh, "Turn on debugging for error handling in libiscsi module. " "Set to 1 to turn on, and zero to turn off. Default is off."); #define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_conn) \ iscsi_conn_printk(KERN_INFO, _conn, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_conn, \ &(_conn)->cls_conn->dev, \ "%s " dbg_fmt, __func__, ##arg);\ } while (0); #define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_session) \ iscsi_session_printk(KERN_INFO, _session, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_session, \ &(_session)->cls_session->dev, \ "%s " dbg_fmt, __func__, ##arg); \ } while (0); #define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ do { \ if (iscsi_dbg_lib_eh) \ iscsi_session_printk(KERN_INFO, _session, \ "%s " dbg_fmt, \ __func__, ##arg); \ iscsi_dbg_trace(trace_iscsi_dbg_eh, \ &(_session)->cls_session->dev, \ "%s " dbg_fmt, __func__, ##arg); \ } while (0); #define ISCSI_CMD_COMPL_WAIT 5 inline void iscsi_conn_queue_xmit(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; struct iscsi_host *ihost = shost_priv(shost); if (ihost->workq) queue_work(ihost->workq, &conn->xmitwork); } EXPORT_SYMBOL_GPL(iscsi_conn_queue_xmit); inline void iscsi_conn_queue_recv(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; struct iscsi_host *ihost = shost_priv(shost); if (ihost->workq && !test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags)) queue_work(ihost->workq, &conn->recvwork); } EXPORT_SYMBOL_GPL(iscsi_conn_queue_recv); static void __iscsi_update_cmdsn(struct iscsi_session *session, uint32_t exp_cmdsn, uint32_t max_cmdsn) { /* * standard specifies this check for when to update expected and * max sequence numbers */ if (iscsi_sna_lt(max_cmdsn, exp_cmdsn - 1)) return; if (exp_cmdsn != session->exp_cmdsn && !iscsi_sna_lt(exp_cmdsn, session->exp_cmdsn)) session->exp_cmdsn = exp_cmdsn; if (max_cmdsn != session->max_cmdsn && !iscsi_sna_lt(max_cmdsn, session->max_cmdsn)) session->max_cmdsn = max_cmdsn; } void iscsi_update_cmdsn(struct iscsi_session *session, struct iscsi_nopin *hdr) { __iscsi_update_cmdsn(session, be32_to_cpu(hdr->exp_cmdsn), be32_to_cpu(hdr->max_cmdsn)); } EXPORT_SYMBOL_GPL(iscsi_update_cmdsn); /** * iscsi_prep_data_out_pdu - initialize Data-Out * @task: scsi command task * @r2t: R2T info * @hdr: iscsi data in pdu * * Notes: * Initialize Data-Out within this R2T sequence and finds * proper data_offset within this SCSI command. * * This function is called with connection lock taken. **/ void iscsi_prep_data_out_pdu(struct iscsi_task *task, struct iscsi_r2t_info *r2t, struct iscsi_data *hdr) { struct iscsi_conn *conn = task->conn; unsigned int left = r2t->data_length - r2t->sent; task->hdr_len = sizeof(struct iscsi_data); memset(hdr, 0, sizeof(struct iscsi_data)); hdr->ttt = r2t->ttt; hdr->datasn = cpu_to_be32(r2t->datasn); r2t->datasn++; hdr->opcode = ISCSI_OP_SCSI_DATA_OUT; hdr->lun = task->lun; hdr->itt = task->hdr_itt; hdr->exp_statsn = r2t->exp_statsn; hdr->offset = cpu_to_be32(r2t->data_offset + r2t->sent); if (left > conn->max_xmit_dlength) { hton24(hdr->dlength, conn->max_xmit_dlength); r2t->data_count = conn->max_xmit_dlength; hdr->flags = 0; } else { hton24(hdr->dlength, left); r2t->data_count = left; hdr->flags = ISCSI_FLAG_CMD_FINAL; } conn->dataout_pdus_cnt++; } EXPORT_SYMBOL_GPL(iscsi_prep_data_out_pdu); static int iscsi_add_hdr(struct iscsi_task *task, unsigned len) { unsigned exp_len = task->hdr_len + len; if (exp_len > task->hdr_max) { WARN_ON(1); return -EINVAL; } WARN_ON(len & (ISCSI_PAD_LEN - 1)); /* caller must pad the AHS */ task->hdr_len = exp_len; return 0; } /* * make an extended cdb AHS */ static int iscsi_prep_ecdb_ahs(struct iscsi_task *task) { struct scsi_cmnd *cmd = task->sc; unsigned rlen, pad_len; unsigned short ahslength; struct iscsi_ecdb_ahdr *ecdb_ahdr; int rc; ecdb_ahdr = iscsi_next_hdr(task); rlen = cmd->cmd_len - ISCSI_CDB_SIZE; BUG_ON(rlen > sizeof(ecdb_ahdr->ecdb)); ahslength = rlen + sizeof(ecdb_ahdr->reserved); pad_len = iscsi_padding(rlen); rc = iscsi_add_hdr(task, sizeof(ecdb_ahdr->ahslength) + sizeof(ecdb_ahdr->ahstype) + ahslength + pad_len); if (rc) return rc; if (pad_len) memset(&ecdb_ahdr->ecdb[rlen], 0, pad_len); ecdb_ahdr->ahslength = cpu_to_be16(ahslength); ecdb_ahdr->ahstype = ISCSI_AHSTYPE_CDB; ecdb_ahdr->reserved = 0; memcpy(ecdb_ahdr->ecdb, cmd->cmnd + ISCSI_CDB_SIZE, rlen); ISCSI_DBG_SESSION(task->conn->session, "iscsi_prep_ecdb_ahs: varlen_cdb_len %d " "rlen %d pad_len %d ahs_length %d iscsi_headers_size " "%u\n", cmd->cmd_len, rlen, pad_len, ahslength, task->hdr_len); return 0; } /** * iscsi_check_tmf_restrictions - check if a task is affected by TMF * @task: iscsi task * @opcode: opcode to check for * * During TMF a task has to be checked if it's affected. * All unrelated I/O can be passed through, but I/O to the * affected LUN should be restricted. * If 'fast_abort' is set we won't be sending any I/O to the * affected LUN. * Otherwise the target is waiting for all TTTs to be completed, * so we have to send all outstanding Data-Out PDUs to the target. */ static int iscsi_check_tmf_restrictions(struct iscsi_task *task, int opcode) { struct iscsi_session *session = task->conn->session; struct iscsi_tm *tmf = &session->tmhdr; u64 hdr_lun; if (session->tmf_state == TMF_INITIAL) return 0; if ((tmf->opcode & ISCSI_OPCODE_MASK) != ISCSI_OP_SCSI_TMFUNC) return 0; switch (ISCSI_TM_FUNC_VALUE(tmf)) { case ISCSI_TM_FUNC_LOGICAL_UNIT_RESET: /* * Allow PDUs for unrelated LUNs */ hdr_lun = scsilun_to_int(&tmf->lun); if (hdr_lun != task->sc->device->lun) return 0; fallthrough; case ISCSI_TM_FUNC_TARGET_WARM_RESET: /* * Fail all SCSI cmd PDUs */ if (opcode != ISCSI_OP_SCSI_DATA_OUT) { iscsi_session_printk(KERN_INFO, session, "task [op %x itt 0x%x/0x%x] rejected.\n", opcode, task->itt, task->hdr_itt); return -EACCES; } /* * And also all data-out PDUs in response to R2T * if fast_abort is set. */ if (session->fast_abort) { iscsi_session_printk(KERN_INFO, session, "task [op %x itt 0x%x/0x%x] fast abort.\n", opcode, task->itt, task->hdr_itt); return -EACCES; } break; case ISCSI_TM_FUNC_ABORT_TASK: /* * the caller has already checked if the task * they want to abort was in the pending queue so if * we are here the cmd pdu has gone out already, and * we will only hit this for data-outs */ if (opcode == ISCSI_OP_SCSI_DATA_OUT && task->hdr_itt == tmf->rtt) { ISCSI_DBG_SESSION(session, "Preventing task %x/%x from sending " "data-out due to abort task in " "progress\n", task->itt, task->hdr_itt); return -EACCES; } break; } return 0; } /** * iscsi_prep_scsi_cmd_pdu - prep iscsi scsi cmd pdu * @task: iscsi task * * Prep basic iSCSI PDU fields for a scsi cmd pdu. The LLD should set * fields like dlength or final based on how much data it sends */ static int iscsi_prep_scsi_cmd_pdu(struct iscsi_task *task) { struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; struct iscsi_scsi_req *hdr; unsigned hdrlength, cmd_len, transfer_length; itt_t itt; int rc; rc = iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_CMD); if (rc) return rc; if (conn->session->tt->alloc_pdu) { rc = conn->session->tt->alloc_pdu(task, ISCSI_OP_SCSI_CMD); if (rc) return rc; } hdr = (struct iscsi_scsi_req *)task->hdr; itt = hdr->itt; memset(hdr, 0, sizeof(*hdr)); if (session->tt->parse_pdu_itt) hdr->itt = task->hdr_itt = itt; else hdr->itt = task->hdr_itt = build_itt(task->itt, task->conn->session->age); task->hdr_len = 0; rc = iscsi_add_hdr(task, sizeof(*hdr)); if (rc) return rc; hdr->opcode = ISCSI_OP_SCSI_CMD; hdr->flags = ISCSI_ATTR_SIMPLE; int_to_scsilun(sc->device->lun, &hdr->lun); task->lun = hdr->lun; hdr->exp_statsn = cpu_to_be32(conn->exp_statsn); cmd_len = sc->cmd_len; if (cmd_len < ISCSI_CDB_SIZE) memset(&hdr->cdb[cmd_len], 0, ISCSI_CDB_SIZE - cmd_len); else if (cmd_len > ISCSI_CDB_SIZE) { rc = iscsi_prep_ecdb_ahs(task); if (rc) return rc; cmd_len = ISCSI_CDB_SIZE; } memcpy(hdr->cdb, sc->cmnd, cmd_len); task->imm_count = 0; if (scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) task->protected = true; transfer_length = scsi_transfer_length(sc); hdr->data_length = cpu_to_be32(transfer_length); if (sc->sc_data_direction == DMA_TO_DEVICE) { struct iscsi_r2t_info *r2t = &task->unsol_r2t; hdr->flags |= ISCSI_FLAG_CMD_WRITE; /* * Write counters: * * imm_count bytes to be sent right after * SCSI PDU Header * * unsol_count bytes(as Data-Out) to be sent * without R2T ack right after * immediate data * * r2t data_length bytes to be sent via R2T ack's * * pad_count bytes to be sent as zero-padding */ memset(r2t, 0, sizeof(*r2t)); if (session->imm_data_en) { if (transfer_length >= session->first_burst) task->imm_count = min(session->first_burst, conn->max_xmit_dlength); else task->imm_count = min(transfer_length, conn->max_xmit_dlength); hton24(hdr->dlength, task->imm_count); } else zero_data(hdr->dlength); if (!session->initial_r2t_en) { r2t->data_length = min(session->first_burst, transfer_length) - task->imm_count; r2t->data_offset = task->imm_count; r2t->ttt = cpu_to_be32(ISCSI_RESERVED_TAG); r2t->exp_statsn = cpu_to_be32(conn->exp_statsn); } if (!task->unsol_r2t.data_length) /* No unsolicit Data-Out's */ hdr->flags |= ISCSI_FLAG_CMD_FINAL; } else { hdr->flags |= ISCSI_FLAG_CMD_FINAL; zero_data(hdr->dlength); if (sc->sc_data_direction == DMA_FROM_DEVICE) hdr->flags |= ISCSI_FLAG_CMD_READ; } /* calculate size of additional header segments (AHSs) */ hdrlength = task->hdr_len - sizeof(*hdr); WARN_ON(hdrlength & (ISCSI_PAD_LEN-1)); hdrlength /= ISCSI_PAD_LEN; WARN_ON(hdrlength >= 256); hdr->hlength = hdrlength & 0xFF; hdr->cmdsn = task->cmdsn = cpu_to_be32(session->cmdsn); if (session->tt->init_task && session->tt->init_task(task)) return -EIO; task->state = ISCSI_TASK_RUNNING; session->cmdsn++; conn->scsicmd_pdus_cnt++; ISCSI_DBG_SESSION(session, "iscsi prep [%s cid %d sc %p cdb 0x%x " "itt 0x%x len %d cmdsn %d win %d]\n", sc->sc_data_direction == DMA_TO_DEVICE ? "write" : "read", conn->id, sc, sc->cmnd[0], task->itt, transfer_length, session->cmdsn, session->max_cmdsn - session->exp_cmdsn + 1); return 0; } /** * iscsi_free_task - free a task * @task: iscsi cmd task * * Must be called with session back_lock. * This function returns the scsi command to scsi-ml or cleans * up mgmt tasks then returns the task to the pool. */ static void iscsi_free_task(struct iscsi_task *task) { struct iscsi_conn *conn = task->conn; struct iscsi_session *session = conn->session; struct scsi_cmnd *sc = task->sc; int oldstate = task->state; ISCSI_DBG_SESSION(session, "freeing task itt 0x%x state %d sc %p\n", task->itt, task->state, task->sc); session->tt->cleanup_task(task); task->state = ISCSI_TASK_FREE; task->sc = NULL; /* * login task is preallocated so do not free */ if (conn->login_task == task) return; kfifo_in(&session->cmdpool.queue, (void*)&task, sizeof(void*)); if (sc) { /* SCSI eh reuses commands to verify us */ iscsi_cmd(sc)->task = NULL; /* * queue command may call this to free the task, so * it will decide how to return sc to scsi-ml. */ if (oldstate != ISCSI_TASK_REQUEUE_SCSIQ) scsi_done(sc); } } bool iscsi_get_task(struct iscsi_task *task) { return refcount_inc_not_zero(&task->refcount); } EXPORT_SYMBOL_GPL(iscsi_get_task); /** * __iscsi_put_task - drop the refcount on a task * @task: iscsi_task to drop the refcount on * * The back_lock must be held when calling in case it frees the task. */ void __iscsi_put_task(struct iscsi_task *task) {