diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index acdacabed..6d4e81f20 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -2027,7 +2027,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req) struct iscsi_cdb_ahdr *eca = (struct iscsi_cdb_ahdr *)ahdr; scst_cmd_set_ext_cdb(scst_cmd, eca->cdb, - be16_to_cpu(ahdr->ahslength) - 1); + be16_to_cpu(ahdr->ahslength) - 1, + GFP_KERNEL); break; } s = 3 + be16_to_cpu(ahdr->ahslength); diff --git a/scst/include/scst.h b/scst/include/scst.h index 825b9a09a..2547a8d20 100644 --- a/scst/include/scst.h +++ b/scst/include/scst.h @@ -2844,7 +2844,7 @@ static inline unsigned int scst_cmd_get_cdb_len(struct scst_cmd *cmd) } void scst_cmd_set_ext_cdb(struct scst_cmd *cmd, - uint8_t *ext_cdb, unsigned int ext_cdb_len); + uint8_t *ext_cdb, unsigned int ext_cdb_len, gfp_t gfp_mask); /* Returns cmd's session */ static inline struct scst_session *scst_cmd_get_session(struct scst_cmd *cmd) diff --git a/scst/include/scst_const.h b/scst/include/scst_const.h index ac8bc7f68..fd6b6344f 100644 --- a/scst/include/scst_const.h +++ b/scst/include/scst_const.h @@ -37,6 +37,9 @@ /* Max size of CDB */ #define SCST_MAX_CDB_SIZE 16 +/* Max size of long CDB */ +#define SCST_MAX_LONG_CDB_SIZE 65536 + /* Max size of various names */ #define SCST_MAX_NAME 50 diff --git a/scst/src/scst_lib.c b/scst/src/scst_lib.c index d77043d5b..ba3abdfde 100644 --- a/scst/src/scst_lib.c +++ b/scst/src/scst_lib.c @@ -3684,14 +3684,15 @@ int scst_acg_remove_name(struct scst_acg *acg, const char *name, bool reassign) #endif static struct scst_cmd *scst_create_prepare_internal_cmd( - struct scst_cmd *orig_cmd, int bufsize) + struct scst_cmd *orig_cmd, const uint8_t *cdb, + unsigned int cdb_len, int bufsize) { struct scst_cmd *res; gfp_t gfp_mask = scst_cmd_atomic(orig_cmd) ? GFP_ATOMIC : GFP_KERNEL; TRACE_ENTRY(); - res = scst_alloc_cmd(gfp_mask); + res = scst_alloc_cmd(cdb, cdb_len, gfp_mask); if (res == NULL) goto out; @@ -3738,13 +3739,12 @@ int scst_prepare_request_sense(struct scst_cmd *orig_cmd) } rs_cmd = scst_create_prepare_internal_cmd(orig_cmd, + request_sense, sizeof(request_sense), SCST_SENSE_BUFFERSIZE); if (rs_cmd == NULL) goto out_error; - memcpy(rs_cmd->cdb, request_sense, sizeof(request_sense)); rs_cmd->cdb[1] |= scst_get_cmd_dev_d_sense(orig_cmd); - rs_cmd->cdb_len = sizeof(request_sense); rs_cmd->data_direction = SCST_DATA_READ; rs_cmd->expected_data_direction = rs_cmd->data_direction; rs_cmd->expected_transfer_len = SCST_SENSE_BUFFERSIZE; @@ -4155,16 +4155,28 @@ EXPORT_SYMBOL(scst_cmd_put); * scst_cmd_set_ext_cdb() - sets cmd's extended CDB and its length */ void scst_cmd_set_ext_cdb(struct scst_cmd *cmd, - uint8_t *ext_cdb, unsigned int ext_cdb_len) + uint8_t *ext_cdb, unsigned int ext_cdb_len, + gfp_t gfp_mask) { + unsigned int len = cmd->cdb_len + ext_cdb_len; + TRACE_ENTRY(); - if ((cmd->cdb_len + ext_cdb_len) <= sizeof(cmd->cdb_buf)) + if (len <= sizeof(cmd->cdb_buf)) goto copy; - cmd->cdb = kmalloc(cmd->cdb_len + ext_cdb_len, GFP_ATOMIC); - if (cmd->cdb == NULL) + if (unlikely(len > SCST_MAX_LONG_CDB_SIZE)) { + PRINT_ERROR("Too big CDB (%d)", len); + scst_set_cmd_error(cmd, + SCST_LOAD_SENSE(scst_sense_hardw_error)); + goto out; + } + + cmd->cdb = kmalloc(len, gfp_mask); + if (unlikely(cmd->cdb == NULL)) { + PRINT_ERROR("Unable to alloc extended CDB (size %d)", len); goto out_err; + } memcpy(cmd->cdb, cmd->cdb_buf, cmd->cdb_len); @@ -4184,7 +4196,8 @@ out_err: } EXPORT_SYMBOL(scst_cmd_set_ext_cdb); -struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask) +struct scst_cmd *scst_alloc_cmd(const uint8_t *cdb, + unsigned int cdb_len, gfp_t gfp_mask) { struct scst_cmd *cmd; @@ -4221,9 +4234,36 @@ struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask) cmd->dbl_ua_orig_data_direction = SCST_DATA_UNKNOWN; cmd->dbl_ua_orig_resp_data_len = -1; + if (unlikely(cdb_len == 0)) { + PRINT_ERROR("%s", "Wrong CDB len 0, finishing cmd"); + goto out_free; + } 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; + } + 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; + } + memcpy(cmd->cdb, cdb, cdb_len); + } + + cmd->cdb_len = cdb_len; + out: TRACE_EXIT(); return cmd; + +out_free: + kmem_cache_free(scst_cmd_cachep, cmd); + cmd = NULL; + goto out; } static void scst_destroy_put_cmd(struct scst_cmd *cmd) diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index ce9364836..c66c9e867 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -363,7 +363,8 @@ struct scst_session *scst_alloc_session(struct scst_tgt *tgt, gfp_t gfp_mask, void scst_free_session(struct scst_session *sess); void scst_free_session_callback(struct scst_session *sess); -struct scst_cmd *scst_alloc_cmd(gfp_t gfp_mask); +struct scst_cmd *scst_alloc_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_destroy_cmd(struct scst_cmd *cmd) { diff --git a/scst/src/scst_targ.c b/scst/src/scst_targ.c index 9515ae60b..44ab7bbfe 100644 --- a/scst/src/scst_targ.c +++ b/scst/src/scst_targ.c @@ -127,8 +127,8 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess, } #endif - cmd = scst_alloc_cmd(atomic ? GFP_ATOMIC : GFP_KERNEL); - if (cmd == NULL) + cmd = scst_alloc_cmd(cdb, cdb_len, atomic ? GFP_ATOMIC : GFP_KERNEL); + if (unlikely(cmd == NULL)) goto out; cmd->sess = sess; @@ -142,20 +142,6 @@ struct scst_cmd *scst_rx_cmd(struct scst_session *sess, SCST_LOAD_SENSE(scst_sense_lun_not_supported)); } - /* - * For cdb_len 0 defer the error reporting until scst_cmd_init_done(), - * scst_set_cmd_error() supports nested calls. - */ - if (unlikely(cdb_len > SCST_MAX_CDB_SIZE)) { - PRINT_ERROR("Too big CDB len %d, finishing cmd", cdb_len); - cdb_len = SCST_MAX_CDB_SIZE; - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_message)); - } - - memcpy(cmd->cdb, cdb, cdb_len); - cmd->cdb_len = cdb_len; - TRACE_DBG("cmd %p, sess %p", cmd, sess); scst_sess_get(sess); @@ -337,12 +323,6 @@ void scst_cmd_init_done(struct scst_cmd *cmd, spin_unlock_irqrestore(&sess->sess_list_lock, flags); - if (unlikely(cmd->cdb_len == 0)) { - PRINT_ERROR("%s", "Wrong CDB len 0, finishing cmd"); - scst_set_cmd_error(cmd, - SCST_LOAD_SENSE(scst_sense_invalid_opcode)); - } - if (unlikely(cmd->queue_type >= SCST_CMD_QUEUE_ACA)) { PRINT_ERROR("Unsupported queue type %d", cmd->queue_type); scst_set_cmd_error(cmd,