From: Nicholas Bellinger <nab@xxxxxxxxxxxxxxx> This patch adds support for the initialization and release of pre-allocated struct se_cmd descriptors for use within a per I/O context TCM fabric module dependent data structures. This allows a TCM fabric module to avoid the extra memory allocation calls in *transport_alloc_se_cmd(), and have the 'struct se_cmd', 'struct se_cmd->t_task' and 'struct se_cmd->sense_buffer' memory provided by said per I/O context TCM fabric module dependent data structures. This includes the addition of a new transport_init_se_cmd() function, which is intended to be used by v4 fabric modules using pre-allocated struct se_cmd descriptors instead of the current *transport_alloc_se_cmd() code. This includes a 'unsigned char *sense_buffer' parameter used for setting up the struct se_cmd->sense_buffer pointer. __transport_alloc_se_cmd() has also been converted to use transport_init_se_cmd(). On the release path, this includes the addition of a transport_release_se_cmd() wrapper for handling both the pre-allocation and legacy allocation cases, and also updates transport_release_cmd_to_pool() to follow the new pre-allocated logic. Also note this patch series keeps existing TCM fabric module compatibility of using *transport_alloc_se_cmd(), and transport_init_se_cmd() can be enabled transparently as an optional feature on a per fabric module basis. Many thanks to Joe Eykholt for originally mentioning this optimization here: http://groups.google.com/group/linux-iscsi-target-dev/browse_thread/thread/88a6434fa9cad163 Thanks again Joe! Signed-off-by: Nicholas A. Bellinger <nab@xxxxxxxxxxxxxxx> --- drivers/target/target_core_transport.c | 131 +++++++++++++++++++++++++------- include/target/target_core_base.h | 3 + include/target/target_core_transport.h | 4 + 3 files changed, 110 insertions(+), 28 deletions(-) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 95401cb..8c72993 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -2738,6 +2738,7 @@ struct se_cmd *__transport_alloc_se_cmd( int task_attr) { struct se_cmd *cmd; + unsigned char *sense_buffer; int gfp_type = (in_interrupt()) ? GFP_ATOMIC : GFP_KERNEL; if (data_direction == SE_DIRECTION_BIDI) { @@ -2750,27 +2751,50 @@ struct se_cmd *__transport_alloc_se_cmd( printk(KERN_ERR "kmem_cache_alloc() failed for se_cmd_cache\n"); return ERR_PTR(-ENOMEM); } - INIT_LIST_HEAD(&cmd->se_lun_list); - INIT_LIST_HEAD(&cmd->se_delayed_list); - INIT_LIST_HEAD(&cmd->se_ordered_list); - - cmd->t_task = kzalloc(sizeof(struct se_transport_task), gfp_type); - if (!(cmd->t_task)) { - printk(KERN_ERR "Unable to allocate cmd->t_task\n"); - kmem_cache_free(se_cmd_cache, cmd); - return NULL; - } - cmd->sense_buffer = kzalloc( + sense_buffer = kzalloc( TRANSPORT_SENSE_BUFFER + tfo->get_fabric_sense_len(), gfp_type); - if (!(cmd->sense_buffer)) { + if (!(sense_buffer)) { printk(KERN_ERR "Unable to allocate memory for" " cmd->sense_buffer\n"); - kfree(cmd->t_task); kmem_cache_free(se_cmd_cache, cmd); return NULL; } + /* + * Initialize the new struct se_cmd descriptor + */ + transport_init_se_cmd(cmd, tfo, se_sess, data_length, data_direction, + task_attr, sense_buffer); + /* + * Setup the se_fabric_cmd_ptr assignment which will signal + * TCM allocation of struct se_cmd in the release and free codepaths + */ + cmd->se_fabric_cmd_ptr = fabric_cmd_ptr; + return cmd; +} + +/* + * Used by fabric modules containing a local struct se_cmd within their + * fabric dependent per I/O descriptor. + */ +void transport_init_se_cmd( + struct se_cmd *cmd, + struct target_core_fabric_ops *tfo, + struct se_session *se_sess, + u32 data_length, + int data_direction, + int task_attr, + unsigned char *sense_buffer) +{ + INIT_LIST_HEAD(&cmd->se_lun_list); + INIT_LIST_HEAD(&cmd->se_delayed_list); + INIT_LIST_HEAD(&cmd->se_ordered_list); + /* + * Setup t_task pointer to t_task_backstore + */ + cmd->t_task = &cmd->t_task_backstore; + INIT_LIST_HEAD(&T_TASK(cmd)->t_task_list); init_completion(&T_TASK(cmd)->transport_lun_fe_stop_comp); init_completion(&T_TASK(cmd)->transport_lun_stop_comp); @@ -2782,13 +2806,12 @@ struct se_cmd *__transport_alloc_se_cmd( cmd->se_tfo = tfo; cmd->se_sess = se_sess; - cmd->se_fabric_cmd_ptr = fabric_cmd_ptr; cmd->data_length = data_length; cmd->data_direction = data_direction; cmd->sam_task_attr = task_attr; - - return cmd; + cmd->sense_buffer = sense_buffer; } +EXPORT_SYMBOL(transport_init_se_cmd); int transport_check_alloc_task_attr(struct se_cmd *cmd) { @@ -2834,11 +2857,19 @@ void transport_free_se_cmd( { if (se_cmd->se_tmr_req) core_tmr_release_req(se_cmd->se_tmr_req); - + /* + * Release any optional TCM fabric dependent iovecs allocated by + * transport_allocate_iovecs_for_cmd() + */ kfree(se_cmd->iov_data); - kfree(se_cmd->sense_buffer); - kfree(se_cmd->t_task); - kmem_cache_free(se_cmd_cache, se_cmd); + /* + * Only release the sense_buffer, t_task, and remaining se_cmd memory + * if this descriptor was allocated with transport_alloc_se_cmd() + */ + if (se_cmd->se_fabric_cmd_ptr) { + kfree(se_cmd->sense_buffer); + kmem_cache_free(se_cmd_cache, se_cmd); + } } EXPORT_SYMBOL(transport_free_se_cmd); @@ -5841,8 +5872,8 @@ static inline struct se_cmd *transport_alloc_passthrough_cmd( u32 data_length, int data_direction) { - return __transport_alloc_se_cmd(&passthrough_fabric_ops, NULL, NULL, - data_length, data_direction, TASK_ATTR_SIMPLE); + return __transport_alloc_se_cmd(&passthrough_fabric_ops, NULL, + (void *)1, data_length, data_direction, TASK_ATTR_SIMPLE); } static inline void transport_release_tasks(struct se_cmd *); @@ -6346,6 +6377,22 @@ static inline int transport_dec_and_check(struct se_cmd *cmd) return 0; } +static inline void transport_release_se_cmd(struct se_cmd *cmd) +{ + /* + * Determine if this struct se_cmd descriptor was allocated + * with __transport_alloc_se_cmd(), or is a member of a + * TCM fabric module dependent descriptor. + */ + if (cmd->se_fabric_cmd_ptr) { + CMD_TFO(cmd)->release_cmd_direct(cmd); + transport_free_se_cmd(cmd); + } else { + transport_free_se_cmd(cmd); + CMD_TFO(cmd)->release_cmd_direct(cmd); + } +} + void transport_release_fe_cmd(struct se_cmd *cmd) { unsigned long flags; @@ -6369,8 +6416,7 @@ free_pages: if (cmd->se_cmd_flags & SCF_CMD_PASSTHROUGH) kfree(cmd->se_lun); - CMD_TFO(cmd)->release_cmd_direct(cmd); - transport_free_se_cmd(cmd); + transport_release_se_cmd(cmd); } /* transport_generic_remove(): @@ -6417,8 +6463,7 @@ release_cmd: if (cmd->se_cmd_flags & SCF_CMD_PASSTHROUGH) kfree(cmd->se_lun); - CMD_TFO(cmd)->release_cmd_direct(cmd); - transport_free_se_cmd(cmd); + transport_release_se_cmd(cmd); } return 0; @@ -7498,9 +7543,39 @@ void transport_release_cmd_to_pool(struct se_cmd *cmd) if (cmd->se_cmd_flags & SCF_CMD_PASSTHROUGH) kfree(cmd->se_lun); /* - * Release struct se_cmd->se_fabric_cmd_ptr in fabric + * A NULL cmd->se_fabric_cmd_ptr signals that the TCM fabric module + * is using struct se_cmd as part of it's internal fabric per I/O + * descriptor + */ + if (!(cmd->se_fabric_cmd_ptr)) { + transport_free_se_cmd(cmd); + /* + * Make sure that this is only called for struct se_cmd + * descriptors containing valid T_TASK(cmd) and CMD_TFO(cmd) + * pointers + */ + if ((T_TASK(cmd) && (CMD_TFO(cmd)))) + CMD_TFO(cmd)->release_cmd_to_pool(cmd); + else { + printk(KERN_ERR "T_TASK(cmd) && (CMD_TFO(cmd) NULL for" + " se_fabric_cmd_ptr=NULL inside of" + " transport_release_cmd_to_pool()\n"); + dump_stack(); + } + + return; + } + /* + * Release explict allocated struct se_cmd->se_fabric_cmd_ptr in fabric */ - CMD_TFO(cmd)->release_cmd_to_pool(cmd); + if ((T_TASK(cmd) && (CMD_TFO(cmd)))) + CMD_TFO(cmd)->release_cmd_to_pool(cmd); + else { + dump_stack(); + printk(KERN_ERR "NULL T_TASK(cmd) && (CMD_TFO(cmd) for" + " se_fabric_cmd_ptr=1 inside of" + " transport_release_cmd_to_pool()\n"); + } transport_free_se_cmd(cmd); } diff --git a/include/target/target_core_base.h b/include/target/target_core_base.h index 3c14037..e9f053f 100644 --- a/include/target/target_core_base.h +++ b/include/target/target_core_base.h @@ -584,10 +584,13 @@ struct se_cmd { struct se_device *se_obj_ptr; struct se_device *se_orig_obj_ptr; struct se_lun *se_lun; + /* Only used for internal passthrough and legacy TCM fabric modules */ void *se_fabric_cmd_ptr; struct se_session *se_sess; struct se_tmr_req *se_tmr_req; + /* t_task is setup to t_task_backstore in transport_init_se_cmd() */ struct se_transport_task *t_task; + struct se_transport_task t_task_backstore; struct target_core_fabric_ops *se_tfo; int (*transport_add_cmd_to_queue)(struct se_cmd *, int); int (*transport_allocate_resources)(struct se_cmd *, u32, u32); diff --git a/include/target/target_core_transport.h b/include/target/target_core_transport.h index 2eee898..8f6e3e2 100644 --- a/include/target/target_core_transport.h +++ b/include/target/target_core_transport.h @@ -186,6 +186,10 @@ extern int transport_check_alloc_task_attr(struct se_cmd *); extern struct se_cmd *transport_alloc_se_cmd(struct target_core_fabric_ops *, struct se_session *, void *, u32, int, int); +extern void transport_init_se_cmd(struct se_cmd *, + struct target_core_fabric_ops *, + struct se_session *, u32, int, int, + unsigned char *); extern void transport_free_se_cmd(struct se_cmd *); extern int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *); extern int transport_generic_handle_cdb(struct se_cmd *); -- 1.5.6.5 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html