The last functionality of the target processing thread is offloading possibly long running task management requests from the submitter context. To keep TMR semantics the same we need a single threaded ordered queue, which can be provided by a per-device workqueue with the right flags. Signed-off-by: Christoph Hellwig <hch@xxxxxx> --- drivers/target/target_core_device.c | 2 drivers/target/target_core_tmr.c | 54 ---------- drivers/target/target_core_transport.c | 174 +++------------------------------ include/target/target_core_base.h | 13 -- 4 files changed, 20 insertions(+), 223 deletions(-) Index: lio-core/drivers/target/target_core_transport.c =================================================================== --- lio-core.orig/drivers/target/target_core_transport.c 2012-07-01 14:35:15.774259794 +0200 +++ lio-core/drivers/target/target_core_transport.c 2012-07-01 19:26:09.937488860 +0200 @@ -66,13 +66,11 @@ struct kmem_cache *t10_alua_lu_gp_mem_ca struct kmem_cache *t10_alua_tg_pt_gp_cache; struct kmem_cache *t10_alua_tg_pt_gp_mem_cache; -static int transport_processing_thread(void *param); static void transport_complete_task_attr(struct se_cmd *cmd); static void transport_handle_queue_full(struct se_cmd *cmd, struct se_device *dev); static int transport_generic_get_mem(struct se_cmd *cmd); static void transport_put_cmd(struct se_cmd *cmd); -static void transport_remove_cmd_from_queue(struct se_cmd *cmd); static int transport_set_sense_codes(struct se_cmd *cmd, u8 asc, u8 ascq); static void target_complete_ok_work(struct work_struct *work); @@ -193,14 +191,6 @@ u32 scsi_get_new_index(scsi_index_t type return new_index; } -static void transport_init_queue_obj(struct se_queue_obj *qobj) -{ - atomic_set(&qobj->queue_cnt, 0); - INIT_LIST_HEAD(&qobj->qobj_list); - init_waitqueue_head(&qobj->thread_wq); - spin_lock_init(&qobj->cmd_queue_lock); -} - void transport_subsystem_check_init(void) { int ret; @@ -560,79 +550,8 @@ void transport_cmd_finish_abort(struct s if (transport_cmd_check_stop_to_fabric(cmd)) return; - if (remove) { - transport_remove_cmd_from_queue(cmd); + if (remove) transport_put_cmd(cmd); - } -} - -static void transport_add_cmd_to_queue(struct se_cmd *cmd, int t_state, - bool at_head) -{ - struct se_device *dev = cmd->se_dev; - struct se_queue_obj *qobj = &dev->dev_queue_obj; - unsigned long flags; - - if (t_state) { - spin_lock_irqsave(&cmd->t_state_lock, flags); - cmd->t_state = t_state; - cmd->transport_state |= CMD_T_ACTIVE; - spin_unlock_irqrestore(&cmd->t_state_lock, flags); - } - - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - - /* If the cmd is already on the list, remove it before we add it */ - if (!list_empty(&cmd->se_queue_node)) - list_del(&cmd->se_queue_node); - else - atomic_inc(&qobj->queue_cnt); - - if (at_head) - list_add(&cmd->se_queue_node, &qobj->qobj_list); - else - list_add_tail(&cmd->se_queue_node, &qobj->qobj_list); - cmd->transport_state |= CMD_T_QUEUED; - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - - wake_up_interruptible(&qobj->thread_wq); -} - -static struct se_cmd * -transport_get_cmd_from_queue(struct se_queue_obj *qobj) -{ - struct se_cmd *cmd; - unsigned long flags; - - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - if (list_empty(&qobj->qobj_list)) { - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - return NULL; - } - cmd = list_first_entry(&qobj->qobj_list, struct se_cmd, se_queue_node); - - cmd->transport_state &= ~CMD_T_QUEUED; - list_del_init(&cmd->se_queue_node); - atomic_dec(&qobj->queue_cnt); - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - - return cmd; -} - -static void transport_remove_cmd_from_queue(struct se_cmd *cmd) -{ - struct se_queue_obj *qobj = &cmd->se_dev->dev_queue_obj; - unsigned long flags; - - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - if (!(cmd->transport_state & CMD_T_QUEUED)) { - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - return; - } - cmd->transport_state &= ~CMD_T_QUEUED; - atomic_dec(&qobj->queue_cnt); - list_del_init(&cmd->se_queue_node); - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); } static void target_complete_failure_work(struct work_struct *work) @@ -1126,7 +1045,6 @@ struct se_device *transport_add_device_t return NULL; } - transport_init_queue_obj(&dev->dev_queue_obj); dev->dev_flags = device_flags; dev->dev_status |= TRANSPORT_DEVICE_DEACTIVATED; dev->dev_ptr = transport_dev; @@ -1179,10 +1097,10 @@ struct se_device *transport_add_device_t /* * Startup the struct se_device processing thread */ - dev->process_thread = kthread_run(transport_processing_thread, dev, - "LIO_%s", dev->transport->name); - if (IS_ERR(dev->process_thread)) { - pr_err("Unable to create kthread: LIO_%s\n", + dev->tmr_wq = alloc_workqueue("tmr-%s", WQ_MEM_RECLAIM | WQ_UNBOUND, 1, + dev->transport->name); + if (!dev->tmr_wq) { + pr_err("Unable to create tmr workqueue for %s\n", dev->transport->name); goto out; } @@ -1213,7 +1131,7 @@ struct se_device *transport_add_device_t return dev; out: - kthread_stop(dev->process_thread); + destroy_workqueue(dev->tmr_wq); spin_lock(&hba->device_lock); list_del(&dev->dev_list); @@ -1293,7 +1211,6 @@ void transport_init_se_cmd( INIT_LIST_HEAD(&cmd->se_lun_node); INIT_LIST_HEAD(&cmd->se_delayed_node); INIT_LIST_HEAD(&cmd->se_qf_node); - INIT_LIST_HEAD(&cmd->se_queue_node); INIT_LIST_HEAD(&cmd->se_cmd_list); INIT_LIST_HEAD(&cmd->state_list); init_completion(&cmd->transport_lun_fe_stop_comp); @@ -1488,10 +1405,9 @@ int transport_handle_cdb_direct( return -EINVAL; } /* - * Set TRANSPORT_NEW_CMD state and CMD_T_ACTIVE following - * transport_generic_handle_cdb*() -> transport_add_cmd_to_queue() - * in existing usage to ensure that outstanding descriptors are handled - * correctly during shutdown via transport_wait_for_tasks() + * Set TRANSPORT_NEW_CMD state and CMD_T_ACTIVE to ensure that + * outstanding descriptors are handled correctly during shutdown via + * transport_wait_for_tasks() * * Also, we don't take cmd->t_state_lock here as we only expect * this to be called for initial descriptor submission. @@ -1655,18 +1571,6 @@ int target_submit_tmr(struct se_cmd *se_ } EXPORT_SYMBOL(target_submit_tmr); -/* transport_generic_handle_tmr(): - * - * - */ -int transport_generic_handle_tmr( - struct se_cmd *cmd) -{ - transport_add_cmd_to_queue(cmd, TRANSPORT_PROCESS_TMR, false); - return 0; -} -EXPORT_SYMBOL(transport_generic_handle_tmr); - /* * If the cmd is active, request it to be stopped and sleep until it * has completed. @@ -2647,8 +2551,6 @@ static int transport_lun_wait_for_tasks( cmd->transport_state |= CMD_T_LUN_FE_STOP; spin_unlock_irqrestore(&cmd->t_state_lock, flags); - wake_up_interruptible(&cmd->se_dev->dev_queue_obj.thread_wq); - // XXX: audit task_flags checks. spin_lock_irqsave(&cmd->t_state_lock, flags); if ((cmd->transport_state & CMD_T_BUSY) && @@ -2667,7 +2569,6 @@ static int transport_lun_wait_for_tasks( pr_debug("ConfigFS: ITT[0x%08x] - stopped cmd....\n", cmd->se_tfo->get_task_tag(cmd)); } - transport_remove_cmd_from_queue(cmd); return 0; } @@ -2866,8 +2767,6 @@ bool transport_wait_for_tasks(struct se_ spin_unlock_irqrestore(&cmd->t_state_lock, flags); - wake_up_interruptible(&cmd->se_dev->dev_queue_obj.thread_wq); - wait_for_completion(&cmd->t_transport_stop_comp); spin_lock_irqsave(&cmd->t_state_lock, flags); @@ -3150,8 +3049,9 @@ void transport_send_task_abort(struct se cmd->se_tfo->queue_status(cmd); } -static int transport_generic_do_tmr(struct se_cmd *cmd) +static void target_tmr_work(struct work_struct *work) { + struct se_cmd *cmd = container_of(work, struct se_cmd, work); struct se_device *dev = cmd->se_dev; struct se_tmr_req *tmr = cmd->se_tmr_req; int ret; @@ -3187,54 +3087,13 @@ static int transport_generic_do_tmr(stru cmd->se_tfo->queue_tm_rsp(cmd); transport_cmd_check_stop_to_fabric(cmd); - return 0; } -/* transport_processing_thread(): - * - * - */ -static int transport_processing_thread(void *param) +int transport_generic_handle_tmr( + struct se_cmd *cmd) { - int ret; - struct se_cmd *cmd; - struct se_device *dev = param; - - while (!kthread_should_stop()) { - ret = wait_event_interruptible(dev->dev_queue_obj.thread_wq, - atomic_read(&dev->dev_queue_obj.queue_cnt) || - kthread_should_stop()); - if (ret < 0) - goto out; - -get_cmd: - cmd = transport_get_cmd_from_queue(&dev->dev_queue_obj); - if (!cmd) - continue; - - switch (cmd->t_state) { - case TRANSPORT_NEW_CMD: - BUG(); - break; - case TRANSPORT_PROCESS_TMR: - transport_generic_do_tmr(cmd); - break; - default: - pr_err("Unknown t_state: %d for ITT: 0x%08x " - "i_state: %d on SE LUN: %u\n", - cmd->t_state, - cmd->se_tfo->get_task_tag(cmd), - cmd->se_tfo->get_cmd_state(cmd), - cmd->se_lun->unpacked_lun); - BUG(); - } - - goto get_cmd; - } - -out: - WARN_ON(!list_empty(&dev->state_list)); - WARN_ON(!list_empty(&dev->dev_queue_obj.qobj_list)); - dev->process_thread = NULL; + INIT_WORK(&cmd->work, target_tmr_work); + queue_work(cmd->se_dev->tmr_wq, &cmd->work); return 0; } +EXPORT_SYMBOL(transport_generic_handle_tmr); Index: lio-core/include/target/target_core_base.h =================================================================== --- lio-core.orig/include/target/target_core_base.h 2012-07-01 14:35:15.777593127 +0200 +++ lio-core/include/target/target_core_base.h 2012-07-01 14:36:41.544259281 +0200 @@ -147,7 +147,6 @@ enum transport_state_table { TRANSPORT_WRITE_PENDING = 3, TRANSPORT_PROCESSING = 5, TRANSPORT_COMPLETE = 6, - TRANSPORT_PROCESS_TMR = 9, TRANSPORT_ISTATE_PROCESSING = 11, TRANSPORT_COMPLETE_QF_WP = 18, TRANSPORT_COMPLETE_QF_OK = 19, @@ -464,13 +463,6 @@ struct t10_reservation { struct t10_reservation_ops pr_ops; }; -struct se_queue_obj { - atomic_t queue_cnt; - spinlock_t cmd_queue_lock; - struct list_head qobj_list; - wait_queue_head_t thread_wq; -}; - struct se_tmr_req { /* Task Management function to be performed */ u8 function; @@ -527,7 +519,6 @@ struct se_cmd { /* Only used for internal passthrough and legacy TCM fabric modules */ struct se_session *se_sess; struct se_tmr_req *se_tmr_req; - struct list_head se_queue_node; struct list_head se_cmd_list; struct completion cmd_wait_comp; struct kref cmd_kref; @@ -774,7 +765,6 @@ struct se_device { struct se_obj dev_obj; struct se_obj dev_access_obj; struct se_obj dev_export_obj; - struct se_queue_obj dev_queue_obj; spinlock_t delayed_cmd_lock; spinlock_t execute_task_lock; spinlock_t dev_reservation_lock; @@ -790,8 +780,7 @@ struct se_device { struct t10_pr_registration *dev_pr_res_holder; struct list_head dev_sep_list; struct list_head dev_tmr_list; - /* Pointer to descriptor for processing thread */ - struct task_struct *process_thread; + struct workqueue_struct *tmr_wq; struct work_struct qf_work_queue; struct list_head delayed_cmd_list; struct list_head state_list; Index: lio-core/drivers/target/target_core_tmr.c =================================================================== --- lio-core.orig/drivers/target/target_core_tmr.c 2012-07-01 14:28:56.930928723 +0200 +++ lio-core/drivers/target/target_core_tmr.c 2012-07-01 14:36:41.000000000 +0200 @@ -351,57 +351,6 @@ static void core_tmr_drain_state_list( } } -static void core_tmr_drain_cmd_list( - struct se_device *dev, - struct se_cmd *prout_cmd, - struct se_node_acl *tmr_nacl, - int tas, - struct list_head *preempt_and_abort_list) -{ - LIST_HEAD(drain_cmd_list); - struct se_queue_obj *qobj = &dev->dev_queue_obj; - struct se_cmd *cmd, *tcmd; - unsigned long flags; - - /* - * Release all commands remaining in the per-device command queue. - * - * This follows the same logic as above for the state list. - */ - spin_lock_irqsave(&qobj->cmd_queue_lock, flags); - list_for_each_entry_safe(cmd, tcmd, &qobj->qobj_list, se_queue_node) { - /* - * For PREEMPT_AND_ABORT usage, only process commands - * with a matching reservation key. - */ - if (target_check_cdb_and_preempt(preempt_and_abort_list, cmd)) - continue; - /* - * Not aborting PROUT PREEMPT_AND_ABORT CDB.. - */ - if (prout_cmd == cmd) - continue; - - cmd->transport_state &= ~CMD_T_QUEUED; - atomic_dec(&qobj->queue_cnt); - list_move_tail(&cmd->se_queue_node, &drain_cmd_list); - } - spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags); - - while (!list_empty(&drain_cmd_list)) { - cmd = list_entry(drain_cmd_list.next, struct se_cmd, se_queue_node); - list_del_init(&cmd->se_queue_node); - - pr_debug("LUN_RESET: %s from Device Queue: cmd: %p t_state:" - " %d t_fe_count: %d\n", (preempt_and_abort_list) ? - "Preempt" : "", cmd, cmd->t_state, - atomic_read(&cmd->t_fe_count)); - - core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, - atomic_read(&cmd->t_fe_count)); - } -} - int core_tmr_lun_reset( struct se_device *dev, struct se_tmr_req *tmr, @@ -444,8 +393,7 @@ int core_tmr_lun_reset( core_tmr_drain_tmr_list(dev, tmr, preempt_and_abort_list); core_tmr_drain_state_list(dev, prout_cmd, tmr_nacl, tas, preempt_and_abort_list); - core_tmr_drain_cmd_list(dev, prout_cmd, tmr_nacl, tas, - preempt_and_abort_list); + /* * Clear any legacy SPC-2 reservation when called during * LOGICAL UNIT RESET Index: lio-core/drivers/target/target_core_device.c =================================================================== --- lio-core.orig/drivers/target/target_core_device.c 2012-07-01 14:28:56.930928723 +0200 +++ lio-core/drivers/target/target_core_device.c 2012-07-01 14:36:41.544259281 +0200 @@ -715,7 +715,7 @@ void se_release_device_for_hba(struct se se_dev_stop(dev); if (dev->dev_ptr) { - kthread_stop(dev->process_thread); + destroy_workqueue(dev->tmr_wq); if (dev->transport->free_device) dev->transport->free_device(dev->dev_ptr); } -- To unsubscribe from this list: send the line "unsubscribe target-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html