[PATCH] libata-hp: implement hotplug
[deliverable/linux.git] / drivers / scsi / libata-eh.c
index a049bffdf7705e279429b37a58adff59920c58c4..b53e2e7db498a5a2155302babf40ed7bf40f33a0 100644 (file)
@@ -932,10 +932,8 @@ static void ata_eh_analyze_serror(struct ata_port *ap)
                err_mask |= AC_ERR_SYSTEM;
                action |= ATA_EH_SOFTRESET;
        }
-       if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG)) {
-               err_mask |= AC_ERR_ATA_BUS;
-               action |= ATA_EH_HARDRESET;
-       }
+       if (serror & (SERR_PHYRDY_CHG | SERR_DEV_XCHG))
+               ata_ehi_hotplugged(&ehc->i);
 
        ehc->i.err_mask |= err_mask;
        ehc->i.action |= action;
@@ -1487,11 +1485,12 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
        return rc;
 }
 
-static int ata_eh_revalidate(struct ata_port *ap,
-                            struct ata_device **r_failed_dev)
+static int ata_eh_revalidate_and_attach(struct ata_port *ap,
+                                       struct ata_device **r_failed_dev)
 {
        struct ata_eh_context *ehc = &ap->eh_context;
        struct ata_device *dev;
+       unsigned long flags;
        int i, rc = 0;
 
        DPRINTK("ENTER\n");
@@ -1513,6 +1512,23 @@ static int ata_eh_revalidate(struct ata_port *ap,
                                break;
 
                        ehc->i.action &= ~ATA_EH_REVALIDATE;
+               } else if (dev->class == ATA_DEV_UNKNOWN &&
+                          ehc->tries[dev->devno] &&
+                          ata_class_enabled(ehc->classes[dev->devno])) {
+                       dev->class = ehc->classes[dev->devno];
+
+                       rc = ata_dev_read_id(dev, &dev->class, 1, dev->id);
+                       if (rc == 0)
+                               rc = ata_dev_configure(dev, 1);
+
+                       if (rc) {
+                               dev->class = ATA_DEV_UNKNOWN;
+                               break;
+                       }
+
+                       spin_lock_irqsave(&ap->host_set->lock, flags);
+                       ap->flags |= ATA_FLAG_SCSI_HOTPLUG;
+                       spin_unlock_irqrestore(&ap->host_set->lock, flags);
                }
        }
 
@@ -1533,6 +1549,36 @@ static int ata_port_nr_enabled(struct ata_port *ap)
        return cnt;
 }
 
+static int ata_port_nr_vacant(struct ata_port *ap)
+{
+       int i, cnt = 0;
+
+       for (i = 0; i < ATA_MAX_DEVICES; i++)
+               if (ap->device[i].class == ATA_DEV_UNKNOWN)
+                       cnt++;
+       return cnt;
+}
+
+static int ata_eh_skip_recovery(struct ata_port *ap)
+{
+       struct ata_eh_context *ehc = &ap->eh_context;
+       int i;
+
+       if (ap->flags & ATA_FLAG_FROZEN || ata_port_nr_enabled(ap))
+               return 0;
+
+       /* skip if class codes for all vacant slots are ATA_DEV_NONE */
+       for (i = 0; i < ATA_MAX_DEVICES; i++) {
+               struct ata_device *dev = &ap->device[i];
+
+               if (dev->class == ATA_DEV_UNKNOWN &&
+                   ehc->classes[dev->devno] != ATA_DEV_NONE)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /**
  *     ata_eh_recover - recover host port after error
  *     @ap: host port to recover
@@ -1543,9 +1589,10 @@ static int ata_port_nr_enabled(struct ata_port *ap)
  *
  *     This is the alpha and omega, eum and yang, heart and soul of
  *     libata exception handling.  On entry, actions required to
- *     recover each devices are recorded in eh_context.  This
- *     function executes all the operations with appropriate retrials
- *     and fallbacks to resurrect failed devices.
+ *     recover the port and hotplug requests are recorded in
+ *     eh_context.  This function executes all the operations with
+ *     appropriate retrials and fallbacks to resurrect failed
+ *     devices, detach goners and greet newcomers.
  *
  *     LOCKING:
  *     Kernel thread context (may sleep).
@@ -1568,6 +1615,19 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                dev = &ap->device[i];
 
                ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
+
+               /* process hotplug request */
+               if (dev->flags & ATA_DFLAG_DETACH)
+                       ata_eh_detach_dev(dev);
+
+               if (!ata_dev_enabled(dev) &&
+                   ((ehc->i.probe_mask & (1 << dev->devno)) &&
+                    !(ehc->did_probe_mask & (1 << dev->devno)))) {
+                       ata_eh_detach_dev(dev);
+                       ata_dev_init(dev);
+                       ehc->did_probe_mask |= (1 << dev->devno);
+                       ehc->i.action |= ATA_EH_SOFTRESET;
+               }
        }
 
  retry:
@@ -1575,15 +1635,18 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
        rc = 0;
 
        /* skip EH if possible. */
-       if (!ata_port_nr_enabled(ap) && !(ap->flags & ATA_FLAG_FROZEN))
+       if (ata_eh_skip_recovery(ap))
                ehc->i.action = 0;
 
+       for (i = 0; i < ATA_MAX_DEVICES; i++)
+               ehc->classes[i] = ATA_DEV_UNKNOWN;
+
        /* reset */
        if (ehc->i.action & ATA_EH_RESET_MASK) {
                ata_eh_freeze_port(ap);
 
-               rc = ata_eh_reset(ap, 0, prereset, softreset, hardreset,
-                                 postreset);
+               rc = ata_eh_reset(ap, ata_port_nr_vacant(ap), prereset,
+                                 softreset, hardreset, postreset);
                if (rc) {
                        ata_port_printk(ap, KERN_ERR,
                                        "reset failed, giving up\n");
@@ -1593,8 +1656,8 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                ata_eh_thaw_port(ap);
        }
 
-       /* revalidate existing devices */
-       rc = ata_eh_revalidate(ap, &dev);
+       /* revalidate existing devices and attach new ones */
+       rc = ata_eh_revalidate_and_attach(ap, &dev);
        if (rc)
                goto dev_fail;
 
@@ -1612,6 +1675,8 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
  dev_fail:
        switch (rc) {
        case -ENODEV:
+               /* device missing, schedule probing */
+               ehc->i.probe_mask |= (1 << dev->devno);
        case -EINVAL:
                ehc->tries[dev->devno] = 0;
                break;
@@ -1624,15 +1689,31 @@ static int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,
                        ehc->tries[dev->devno] = 0;
        }
 
-       /* disable device if it has used up all its chances */
-       if (ata_dev_enabled(dev) && !ehc->tries[dev->devno])
+       if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) {
+               /* disable device if it has used up all its chances */
                ata_dev_disable(dev);
 
-       /* soft didn't work?  be haaaaard */
-       if (ehc->i.flags & ATA_EHI_DID_RESET)
-               ehc->i.action |= ATA_EH_HARDRESET;
-       else
-               ehc->i.action |= ATA_EH_SOFTRESET;
+               /* detach if offline */
+               if (ata_port_offline(ap))
+                       ata_eh_detach_dev(dev);
+
+               /* probe if requested */
+               if ((ehc->i.probe_mask & (1 << dev->devno)) &&
+                   !(ehc->did_probe_mask & (1 << dev->devno))) {
+                       ata_eh_detach_dev(dev);
+                       ata_dev_init(dev);
+
+                       ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;
+                       ehc->did_probe_mask |= (1 << dev->devno);
+                       ehc->i.action |= ATA_EH_SOFTRESET;
+               }
+       } else {
+               /* soft didn't work?  be haaaaard */
+               if (ehc->i.flags & ATA_EHI_DID_RESET)
+                       ehc->i.action |= ATA_EH_HARDRESET;
+               else
+                       ehc->i.action |= ATA_EH_SOFTRESET;
+       }
 
        if (ata_port_nr_enabled(ap)) {
                ata_port_printk(ap, KERN_WARNING, "failed to recover some "
This page took 0.028343 seconds and 5 git commands to generate.