[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>
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 11df827..66df895 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -5189,6 +5189,7 @@
 
 	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 b88f492..9173d8f 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -237,6 +237,7 @@
 		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 @@
 		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 b76ad7d..d56d9e1 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -103,6 +103,7 @@
 /* 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__ */