#include "libata.h"
static void __ata_port_freeze(struct ata_port *ap);
+static void ata_eh_finish(struct ata_port *ap);
static void ata_ering_record(struct ata_ering *ering, int is_io,
unsigned int err_mask)
spin_unlock_irqrestore(hs_lock, flags);
- /* invoke EH */
- ap->ops->error_handler(ap);
+ /* invoke EH. if unloading, just finish failed qcs */
+ if (!(ap->flags & ATA_FLAG_UNLOADING))
+ ap->ops->error_handler(ap);
+ else
+ ata_eh_finish(ap);
/* Exception might have happend after ->error_handler
* recovered the port but before this point. Repeat
/* clean up */
spin_lock_irqsave(hs_lock, flags);
- if (ap->flags & ATA_FLAG_RECOVERED)
- ata_port_printk(ap, KERN_INFO, "EH complete\n");
- ap->flags &= ~ATA_FLAG_RECOVERED;
+ if (ap->flags & ATA_FLAG_LOADING) {
+ ap->flags &= ~ATA_FLAG_LOADING;
+ } else {
+ if (ap->flags & ATA_FLAG_SCSI_HOTPLUG)
+ queue_work(ata_aux_wq, &ap->hotplug_task);
+ if (ap->flags & ATA_FLAG_RECOVERED)
+ ata_port_printk(ap, KERN_INFO, "EH complete\n");
+ }
+
+ ap->flags &= ~(ATA_FLAG_SCSI_HOTPLUG | ATA_FLAG_RECOVERED);
/* tell wait_eh that we're done */
ap->flags &= ~ATA_FLAG_EH_IN_PROGRESS;
__ata_eh_qc_complete(qc);
}
+/**
+ * ata_eh_detach_dev - detach ATA device
+ * @dev: ATA device to detach
+ *
+ * Detach @dev.
+ *
+ * LOCKING:
+ * None.
+ */
+static void ata_eh_detach_dev(struct ata_device *dev)
+{
+ struct ata_port *ap = dev->ap;
+ unsigned long flags;
+
+ ata_dev_disable(dev);
+
+ spin_lock_irqsave(&ap->host_set->lock, flags);
+
+ dev->flags &= ~ATA_DFLAG_DETACH;
+
+ if (ata_scsi_offline_dev(dev)) {
+ dev->flags |= ATA_DFLAG_DETACHED;
+ ap->flags |= ATA_FLAG_SCSI_HOTPLUG;
+ }
+
+ spin_unlock_irqrestore(&ap->host_set->lock, flags);
+}
+
/**
* ata_eh_about_to_do - about to perform eh_action
* @ap: target ATA port
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;
}
}
-static int ata_eh_reset(struct ata_port *ap,
+static int ata_eh_followup_srst_needed(int rc, int classify,
+ const unsigned int *classes)
+{
+ if (rc == -EAGAIN)
+ return 1;
+ if (rc != 0)
+ return 0;
+ if (classify && classes[0] == ATA_DEV_UNKNOWN)
+ return 1;
+ return 0;
+}
+
+static int ata_eh_reset(struct ata_port *ap, int classify,
ata_prereset_fn_t prereset, ata_reset_fn_t softreset,
ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
{
struct ata_eh_context *ehc = &ap->eh_context;
- unsigned int classes[ATA_MAX_DEVICES];
+ unsigned int *classes = ehc->classes;
int tries = ATA_EH_RESET_TRIES;
+ int verbose = !(ap->flags & ATA_FLAG_LOADING);
unsigned int action;
ata_reset_fn_t reset;
- int i, rc;
+ int i, did_followup_srst, rc;
/* Determine which reset to use and record in ehc->i.action.
* prereset() may examine and modify it.
}
retry:
- ata_port_printk(ap, KERN_INFO, "%s resetting port\n",
- reset == softreset ? "soft" : "hard");
+ /* shut up during boot probing */
+ if (verbose)
+ ata_port_printk(ap, KERN_INFO, "%s resetting port\n",
+ reset == softreset ? "soft" : "hard");
/* reset */
ata_eh_about_to_do(ap, ATA_EH_RESET_MASK);
rc = ata_do_reset(ap, reset, classes);
+ did_followup_srst = 0;
+ if (reset == hardreset &&
+ ata_eh_followup_srst_needed(rc, classify, classes)) {
+ /* okay, let's do follow-up softreset */
+ did_followup_srst = 1;
+ reset = softreset;
+
+ if (!reset) {
+ ata_port_printk(ap, KERN_ERR,
+ "follow-up softreset required "
+ "but no softreset avaliable\n");
+ return -EINVAL;
+ }
+
+ ata_eh_about_to_do(ap, ATA_EH_RESET_MASK);
+ rc = ata_do_reset(ap, reset, classes);
+
+ if (rc == 0 && classify &&
+ classes[0] == ATA_DEV_UNKNOWN) {
+ ata_port_printk(ap, KERN_ERR,
+ "classification failed\n");
+ return -EINVAL;
+ }
+ }
+
if (rc && --tries) {
+ const char *type;
+
+ if (reset == softreset) {
+ if (did_followup_srst)
+ type = "follow-up soft";
+ else
+ type = "soft";
+ } else
+ type = "hard";
+
ata_port_printk(ap, KERN_WARNING,
- "%sreset failed, retrying in 5 secs\n",
- reset == softreset ? "soft" : "hard");
+ "%sreset failed, retrying in 5 secs\n", type);
ssleep(5);
if (reset == hardreset)
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");
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);
}
}
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
*
* 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).
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:
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, 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");
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;
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;
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 "
ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
ata_postreset_fn_t postreset)
{
- ata_eh_autopsy(ap);
- ata_eh_report(ap);
+ if (!(ap->flags & ATA_FLAG_LOADING)) {
+ ata_eh_autopsy(ap);
+ ata_eh_report(ap);
+ }
+
ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
ata_eh_finish(ap);
}