diff --git a/qla2x00t/qla2x00-target/qla2x00t.c b/qla2x00t/qla2x00-target/qla2x00t.c index 5be2c6cc0..72743b3bc 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.c +++ b/qla2x00t/qla2x00-target/qla2x00t.c @@ -2453,7 +2453,7 @@ static int q2t_pre_xmit_response(struct q2t_cmd *cmd, scsi_qla_host_t *ha = tgt->ha; scsi_qla_host_t *pha = to_qla_parent(ha); uint16_t full_req_cnt; - struct scst_cmd *scst_cmd = cmd->scst_cmd; + struct scst_cmd *scst_cmd = &cmd->scst_cmd; TRACE_ENTRY(); @@ -2751,7 +2751,7 @@ static int q2x_xmit_response(struct scst_cmd *scst_cmd) { int xmit_type = Q2T_XMIT_DATA, res; int is_send_status = scst_cmd_get_is_send_status(scst_cmd); - struct q2t_cmd *cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + struct q2t_cmd *cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); #ifdef CONFIG_SCST_EXTRACHECKS sBUG_ON(!q2t_has_data(cmd) && !is_send_status); @@ -3026,7 +3026,7 @@ static int q2t_rdy_to_xfer(struct scst_cmd *scst_cmd) TRACE(TRACE_SCSI, "qla2x00t: tag=%lld", scst_cmd_get_tag(scst_cmd)); - cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); cmd->bufflen = scst_cmd_get_write_fields(scst_cmd, &cmd->sg, &cmd->sg_cnt); cmd->data_direction = scst_cmd_get_data_direction(scst_cmd); @@ -3100,9 +3100,9 @@ out_unlock: if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) - scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_DIRECT); + scst_tgt_cmd_done(&cmd->scst_cmd, SCST_CONTEXT_DIRECT); else - scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_TASKLET); + scst_tgt_cmd_done(&cmd->scst_cmd, SCST_CONTEXT_TASKLET); /* !! At this point cmd could be already freed !! */ } @@ -3177,9 +3177,9 @@ out_unlock: if (do_tgt_cmd_done) { if (!ha_locked && !in_interrupt()) - scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_DIRECT); + scst_tgt_cmd_done(&cmd->scst_cmd, SCST_CONTEXT_DIRECT); else - scst_tgt_cmd_done(cmd->scst_cmd, SCST_CONTEXT_TASKLET); + scst_tgt_cmd_done(&cmd->scst_cmd, SCST_CONTEXT_TASKLET); /* !! At this point cmd could be already freed !! */ } @@ -3206,8 +3206,7 @@ static void q2t_on_free_cmd(struct scst_cmd *scst_cmd) TRACE(TRACE_SCSI, "qla2x00t: Freeing command %p, tag %lld", scst_cmd, scst_cmd_get_tag(scst_cmd)); - cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); - scst_cmd_set_tgt_priv(scst_cmd, NULL); + cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); q2t_free_cmd(cmd); @@ -3236,8 +3235,7 @@ static int q2t_prepare_srr_ctio(scsi_qla_host_t *ha, struct q2t_cmd *cmd, goto out; } - if (cmd->scst_cmd != NULL) - scst_update_hw_pending_start(cmd->scst_cmd); + scst_update_hw_pending_start(&cmd->scst_cmd); sc = kzalloc(sizeof(*sc), GFP_ATOMIC); if (sc != NULL) { @@ -3410,7 +3408,7 @@ static struct q2t_cmd *q2t_ctio_to_cmd(scsi_qla_host_t *ha, uint32_t handle, goto out; } - cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); TRACE_DBG("Found q2t_cmd %p (tag %d)", cmd, tag); } @@ -3455,7 +3453,7 @@ static void q2t_do_ctio_completion(scsi_qla_host_t *ha, uint32_t handle, goto out; } - scst_cmd = cmd->scst_cmd; + scst_cmd = &cmd->scst_cmd; if (cmd->sg_mapped) q2t_unmap_sg(ha, cmd); @@ -3565,7 +3563,7 @@ static void q2x_ctio_completion(scsi_qla_host_t *ha, uint32_t handle) /* pha->hardware_lock supposed to be held on entry */ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) { - int res = 0; + int res; struct q2t_sess *sess = cmd->sess; uint16_t lun; atio_entry_t *atio = &cmd->atio.atio2x; @@ -3576,19 +3574,14 @@ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) /* make it be in network byte order */ lun = swab16(le16_to_cpu(atio->lun)); - cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, (uint8_t *)&lun, - sizeof(lun), atio->cdb, Q2T_MAX_CDB_LEN, - SCST_ATOMIC); - - if (cmd->scst_cmd == NULL) { - PRINT_ERROR("%s", "qla2x00t: scst_rx_cmd() failed"); - res = -EFAULT; + res = scst_rx_cmd_prealloced(&cmd->scst_cmd, sess->scst_sess, + (uint8_t *)&lun, sizeof(lun), atio->cdb, + Q2T_MAX_CDB_LEN, SCST_ATOMIC); + if (res != 0) goto out; - } cmd->tag = atio->rx_id; - scst_cmd_set_tag(cmd->scst_cmd, cmd->tag); - scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd); + scst_cmd_set_tag(&cmd->scst_cmd, cmd->tag); if ((atio->execution_codes & (ATIO_EXEC_READ | ATIO_EXEC_WRITE)) == (ATIO_EXEC_READ | ATIO_EXEC_WRITE)) @@ -3599,29 +3592,29 @@ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) dir = SCST_DATA_WRITE; else dir = SCST_DATA_NONE; - scst_cmd_set_expected(cmd->scst_cmd, dir, + scst_cmd_set_expected(&cmd->scst_cmd, dir, le32_to_cpu(atio->data_length)); switch (atio->task_codes) { case ATIO_SIMPLE_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_SIMPLE); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_SIMPLE); break; case ATIO_HEAD_OF_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); break; case ATIO_ORDERED_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); break; case ATIO_ACA_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ACA); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ACA); break; case ATIO_UNTAGGED: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_UNTAGGED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_UNTAGGED); break; default: PRINT_WARNING("qla2x00t: unknown task code %x, use " "ORDERED instead", atio->task_codes); - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); break; } @@ -3633,8 +3626,8 @@ static int q2x_do_send_cmd_to_scst(struct q2t_cmd *cmd) TRACE_DBG("Context %x", context); TRACE(TRACE_SCSI, "qla2x00t: START Command (tag %d, queue_type %d)", - cmd->tag, scst_cmd_get_queue_type(cmd->scst_cmd)); - scst_cmd_init_done(cmd->scst_cmd, context); + cmd->tag, scst_cmd_get_queue_type(&cmd->scst_cmd)); + scst_cmd_init_done(&cmd->scst_cmd, context); out: TRACE_EXIT_RES(res); @@ -3644,7 +3637,7 @@ out: /* pha->hardware_lock supposed to be held on entry */ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) { - int res = 0; + int res; struct q2t_sess *sess = cmd->sess; atio7_entry_t *atio = &cmd->atio.atio7; scst_data_direction dir; @@ -3652,20 +3645,15 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) TRACE_ENTRY(); - cmd->scst_cmd = scst_rx_cmd(sess->scst_sess, + res = scst_rx_cmd_prealloced(&cmd->scst_cmd, sess->scst_sess, (uint8_t *)&atio->fcp_cmnd.lun, sizeof(atio->fcp_cmnd.lun), atio->fcp_cmnd.cdb, sizeof(atio->fcp_cmnd.cdb) + atio->fcp_cmnd.add_cdb_len, SCST_ATOMIC); - - if (cmd->scst_cmd == NULL) { - PRINT_ERROR("%s", "qla2x00t: scst_rx_cmd() failed"); - res = -EFAULT; + if (res != 0) goto out; - } cmd->tag = atio->exchange_addr; - scst_cmd_set_tag(cmd->scst_cmd, cmd->tag); - scst_cmd_set_tgt_priv(cmd->scst_cmd, cmd); + scst_cmd_set_tag(&cmd->scst_cmd, cmd->tag); if (atio->fcp_cmnd.rddata && atio->fcp_cmnd.wrdata) dir = SCST_DATA_BIDI; @@ -3675,30 +3663,30 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) dir = SCST_DATA_WRITE; else dir = SCST_DATA_NONE; - scst_cmd_set_expected(cmd->scst_cmd, dir, + scst_cmd_set_expected(&cmd->scst_cmd, dir, get_unaligned_be32( &atio->fcp_cmnd.add_cdb[atio->fcp_cmnd.add_cdb_len])); switch (atio->fcp_cmnd.task_attr) { case ATIO_SIMPLE_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_SIMPLE); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_SIMPLE); break; case ATIO_HEAD_OF_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_HEAD_OF_QUEUE); break; case ATIO_ORDERED_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); break; case ATIO_ACA_QUEUE: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ACA); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ACA); break; case ATIO_UNTAGGED: - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_UNTAGGED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_UNTAGGED); break; default: PRINT_WARNING("qla2x00t: unknown task code %x, use " "ORDERED instead", atio->fcp_cmnd.task_attr); - scst_cmd_set_queue_type(cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); + scst_cmd_set_queue_type(&cmd->scst_cmd, SCST_CMD_QUEUE_ORDERED); break; } @@ -3710,8 +3698,8 @@ static int q24_do_send_cmd_to_scst(struct q2t_cmd *cmd) TRACE_DBG("Context %x", context); TRACE(TRACE_SCSI, "qla2x00t: START Command %p (tag %d, queue type %x)", - cmd, cmd->tag, scst_cmd_get_queue_type(cmd->scst_cmd)); - scst_cmd_init_done(cmd->scst_cmd, context); + cmd, cmd->tag, scst_cmd_get_queue_type(&cmd->scst_cmd)); + scst_cmd_init_done(&cmd->scst_cmd, context); out: TRACE_EXIT_RES(res); @@ -4293,9 +4281,9 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, __q24_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: - cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(&cmd->scst_cmd); if (q2t_has_data(cmd) && - (scst_cmd_get_data_direction(cmd->scst_cmd) & SCST_DATA_READ)) { + (scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_READ)) { uint32_t offset; int xmit_type; offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); @@ -4310,16 +4298,16 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " "without them (tag %d, SCSI status %d, dir %d)," " reject", ha->instance, cmd->tag, - scst_cmd_get_status(cmd->scst_cmd), - scst_cmd_get_data_direction(cmd->scst_cmd)); + scst_cmd_get_status(&cmd->scst_cmd), + scst_cmd_get_data_direction(&cmd->scst_cmd)); goto out_reject; } break; case SRR_IU_DATA_OUT: - cmd->bufflen = scst_cmd_get_write_fields(cmd->scst_cmd, + cmd->bufflen = scst_cmd_get_write_fields(&cmd->scst_cmd, &cmd->sg, &cmd->sg_cnt); if (q2t_has_data(cmd) && - (scst_cmd_get_data_direction(cmd->scst_cmd) & SCST_DATA_WRITE)) { + (scst_cmd_get_data_direction(&cmd->scst_cmd) & SCST_DATA_WRITE)) { uint32_t offset; int xmit_type; offset = le32_to_cpu(imm->imm.notify_entry24.srr_rel_offs); @@ -4335,8 +4323,8 @@ static void q24_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " "without them (tag %d, SCSI status %d, dir %d)," " reject", ha->instance, cmd->tag, - scst_cmd_get_status(cmd->scst_cmd), - scst_cmd_get_data_direction(cmd->scst_cmd)); + scst_cmd_get_status(&cmd->scst_cmd), + scst_cmd_get_data_direction(&cmd->scst_cmd)); goto out_reject; } break; @@ -4357,7 +4345,7 @@ out_reject: NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); if (cmd->state == Q2T_STATE_NEED_DATA) { cmd->state = Q2T_STATE_DATA_IN; - scst_rx_data(cmd->scst_cmd, SCST_RX_STATUS_ERROR, + scst_rx_data(&cmd->scst_cmd, SCST_RX_STATUS_ERROR, SCST_CONTEXT_THREAD); } else q24_send_term_exchange(ha, cmd, &cmd->atio.atio7, 1); @@ -4386,7 +4374,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, __q2x_xmit_response(cmd, Q2T_XMIT_STATUS); break; case SRR_IU_DATA_IN: - cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(cmd->scst_cmd); + cmd->bufflen = scst_cmd_get_adjusted_resp_data_len(&cmd->scst_cmd); if (q2t_has_data(cmd)) { uint32_t offset; int xmit_type; @@ -4402,12 +4390,12 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, PRINT_ERROR("qla2x00t(%ld): SRR for in data for cmd " "without them (tag %d, SCSI status %d), " "reject", ha->instance, cmd->tag, - scst_cmd_get_status(cmd->scst_cmd)); + scst_cmd_get_status(&cmd->scst_cmd)); goto out_reject; } break; case SRR_IU_DATA_OUT: - cmd->bufflen = scst_cmd_get_write_fields(cmd->scst_cmd, + cmd->bufflen = scst_cmd_get_write_fields(&cmd->scst_cmd, &cmd->sg, &cmd->sg_cnt); if (q2t_has_data(cmd)) { uint32_t offset; @@ -4425,7 +4413,7 @@ static void q2x_handle_srr(scsi_qla_host_t *ha, struct srr_ctio *sctio, PRINT_ERROR("qla2x00t(%ld): SRR for out data for cmd " "without them (tag %d, SCSI status %d), " "reject", ha->instance, cmd->tag, - scst_cmd_get_status(cmd->scst_cmd)); + scst_cmd_get_status(&cmd->scst_cmd)); goto out_reject; } break; @@ -4446,7 +4434,7 @@ out_reject: NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); if (cmd->state == Q2T_STATE_NEED_DATA) { cmd->state = Q2T_STATE_DATA_IN; - scst_rx_data(cmd->scst_cmd, SCST_RX_STATUS_ERROR, + scst_rx_data(&cmd->scst_cmd, SCST_RX_STATUS_ERROR, SCST_CONTEXT_THREAD); } else q2x_send_term_exchange(ha, cmd, &cmd->atio.atio2x, 1); @@ -4529,17 +4517,17 @@ restart: cmd = sctio->cmd; /* Restore the originals, except bufflen */ - cmd->offset = scst_cmd_get_ppl_offset(cmd->scst_cmd); + cmd->offset = scst_cmd_get_ppl_offset(&cmd->scst_cmd); if (cmd->free_sg) { kfree(cmd->sg); cmd->free_sg = 0; } - cmd->sg = scst_cmd_get_sg(cmd->scst_cmd); - cmd->sg_cnt = scst_cmd_get_sg_cnt(cmd->scst_cmd); + cmd->sg = scst_cmd_get_sg(&cmd->scst_cmd); + cmd->sg_cnt = scst_cmd_get_sg_cnt(&cmd->scst_cmd); TRACE_MGMT_DBG("SRR cmd %p (scst_cmd %p, tag %d, op %x), " - "sg_cnt=%d, offset=%d", cmd, cmd->scst_cmd, - cmd->tag, cmd->scst_cmd->cdb[0], cmd->sg_cnt, + "sg_cnt=%d, offset=%d", cmd, &cmd->scst_cmd, + cmd->tag, cmd->scst_cmd.cdb[0], cmd->sg_cnt, cmd->offset); if (IS_FWI2_CAPABLE(ha)) @@ -5685,7 +5673,7 @@ static void q2t_cleanup_hw_pending_cmd(scsi_qla_host_t *ha, struct q2t_cmd *cmd) static void q2t_on_hw_pending_cmd_timeout(struct scst_cmd *scst_cmd) { - struct q2t_cmd *cmd = (struct q2t_cmd *)scst_cmd_get_tgt_priv(scst_cmd); + struct q2t_cmd *cmd = container_of(scst_cmd, struct q2t_cmd, scst_cmd); struct q2t_tgt *tgt = cmd->tgt; scsi_qla_host_t *ha = tgt->ha; scsi_qla_host_t *pha = to_qla_parent(ha); diff --git a/qla2x00t/qla2x00-target/qla2x00t.h b/qla2x00t/qla2x00-target/qla2x00t.h index 5280b948b..8e0df1e8d 100644 --- a/qla2x00t/qla2x00-target/qla2x00t.h +++ b/qla2x00t/qla2x00-target/qla2x00t.h @@ -196,7 +196,6 @@ struct q2t_sess { struct q2t_cmd { struct q2t_sess *sess; int state; - struct scst_cmd *scst_cmd; unsigned int conf_compl_supported:1;/* to save extra sess dereferences */ unsigned int sg_mapped:1; @@ -220,6 +219,8 @@ struct q2t_cmd { atio7_entry_t atio7; atio_entry_t atio2x; } __packed atio; + + struct scst_cmd scst_cmd; }; struct q2t_sess_work_param { diff --git a/scst/include/scst.h b/scst/include/scst.h index 580f8900d..5e7b57054 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -753,6 +753,13 @@ struct scst_tgt_template { * Necessary, because for aborted commands xmit_response() could not * be called. Could be called on IRQ context. * + * This callback is called when the last reference to cmd is dropped, + * which can be much later after scst_tgt_cmd_done() called by the + * target driver, so it is not recommended that the target driver + * clean hardware or connection related cmd resources in this callback. + * It is recommended to clean them before calling scst_tgt_cmd_done() + * instead. + * * OPTIONAL */ void (*on_free_cmd) (struct scst_cmd *cmd); @@ -1984,6 +1991,9 @@ struct scst_cmd { */ unsigned int finished:1; + /* Set if cmd was pre-alloced by target driver */ + unsigned int pre_alloced:1; + /**************************************************************/ /* cmd's async flags */ @@ -2867,7 +2877,10 @@ bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name); struct scst_cmd *scst_rx_cmd(struct scst_session *sess, const uint8_t *lun, int lun_len, const uint8_t *cdb, - unsigned int cdb_len, int atomic); + unsigned int cdb_len, bool atomic); +int scst_rx_cmd_prealloced(struct scst_cmd *cmd, struct scst_session *sess, + const uint8_t *lun, int lun_len, const uint8_t *cdb, + unsigned int cdb_len, bool atomic); void scst_cmd_init_done(struct scst_cmd *cmd, enum scst_exec_context pref_context); diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index f36e53670..662ec60bc 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -5273,18 +5273,22 @@ out_err: } EXPORT_SYMBOL(scst_cmd_set_ext_cdb); -struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, +int scst_pre_init_cmd(struct scst_cmd *cmd, const uint8_t *cdb, unsigned int cdb_len, gfp_t gfp_mask) { - struct scst_cmd *cmd; + int res; TRACE_ENTRY(); - cmd = kmem_cache_zalloc(scst_cmd_cachep, gfp_mask); - if (cmd == NULL) { - TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of scst_cmd failed"); - goto out; +#ifdef CONFIG_SCST_EXTRACHECKS + /* cmd supposed to be zeroed */ + { + int i; + uint8_t *b = (uint8_t *)cmd; + for (i = 0; i < sizeof(*cmd); i++) + EXTRACHECKS_BUG_ON(b[i] != 0); } +#endif cmd->state = SCST_CMD_STATE_INIT_WAIT; cmd->start_time = jiffies; @@ -5310,26 +5314,54 @@ struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, if (unlikely(cdb_len == 0)) { PRINT_ERROR("%s", "Wrong CDB len 0, finishing cmd"); - goto out_free; + res = -EINVAL; + goto out; } else if (cdb_len <= SCST_MAX_CDB_SIZE) { /* Duplicate memcpy to save a branch on the most common path */ memcpy(cmd->cdb, cdb, cdb_len); } else { if (unlikely(cdb_len > SCST_MAX_LONG_CDB_SIZE)) { PRINT_ERROR("Too big CDB (%d), finishing cmd", cdb_len); - goto out_free; + res = -EINVAL; + goto out; } cmd->cdb = kmalloc(cdb_len, gfp_mask); if (unlikely(cmd->cdb == NULL)) { PRINT_ERROR("Unable to alloc extended CDB (size %d)", cdb_len); - goto out_free; + res = -ENOMEM; + goto out; } memcpy(cmd->cdb, cdb, cdb_len); } cmd->cdb_len = cdb_len; + res = 0; + +out: + TRACE_EXIT_RES(res); + return res; +} + +struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, + unsigned int cdb_len, gfp_t gfp_mask) +{ + struct scst_cmd *cmd; + int rc; + + TRACE_ENTRY(); + + cmd = kmem_cache_zalloc(scst_cmd_cachep, gfp_mask); + if (cmd == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of scst_cmd failed"); + goto out; + } + + rc = scst_pre_init_cmd(cmd, cdb, cdb_len, gfp_mask); + if (unlikely(rc != 0)) + goto out_free; + out: TRACE_EXIT(); return cmd; @@ -5342,6 +5374,8 @@ out_free: static void scst_destroy_cmd(struct scst_cmd *cmd) { + bool pre_alloced = cmd->pre_alloced; + TRACE_ENTRY(); TRACE_DBG("Destroying cmd %p", cmd); @@ -5354,7 +5388,20 @@ static void scst_destroy_cmd(struct scst_cmd *cmd) if (likely(cmd->tgt_dev != NULL)) scst_put(cmd->cpu_cmd_counter); - kmem_cache_free(scst_cmd_cachep, cmd); + EXTRACHECKS_BUG_ON(cmd->pre_alloced && cmd->internal); + + if ((cmd->tgtt->on_free_cmd != NULL) && likely(!cmd->internal)) { + TRACE_DBG("Calling target's on_free_cmd(%p)", cmd); + scst_set_cur_start(cmd); + cmd->tgtt->on_free_cmd(cmd); + scst_set_tgt_on_free_time(cmd); + TRACE_DBG("%s", "Target's on_free_cmd() returned"); + } + + /* At this point cmd can be already freed! */ + + if (!pre_alloced) + kmem_cache_free(scst_cmd_cachep, cmd); TRACE_EXIT(); return; @@ -5383,14 +5430,6 @@ void scst_free_cmd(struct scst_cmd *cmd) if (!cmd->tgt_i_data_buf_alloced) scst_check_restore_sg_buff(cmd); - if ((cmd->tgtt->on_free_cmd != NULL) && likely(!cmd->internal)) { - TRACE_DBG("Calling target's on_free_cmd(%p)", cmd); - scst_set_cur_start(cmd); - cmd->tgtt->on_free_cmd(cmd); - scst_set_tgt_on_free_time(cmd); - TRACE_DBG("%s", "Target's on_free_cmd() returned"); - } - if (likely(cmd->dev != NULL)) { struct scst_dev_type *devt = cmd->devt; if (devt->on_free_cmd != NULL) { diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index 378df8233..3aa9eeed2 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -666,6 +666,8 @@ static inline void scst_sess_put(struct scst_session *sess) struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, unsigned int cdb_len, gfp_t gfp_mask); +int scst_pre_init_cmd(struct scst_cmd *cmd, const uint8_t *cdb, + unsigned int cdb_len, gfp_t gfp_mask); void scst_free_cmd(struct scst_cmd *cmd); static inline void __scst_cmd_get(struct scst_cmd *cmd) diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 4ea213c0b..c2a3da00c 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -179,6 +179,78 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd) return; } +static void __scst_rx_cmd(struct scst_cmd *cmd, struct scst_session *sess, + const uint8_t *lun, int lun_len, gfp_t gfp_mask) +{ + TRACE_ENTRY(); + + cmd->sess = sess; + scst_sess_get(sess); + + cmd->tgt = sess->tgt; + cmd->tgtt = sess->tgt->tgtt; + + cmd->lun = scst_unpack_lun(lun, lun_len); + if (unlikely(cmd->lun == NO_SUCH_LUN)) + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_lun_not_supported)); + + TRACE_DBG("cmd %p, sess %p", cmd, sess); + + TRACE_EXIT(); + return; +} + +/** + * scst_rx_cmd_prealloced() - notify SCST that new command received + * @cmd: new cmd to initialized + * @sess: SCST session + * @lun: LUN for the command + * @lun_len: length of the LUN in bytes + * @cdb: CDB of the command + * @cdb_len: length of the CDB in bytes + * @atomic: true, if current context is atomic + * + * Description: + * Initializes new prealloced SCST command. Returns 0 on success or + * negative error code otherwise. + * + * Must not be called in parallel with scst_unregister_session() for the + * same session. + * + * Cmd supposed to be zeroed! + */ +int scst_rx_cmd_prealloced(struct scst_cmd *cmd, struct scst_session *sess, + const uint8_t *lun, int lun_len, const uint8_t *cdb, + unsigned int cdb_len, bool atomic) +{ + int res; + gfp_t gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; + + TRACE_ENTRY(); + +#ifdef CONFIG_SCST_EXTRACHECKS + if (unlikely(sess->shut_phase != SCST_SESS_SPH_READY)) { + PRINT_CRIT_ERROR("%s", + "New cmd while shutting down the session"); + sBUG(); + } +#endif + + res = scst_pre_init_cmd(cmd, cdb, cdb_len, gfp_mask); + if (unlikely(res != 0)) + goto out; + + __scst_rx_cmd(cmd, sess, lun, lun_len, gfp_mask); + + cmd->pre_alloced = 1; + +out: + TRACE_EXIT_RES(res); + return res; +} +EXPORT_SYMBOL(scst_rx_cmd_prealloced); + /** * scst_rx_cmd() - create new command * @sess: SCST session @@ -197,9 +269,10 @@ static void scst_check_unblock_dev(struct scst_cmd *cmd) */ struct scst_cmd *scst_rx_cmd(struct scst_session *sess, const uint8_t *lun, int lun_len, const uint8_t *cdb, - unsigned int cdb_len, int atomic) + unsigned int cdb_len, bool atomic) { struct scst_cmd *cmd; + gfp_t gfp_mask = atomic ? GFP_ATOMIC : GFP_KERNEL; TRACE_ENTRY(); @@ -211,22 +284,15 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess, } #endif - cmd = scst_alloc_cmd(cdb, cdb_len, atomic ? GFP_ATOMIC : GFP_KERNEL); - if (unlikely(cmd == NULL)) + cmd = scst_alloc_cmd(cdb, cdb_len, gfp_mask); + if (cmd == NULL) { + TRACE(TRACE_OUT_OF_MEM, "%s", "Allocation of scst_cmd failed"); goto out; + } - cmd->sess = sess; - scst_sess_get(sess); + __scst_rx_cmd(cmd, sess, lun, lun_len, gfp_mask); - cmd->tgt = sess->tgt; - cmd->tgtt = sess->tgt->tgtt; - - cmd->lun = scst_unpack_lun(lun, lun_len); - if (unlikely(cmd->lun == NO_SUCH_LUN)) - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_lun_not_supported)); - - TRACE_DBG("cmd %p, sess %p", cmd, sess); + cmd->pre_alloced = 0; out: TRACE_EXIT();