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/target_core_tmr.c | |
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/target_core_tmr.c')
-rw-r--r-- | drivers/target/target_core_tmr.c | 64 |
1 files changed, 64 insertions, 0 deletions
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, |