diff options
author | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-02-13 05:38:14 -0500 |
---|---|---|
committer | Nicholas Bellinger <nab@linux-iscsi.org> | 2012-02-25 17:37:49 -0500 |
commit | 3d28934aaae5e924afedf0f5cb42e1316514da6b (patch) | |
tree | 6a11e13b7da8187339f262c14726107ccb32eb1b /drivers/target | |
parent | ffc32d5259d107a3aa1b822e22f20b69cb9ec0a5 (diff) |
target: Add TMR_ABORT_TASK task management support
This patch adds initial support for TMR_ABORT_TASK ops for se_cmd
descriptors using se_sess->sess_cmd_list and se_cmd->cmd_kref counting.
It will perform an explict abort for all outstanding se_cmd ops based
upon tmr->ref_task_tag that have not been set CMD_T_COMPLETE.
It will cancel se_cmd->work and wait for backing I/O to complete before
attempting to send SAM_STAT_TASK_ABORTED and perform
target_put_sess_cmd() to release the referenced descriptor.
It also adds a CMD_T_ABORTED check into transport_complete_task() to
catch the completion from backend I/O that has been aborted, and
updates transport_wait_for_tasks() to allow CMD_T_ABORTED usage with
core_tmr_abort_task() context.
Reported-by: Roland Dreier <roland@purestorage.com>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Nicholas Bellinger <nab@linux-iscsi.org>
Diffstat (limited to 'drivers/target')
-rw-r--r-- | drivers/target/target_core_internal.h | 2 | ||||
-rw-r--r-- | drivers/target/target_core_tmr.c | 64 | ||||
-rw-r--r-- | drivers/target/target_core_transport.c | 20 |
3 files changed, 79 insertions, 7 deletions
diff --git a/drivers/target/target_core_internal.h b/drivers/target/target_core_internal.h index 45001364788a..b026dedb8184 100644 --- a/drivers/target/target_core_internal.h +++ b/drivers/target/target_core_internal.h | |||
@@ -75,6 +75,8 @@ struct se_hba *core_alloc_hba(const char *, u32, u32); | |||
75 | int core_delete_hba(struct se_hba *); | 75 | int core_delete_hba(struct se_hba *); |
76 | 76 | ||
77 | /* target_core_tmr.c */ | 77 | /* target_core_tmr.c */ |
78 | void core_tmr_abort_task(struct se_device *, struct se_tmr_req *, | ||
79 | struct se_session *); | ||
78 | int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *, | 80 | int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *, |
79 | struct list_head *, struct se_cmd *); | 81 | struct list_head *, struct se_cmd *); |
80 | 82 | ||
diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 5d3eb9e6c2ed..f015839aef89 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c | |||
@@ -118,6 +118,70 @@ static int target_check_cdb_and_preempt(struct list_head *list, | |||
118 | return 1; | 118 | return 1; |
119 | } | 119 | } |
120 | 120 | ||
121 | void core_tmr_abort_task( | ||
122 | struct se_device *dev, | ||
123 | struct se_tmr_req *tmr, | ||
124 | struct se_session *se_sess) | ||
125 | { | ||
126 | struct se_cmd *se_cmd, *tmp_cmd; | ||
127 | unsigned long flags; | ||
128 | int ref_tag; | ||
129 | |||
130 | spin_lock_irqsave(&se_sess->sess_cmd_lock, flags); | ||
131 | list_for_each_entry_safe(se_cmd, tmp_cmd, | ||
132 | &se_sess->sess_cmd_list, se_cmd_list) { | ||
133 | |||
134 | if (dev != se_cmd->se_dev) | ||
135 | continue; | ||
136 | ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd); | ||
137 | if (tmr->ref_task_tag != ref_tag) | ||
138 | continue; | ||
139 | |||
140 | printk("ABORT_TASK: Found referenced %s task_tag: %u\n", | ||
141 | se_cmd->se_tfo->get_fabric_name(), ref_tag); | ||
142 | |||
143 | spin_lock_irq(&se_cmd->t_state_lock); | ||
144 | if (se_cmd->transport_state & CMD_T_COMPLETE) { | ||
145 | printk("ABORT_TASK: ref_tag: %u already complete, skipping\n", ref_tag); | ||
146 | spin_unlock_irq(&se_cmd->t_state_lock); | ||
147 | spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); | ||
148 | goto out; | ||
149 | } | ||
150 | se_cmd->transport_state |= CMD_T_ABORTED; | ||
151 | spin_unlock_irq(&se_cmd->t_state_lock); | ||
152 | |||
153 | list_del_init(&se_cmd->se_cmd_list); | ||
154 | kref_get(&se_cmd->cmd_kref); | ||
155 | spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); | ||
156 | |||
157 | cancel_work_sync(&se_cmd->work); | ||
158 | transport_wait_for_tasks(se_cmd); | ||
159 | /* | ||
160 | * Now send SAM_STAT_TASK_ABORTED status for the referenced | ||
161 | * se_cmd descriptor.. | ||
162 | */ | ||
163 | transport_send_task_abort(se_cmd); | ||
164 | /* | ||
165 | * Also deal with possible extra acknowledge reference.. | ||
166 | */ | ||
167 | if (se_cmd->se_cmd_flags & SCF_ACK_KREF) | ||
168 | target_put_sess_cmd(se_sess, se_cmd); | ||
169 | |||
170 | target_put_sess_cmd(se_sess, se_cmd); | ||
171 | |||
172 | printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" | ||
173 | " ref_tag: %d\n", ref_tag); | ||
174 | tmr->response = TMR_FUNCTION_COMPLETE; | ||
175 | return; | ||
176 | } | ||
177 | spin_unlock_irqrestore(&se_sess->sess_cmd_lock, flags); | ||
178 | |||
179 | out: | ||
180 | printk("ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST for ref_tag: %d\n", | ||
181 | tmr->ref_task_tag); | ||
182 | tmr->response = TMR_TASK_DOES_NOT_EXIST; | ||
183 | } | ||
184 | |||
121 | static void core_tmr_drain_tmr_list( | 185 | static void core_tmr_drain_tmr_list( |
122 | struct se_device *dev, | 186 | struct se_device *dev, |
123 | struct se_tmr_req *tmr, | 187 | struct se_tmr_req *tmr, |
diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 52a66710a293..19804b6fdbaa 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c | |||
@@ -699,17 +699,24 @@ void transport_complete_task(struct se_task *task, int success) | |||
699 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 699 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
700 | return; | 700 | return; |
701 | } | 701 | } |
702 | 702 | /* | |
703 | if (cmd->transport_state & CMD_T_FAILED) { | 703 | * Check for case where an explict ABORT_TASK has been received |
704 | * and transport_wait_for_tasks() will be waiting for completion.. | ||
705 | */ | ||
706 | if (cmd->transport_state & CMD_T_ABORTED && | ||
707 | cmd->transport_state & CMD_T_STOP) { | ||
708 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | ||
709 | complete(&cmd->t_transport_stop_comp); | ||
710 | return; | ||
711 | } else if (cmd->transport_state & CMD_T_FAILED) { | ||
704 | cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; | 712 | cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; |
705 | INIT_WORK(&cmd->work, target_complete_failure_work); | 713 | INIT_WORK(&cmd->work, target_complete_failure_work); |
706 | } else { | 714 | } else { |
707 | cmd->transport_state |= CMD_T_COMPLETE; | ||
708 | INIT_WORK(&cmd->work, target_complete_ok_work); | 715 | INIT_WORK(&cmd->work, target_complete_ok_work); |
709 | } | 716 | } |
710 | 717 | ||
711 | cmd->t_state = TRANSPORT_COMPLETE; | 718 | cmd->t_state = TRANSPORT_COMPLETE; |
712 | cmd->transport_state |= CMD_T_ACTIVE; | 719 | cmd->transport_state |= (CMD_T_COMPLETE | CMD_T_ACTIVE); |
713 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 720 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
714 | 721 | ||
715 | queue_work(target_completion_wq, &cmd->work); | 722 | queue_work(target_completion_wq, &cmd->work); |
@@ -4374,8 +4381,7 @@ bool transport_wait_for_tasks(struct se_cmd *cmd) | |||
4374 | cmd->transport_state &= ~CMD_T_LUN_STOP; | 4381 | cmd->transport_state &= ~CMD_T_LUN_STOP; |
4375 | } | 4382 | } |
4376 | 4383 | ||
4377 | if (!(cmd->transport_state & CMD_T_ACTIVE) || | 4384 | if (!(cmd->transport_state & CMD_T_ACTIVE)) { |
4378 | (cmd->transport_state & CMD_T_ABORTED)) { | ||
4379 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); | 4385 | spin_unlock_irqrestore(&cmd->t_state_lock, flags); |
4380 | return false; | 4386 | return false; |
4381 | } | 4387 | } |
@@ -4681,7 +4687,7 @@ static int transport_generic_do_tmr(struct se_cmd *cmd) | |||
4681 | 4687 | ||
4682 | switch (tmr->function) { | 4688 | switch (tmr->function) { |
4683 | case TMR_ABORT_TASK: | 4689 | case TMR_ABORT_TASK: |
4684 | tmr->response = TMR_FUNCTION_REJECTED; | 4690 | core_tmr_abort_task(dev, tmr, cmd->se_sess); |
4685 | break; | 4691 | break; |
4686 | case TMR_ABORT_TASK_SET: | 4692 | case TMR_ABORT_TASK_SET: |
4687 | case TMR_CLEAR_ACA: | 4693 | case TMR_CLEAR_ACA: |