aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas/sas_ata.c
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2011-11-28 14:29:20 -0500
committerJames Bottomley <JBottomley@Parallels.com>2012-02-19 15:06:08 -0500
commit9095a64a9aead653df320e3a6fc70835c15d46e4 (patch)
tree63ad804f78668bf28d90ea2f8c9640ce92dacdea /drivers/scsi/libsas/sas_ata.c
parenta3a142524aa4b1539a64a55087bf12ffa4b1f94e (diff)
[SCSI] libsas: fix timeout vs completion race
Until we have told the lldd to forget a task a timed out operation can return from the hardware at any time. Since completion frees the task we need to make sure that no tasks run their normal completion handler once eh has decided to manage the task. Similar to ata_scsi_cmd_error_handler() freeze completions to let eh judge the outcome of the race. Task collector mode is problematic because it presents a situation where a task can be timed out and aborted before the lldd has even seen it. For this case we need to guarantee that a task that an lldd has been told to forget does not get queued after the lldd says "never seen it". With sas_scsi_timed_out we achieve this with the ->task_queue_flush mutex, rather than adding more time. Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers/scsi/libsas/sas_ata.c')
-rw-r--r--drivers/scsi/libsas/sas_ata.c35
1 files changed, 13 insertions, 22 deletions
diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c
index 903bb441b9f9..4c2a1402373c 100644
--- a/drivers/scsi/libsas/sas_ata.c
+++ b/drivers/scsi/libsas/sas_ata.c
@@ -93,21 +93,30 @@ static enum ata_completion_errors sas_to_ata_err(struct task_status_struct *ts)
93static void sas_ata_task_done(struct sas_task *task) 93static void sas_ata_task_done(struct sas_task *task)
94{ 94{
95 struct ata_queued_cmd *qc = task->uldd_task; 95 struct ata_queued_cmd *qc = task->uldd_task;
96 struct domain_device *dev; 96 struct domain_device *dev = task->dev;
97 struct task_status_struct *stat = &task->task_status; 97 struct task_status_struct *stat = &task->task_status;
98 struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf; 98 struct ata_task_resp *resp = (struct ata_task_resp *)stat->buf;
99 struct sas_ha_struct *sas_ha; 99 struct sas_ha_struct *sas_ha = dev->port->ha;
100 enum ata_completion_errors ac; 100 enum ata_completion_errors ac;
101 unsigned long flags; 101 unsigned long flags;
102 struct ata_link *link; 102 struct ata_link *link;
103 struct ata_port *ap; 103 struct ata_port *ap;
104 104
105 spin_lock_irqsave(&dev->done_lock, flags);
106 if (test_bit(SAS_HA_FROZEN, &sas_ha->state))
107 task = NULL;
108 else if (qc && qc->scsicmd)
109 ASSIGN_SAS_TASK(qc->scsicmd, NULL);
110 spin_unlock_irqrestore(&dev->done_lock, flags);
111
112 /* check if libsas-eh got to the task before us */
113 if (unlikely(!task))
114 return;
115
105 if (!qc) 116 if (!qc)
106 goto qc_already_gone; 117 goto qc_already_gone;
107 118
108 ap = qc->ap; 119 ap = qc->ap;
109 dev = ap->private_data;
110 sas_ha = dev->port->ha;
111 link = &ap->link; 120 link = &ap->link;
112 121
113 spin_lock_irqsave(ap->lock, flags); 122 spin_lock_irqsave(ap->lock, flags);
@@ -156,8 +165,6 @@ static void sas_ata_task_done(struct sas_task *task)
156 } 165 }
157 166
158 qc->lldd_task = NULL; 167 qc->lldd_task = NULL;
159 if (qc->scsicmd)
160 ASSIGN_SAS_TASK(qc->scsicmd, NULL);
161 ata_qc_complete(qc); 168 ata_qc_complete(qc);
162 spin_unlock_irqrestore(ap->lock, flags); 169 spin_unlock_irqrestore(ap->lock, flags);
163 170
@@ -633,22 +640,6 @@ void sas_ata_strategy_handler(struct Scsi_Host *shost)
633 sas_enable_revalidation(sas_ha); 640 sas_enable_revalidation(sas_ha);
634} 641}
635 642
636int sas_ata_timed_out(struct scsi_cmnd *cmd, struct sas_task *task,
637 enum blk_eh_timer_return *rtn)
638{
639 struct domain_device *ddev = cmd_to_domain_dev(cmd);
640
641 if (!dev_is_sata(ddev) || task)
642 return 0;
643
644 /* we're a sata device with no task, so this must be a libata
645 * eh timeout. Ideally should hook into libata timeout
646 * handling, but there's no point, it just wants to activate
647 * the eh thread */
648 *rtn = BLK_EH_NOT_HANDLED;
649 return 1;
650}
651
652int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q, 643int sas_ata_eh(struct Scsi_Host *shost, struct list_head *work_q,
653 struct list_head *done_q) 644 struct list_head *done_q)
654{ 645{