aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-05-31 18:27:27 +0900
committerTejun Heo <htejun@gmail.com>2006-05-31 18:27:27 +0900
commitc6cf9e99d1de5ca6a08fb639bb73031ffe50d802 (patch)
tree555a47d45b5b80ef14ff53ccb4c6d1439460e251 /drivers
parent7395acb2c840fd4d0cacc91d6fb71440057141ab (diff)
downloadlinux-c6cf9e99d1de5ca6a08fb639bb73031ffe50d802.tar.gz
[PATCH] libata: implement ata_eh_wait()
Implement ata_eh_wait(). On return from this function, it's guaranteed that the EH which was pending or in progress when the function was called is complete - including the tailing part of SCSI EH. This will be used by hotplug and others to synchronize with EH. Signed-off-by: Tejun Heo <htejun@gmail.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/scsi/libata-core.c1
-rw-r--r--drivers/scsi/libata-eh.c38
-rw-r--r--drivers/scsi/libata.h1
3 files changed, 40 insertions, 0 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 11df827e166ff..66df895c9617c 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5189,6 +5189,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
INIT_WORK(&ap->port_task, NULL, NULL);
INIT_LIST_HEAD(&ap->eh_done_q);
+ init_waitqueue_head(&ap->eh_wait_q);
/* set cable type */
ap->cbl = ATA_CBL_NONE;
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index b88f492eab125..9173d8f2ce5db 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -237,6 +237,7 @@ void ata_scsi_error(struct Scsi_Host *host)
ap->eh_context.i = ap->eh_info;
memset(&ap->eh_info, 0, sizeof(ap->eh_info));
+ ap->flags |= ATA_FLAG_EH_IN_PROGRESS;
ap->flags &= ~ATA_FLAG_EH_PENDING;
spin_unlock_irqrestore(hs_lock, flags);
@@ -290,12 +291,49 @@ void ata_scsi_error(struct Scsi_Host *host)
ata_port_printk(ap, KERN_INFO, "EH complete\n");
ap->flags &= ~ATA_FLAG_RECOVERED;
+ /* tell wait_eh that we're done */
+ ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS;
+ wake_up_all(&ap->eh_wait_q);
+
spin_unlock_irqrestore(hs_lock, flags);
DPRINTK("EXIT\n");
}
/**
+ * ata_port_wait_eh - Wait for the currently pending EH to complete
+ * @ap: Port to wait EH for
+ *
+ * Wait until the currently pending EH is complete.
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ */
+void ata_port_wait_eh(struct ata_port *ap)
+{
+ unsigned long flags;
+ DEFINE_WAIT(wait);
+
+ retry:
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+
+ while (ap->flags & (ATA_FLAG_EH_PENDING | ATA_FLAG_EH_IN_PROGRESS)) {
+ prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+ schedule();
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+ }
+
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+
+ /* make sure SCSI EH is complete */
+ if (scsi_host_in_recovery(ap->host)) {
+ msleep(10);
+ goto retry;
+ }
+}
+
+/**
* ata_qc_timeout - Handle timeout of queued command
* @qc: Command that timed out
*
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index b76ad7d7062af..d56d9e1d73dc0 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -103,6 +103,7 @@ extern void ata_schedule_scsi_eh(struct Scsi_Host *shost);
/* libata-eh.c */
extern enum scsi_eh_timer_return ata_scsi_timed_out(struct scsi_cmnd *cmd);
extern void ata_scsi_error(struct Scsi_Host *host);
+extern void ata_port_wait_eh(struct ata_port *ap);
extern void ata_qc_schedule_eh(struct ata_queued_cmd *qc);
#endif /* __LIBATA_H__ */