[PATCH] libata: implement ata_eh_wait()
authorTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:27:27 +0000 (18:27 +0900)
committerTejun Heo <htejun@gmail.com>
Wed, 31 May 2006 09:27:27 +0000 (18:27 +0900)
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>
drivers/scsi/libata-core.c
drivers/scsi/libata-eh.c
drivers/scsi/libata.h
include/linux/libata.h

index 11df827e166ff6f63b21b3653ffbafd9889116e6..66df895c9617c54eb9363e2f14f04c1af7f0fa7c 100644 (file)
@@ -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;
index b88f492eab125d797abf4ae0cbf32c1b2919c471..9173d8f2ce5db7e999211e03f3509895cb0e2a9a 100644 (file)
@@ -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,11 +291,48 @@ 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
index b76ad7d7062aff4beba9b6521a78f9788cb130f7..d56d9e1d73dc0bfab120a71f3fcefef2ecac9987 100644 (file)
@@ -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__ */
index 3f9c65f1aafa144270a04928b3a9bb3b52a059f6..2eb5828839e43886820a4f2654691b95b89614e1 100644 (file)
@@ -157,6 +157,7 @@ enum {
        ATA_FLAG_FLUSH_PORT_TASK = (1 << 14), /* flush port task */
 
        ATA_FLAG_EH_PENDING     = (1 << 15), /* EH pending */
+       ATA_FLAG_EH_IN_PROGRESS = (1 << 16), /* EH in progress */
        ATA_FLAG_FROZEN         = (1 << 17), /* port is frozen */
        ATA_FLAG_RECOVERED      = (1 << 18), /* recovery action performed */
 
@@ -490,6 +491,7 @@ struct ata_port {
 
        u32                     msg_enable;
        struct list_head        eh_done_q;
+       wait_queue_head_t       eh_wait_q;
 
        void                    *private_data;