static void tcm_loop_submission_work(struct work_struct *work) { struct tcm_loop_cmd *tl_cmd = container_of(work, struct tcm_loop_cmd, work); struct se_cmd *se_cmd = &tl_cmd->tl_se_cmd; struct scsi_cmnd *sc = tl_cmd->sc; struct tcm_loop_nexus *tl_nexus; struct tcm_loop_hba *tl_hba; struct tcm_loop_tpg *tl_tpg; struct scatterlist *sgl_bidi = NULL; u32 sgl_bidi_count = 0; int ret; tl_hba = *(struct tcm_loop_hba **)shost_priv(sc->device->host); tl_tpg = &tl_hba->tl_hba_tpgs[sc->device->id]; /* * Ensure that this tl_tpg reference from the incoming sc->device->id * has already been configured via tcm_loop_make_naa_tpg(). */ if (!tl_tpg->tl_hba) { set_host_byte(sc, DID_NO_CONNECT); goto out_done; } tl_nexus = tl_hba->tl_nexus; if (!tl_nexus) { scmd_printk(KERN_ERR, sc, "TCM_Loop I_T Nexus" " does not exist\n"); set_host_byte(sc, DID_ERROR); goto out_done; } transport_init_se_cmd(se_cmd, tl_tpg->tl_se_tpg.se_tpg_tfo, tl_nexus->se_sess, scsi_bufflen(sc), sc->sc_data_direction, tcm_loop_sam_attr(sc), &tl_cmd->tl_sense_buf[0]); if (scsi_bidi_cmnd(sc)) { struct scsi_data_buffer *sdb = scsi_in(sc); sgl_bidi = sdb->table.sgl; sgl_bidi_count = sdb->table.nents; se_cmd->se_cmd_flags |= SCF_BIDI; } if (transport_lookup_cmd_lun(se_cmd, tl_cmd->sc->device->lun) < 0) { kmem_cache_free(tcm_loop_cmd_cache, tl_cmd); set_host_byte(sc, DID_NO_CONNECT); goto out_done; } /* * Because some userspace code via scsi-generic do not memset their * associated read buffers, go ahead and do that here for type * SCF_SCSI_CONTROL_SG_IO_CDB. Also note that this is currently * guaranteed to be a single SGL for SCF_SCSI_CONTROL_SG_IO_CDB * by target core in target_setup_cmd_from_cdb() -> * transport_generic_cmd_sequencer(). */ if (se_cmd->se_cmd_flags & SCF_SCSI_CONTROL_SG_IO_CDB && se_cmd->data_direction == DMA_FROM_DEVICE) { struct scatterlist *sg = scsi_sglist(sc); unsigned char *buf = kmap(sg_page(sg)) + sg->offset; if (buf != NULL) { memset(buf, 0, sg->length); kunmap(sg_page(sg)); } } ret = target_setup_cmd_from_cdb(se_cmd, sc->cmnd); if (ret == -ENOMEM) { transport_send_check_condition_and_sense(se_cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); transport_generic_free_cmd(se_cmd, 0); return; } else if (ret < 0) { if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) tcm_loop_queue_status(se_cmd); else transport_send_check_condition_and_sense(se_cmd, se_cmd->scsi_sense_reason, 0); transport_generic_free_cmd(se_cmd, 0); return; } ret = transport_generic_map_mem_to_cmd(se_cmd, scsi_sglist(sc), scsi_sg_count(sc), sgl_bidi, sgl_bidi_count); if (ret) { transport_send_check_condition_and_sense(se_cmd, se_cmd->scsi_sense_reason, 0); transport_generic_free_cmd(se_cmd, 0); return; } transport_handle_cdb_direct(se_cmd); return; out_done: sc->scsi_done(sc); return; }
/* * Handle Task Management Request. */ static void ft_send_tm(struct ft_cmd *cmd) { struct se_tmr_req *tmr; struct fcp_cmnd *fcp; struct ft_sess *sess; u8 tm_func; fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp)); switch (fcp->fc_tm_flags) { case FCP_TMF_LUN_RESET: tm_func = TMR_LUN_RESET; break; case FCP_TMF_TGT_RESET: tm_func = TMR_TARGET_WARM_RESET; break; case FCP_TMF_CLR_TASK_SET: tm_func = TMR_CLEAR_TASK_SET; break; case FCP_TMF_ABT_TASK_SET: tm_func = TMR_ABORT_TASK_SET; break; case FCP_TMF_CLR_ACA: tm_func = TMR_CLEAR_ACA; break; default: /* * FCP4r01 indicates having a combination of * tm_flags set is invalid. */ FT_TM_DBG("invalid FCP tm_flags %x\n", fcp->fc_tm_flags); ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID); return; } FT_TM_DBG("alloc tm cmd fn %d\n", tm_func); tmr = core_tmr_alloc_req(&cmd->se_cmd, cmd, tm_func); if (!tmr) { FT_TM_DBG("alloc failed\n"); ft_send_resp_code(cmd, FCP_TMF_FAILED); return; } cmd->se_cmd.se_tmr_req = tmr; switch (fcp->fc_tm_flags) { case FCP_TMF_LUN_RESET: cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun); if (transport_get_lun_for_tmr(&cmd->se_cmd, cmd->lun) < 0) { /* * Make sure to clean up newly allocated TMR request * since "unable to handle TMR request because failed * to get to LUN" */ FT_TM_DBG("Failed to get LUN for TMR func %d, " "se_cmd %p, unpacked_lun %d\n", tm_func, &cmd->se_cmd, cmd->lun); ft_dump_cmd(cmd, __func__); sess = cmd->sess; transport_send_check_condition_and_sense(&cmd->se_cmd, cmd->se_cmd.scsi_sense_reason, 0); transport_generic_free_cmd(&cmd->se_cmd, 0, 1, 0); ft_sess_put(sess); return; } break; case FCP_TMF_TGT_RESET: case FCP_TMF_CLR_TASK_SET: case FCP_TMF_ABT_TASK_SET: case FCP_TMF_CLR_ACA: break; default: return; } transport_generic_handle_tmr(&cmd->se_cmd); }
/* * Send new command to target. */ static void ft_send_cmd(struct ft_cmd *cmd) { struct fc_frame_header *fh = fc_frame_header_get(cmd->req_frame); struct se_cmd *se_cmd; struct fcp_cmnd *fcp; int data_dir; u32 data_len; int task_attr; int ret; fcp = fc_frame_payload_get(cmd->req_frame, sizeof(*fcp)); if (!fcp) goto err; if (fcp->fc_flags & FCP_CFL_LEN_MASK) goto err; /* not handling longer CDBs yet */ if (fcp->fc_tm_flags) { task_attr = FCP_PTA_SIMPLE; data_dir = DMA_NONE; data_len = 0; } else { switch (fcp->fc_flags & (FCP_CFL_RDDATA | FCP_CFL_WRDATA)) { case 0: data_dir = DMA_NONE; break; case FCP_CFL_RDDATA: data_dir = DMA_FROM_DEVICE; break; case FCP_CFL_WRDATA: data_dir = DMA_TO_DEVICE; break; case FCP_CFL_WRDATA | FCP_CFL_RDDATA: goto err; /* TBD not supported by tcm_fc yet */ } /* * Locate the SAM Task Attr from fc_pri_ta */ switch (fcp->fc_pri_ta & FCP_PTA_MASK) { case FCP_PTA_HEADQ: task_attr = MSG_HEAD_TAG; break; case FCP_PTA_ORDERED: task_attr = MSG_ORDERED_TAG; break; case FCP_PTA_ACA: task_attr = MSG_ACA_TAG; break; case FCP_PTA_SIMPLE: /* Fallthrough */ default: task_attr = MSG_SIMPLE_TAG; } task_attr = fcp->fc_pri_ta & FCP_PTA_MASK; data_len = ntohl(fcp->fc_dl); cmd->cdb = fcp->fc_cdb; } se_cmd = &cmd->se_cmd; /* * Initialize struct se_cmd descriptor from target_core_mod * infrastructure */ transport_init_se_cmd(se_cmd, &ft_configfs->tf_ops, cmd->sess->se_sess, data_len, data_dir, task_attr, &cmd->ft_sense_buffer[0]); /* * Check for FCP task management flags */ if (fcp->fc_tm_flags) { ft_send_tm(cmd); return; } fc_seq_exch(cmd->seq)->lp->tt.seq_set_resp(cmd->seq, ft_recv_seq, cmd); cmd->lun = scsilun_to_int((struct scsi_lun *)fcp->fc_lun); ret = transport_get_lun_for_cmd(&cmd->se_cmd, NULL, cmd->lun); if (ret < 0) { ft_dump_cmd(cmd, __func__); transport_send_check_condition_and_sense(&cmd->se_cmd, cmd->se_cmd.scsi_sense_reason, 0); return; } ret = transport_generic_allocate_tasks(se_cmd, cmd->cdb); FT_IO_DBG("r_ctl %x alloc task ret %d\n", fh->fh_r_ctl, ret); ft_dump_cmd(cmd, __func__); if (ret == -1) { transport_send_check_condition_and_sense(se_cmd, TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0); transport_generic_free_cmd(se_cmd, 0, 1, 0); return; } if (ret == -2) { if (se_cmd->se_cmd_flags & SCF_SCSI_RESERVATION_CONFLICT) ft_queue_status(se_cmd); else transport_send_check_condition_and_sense(se_cmd, se_cmd->scsi_sense_reason, 0); transport_generic_free_cmd(se_cmd, 0, 1, 0); return; } transport_generic_handle_cdb(se_cmd); return; err: ft_send_resp_code(cmd, FCP_CMND_FIELDS_INVALID); return; }
static int iscsit_dataout_check_unsolicited_sequence( struct iscsi_cmd *cmd, unsigned char *buf) { u32 first_burst_len; struct iscsi_conn *conn = cmd->conn; struct iscsi_data *hdr = (struct iscsi_data *) buf; u32 payload_length = ntoh24(hdr->dlength); if ((be32_to_cpu(hdr->offset) < cmd->seq_start_offset) || ((be32_to_cpu(hdr->offset) + payload_length) > cmd->seq_end_offset)) { pr_err("Command ITT: 0x%08x with Offset: %u," " Length: %u outside of Unsolicited Sequence %u:%u while" " DataSequenceInOrder=Yes.\n", cmd->init_task_tag, be32_to_cpu(hdr->offset), payload_length, cmd->seq_start_offset, cmd->seq_end_offset); return DATAOUT_CANNOT_RECOVER; } first_burst_len = (cmd->first_burst_len + payload_length); if (first_burst_len > conn->sess->sess_ops->FirstBurstLength) { pr_err("Total %u bytes exceeds FirstBurstLength: %u" " for this Unsolicited DataOut Burst.\n", first_burst_len, conn->sess->sess_ops->FirstBurstLength); transport_send_check_condition_and_sense(&cmd->se_cmd, TCM_INCORRECT_AMOUNT_OF_DATA, 0); return DATAOUT_CANNOT_RECOVER; } /* * Perform various MaxBurstLength and ISCSI_FLAG_CMD_FINAL sanity * checks for the current Unsolicited DataOUT Sequence. */ if (hdr->flags & ISCSI_FLAG_CMD_FINAL) { /* * Ignore ISCSI_FLAG_CMD_FINAL checks while DataPDUInOrder=No, end of * sequence checks are handled in * iscsit_dataout_datapduinorder_no_fbit(). */ if (!conn->sess->sess_ops->DataPDUInOrder) goto out; if ((first_burst_len != cmd->se_cmd.data_length) && (first_burst_len != conn->sess->sess_ops->FirstBurstLength)) { pr_err("Unsolicited non-immediate data" " received %u does not equal FirstBurstLength: %u, and" " does not equal ExpXferLen %u.\n", first_burst_len, conn->sess->sess_ops->FirstBurstLength, cmd->se_cmd.data_length); transport_send_check_condition_and_sense(&cmd->se_cmd, TCM_INCORRECT_AMOUNT_OF_DATA, 0); return DATAOUT_CANNOT_RECOVER; } } else { if (first_burst_len == conn->sess->sess_ops->FirstBurstLength) { pr_err("Command ITT: 0x%08x reached" " FirstBurstLength: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" " error.\n", cmd->init_task_tag, conn->sess->sess_ops->FirstBurstLength); return DATAOUT_CANNOT_RECOVER; } if (first_burst_len == cmd->se_cmd.data_length) { pr_err("Command ITT: 0x%08x reached" " ExpXferLen: %u, but ISCSI_FLAG_CMD_FINAL is not set. protocol" " error.\n", cmd->init_task_tag, cmd->se_cmd.data_length); return DATAOUT_CANNOT_RECOVER; } } out: return DATAOUT_NORMAL; }