aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/target
diff options
context:
space:
mode:
authorNicholas Bellinger <nab@linux-iscsi.org>2012-02-13 05:38:14 -0500
committerNicholas Bellinger <nab@linux-iscsi.org>2012-02-25 17:37:49 -0500
commit3d28934aaae5e924afedf0f5cb42e1316514da6b (patch)
tree6a11e13b7da8187339f262c14726107ccb32eb1b /drivers/target
parentffc32d5259d107a3aa1b822e22f20b69cb9ec0a5 (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.h2
-rw-r--r--drivers/target/target_core_tmr.c64
-rw-r--r--drivers/target/target_core_transport.c20
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);
75int core_delete_hba(struct se_hba *); 75int core_delete_hba(struct se_hba *);
76 76
77/* target_core_tmr.c */ 77/* target_core_tmr.c */
78void core_tmr_abort_task(struct se_device *, struct se_tmr_req *,
79 struct se_session *);
78int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *, 80int 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
121void 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
179out:
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
121static void core_tmr_drain_tmr_list( 185static 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: