summaryrefslogtreecommitdiff
path: root/drivers/scsi/libata-eh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libata-eh.c')
-rw-r--r--drivers/scsi/libata-eh.c60
1 files changed, 51 insertions, 9 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 9173d8f2ce5d..0e66f140e53b 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -1318,20 +1318,58 @@ static void ata_eh_report(struct ata_port *ap)
}
}
-static int ata_eh_reset(struct ata_port *ap, ata_reset_fn_t softreset,
+static int ata_eh_reset(struct ata_port *ap,
+ 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];
int tries = ATA_EH_RESET_TRIES;
+ unsigned int action;
ata_reset_fn_t reset;
int i, rc;
+ /* Determine which reset to use and record in ehc->i.action.
+ * prereset() may examine and modify it.
+ */
+ action = ehc->i.action;
+ ehc->i.action &= ~ATA_EH_RESET_MASK;
if (softreset && (!hardreset || (!sata_set_spd_needed(ap) &&
- !(ehc->i.action & ATA_EH_HARDRESET))))
- reset = softreset;
+ !(action & ATA_EH_HARDRESET))))
+ ehc->i.action |= ATA_EH_SOFTRESET;
else
+ ehc->i.action |= ATA_EH_HARDRESET;
+
+ if (prereset) {
+ rc = prereset(ap);
+ if (rc) {
+ ata_port_printk(ap, KERN_ERR,
+ "prereset failed (errno=%d)\n", rc);
+ return rc;
+ }
+ }
+
+ /* prereset() might have modified ehc->i.action */
+ if (ehc->i.action & ATA_EH_HARDRESET)
reset = hardreset;
+ else if (ehc->i.action & ATA_EH_SOFTRESET)
+ reset = softreset;
+ else {
+ /* prereset told us not to reset, bang classes and return */
+ for (i = 0; i < ATA_MAX_DEVICES; i++)
+ classes[i] = ATA_DEV_NONE;
+ return 0;
+ }
+
+ /* did prereset() screw up? if so, fix up to avoid oopsing */
+ if (!reset) {
+ ata_port_printk(ap, KERN_ERR, "BUG: prereset() requested "
+ "invalid reset type\n");
+ if (softreset)
+ reset = softreset;
+ else
+ reset = hardreset;
+ }
retry:
ata_port_printk(ap, KERN_INFO, "%s resetting port\n",
@@ -1424,6 +1462,7 @@ static int ata_port_nr_enabled(struct ata_port *ap)
/**
* ata_eh_recover - recover host port after error
* @ap: host port to recover
+ * @prereset: prereset method (can be NULL)
* @softreset: softreset method (can be NULL)
* @hardreset: hardreset method (can be NULL)
* @postreset: postreset method (can be NULL)
@@ -1440,8 +1479,8 @@ static int ata_port_nr_enabled(struct ata_port *ap)
* RETURNS:
* 0 on success, -errno on failure.
*/
-static int ata_eh_recover(struct ata_port *ap, ata_reset_fn_t softreset,
- ata_reset_fn_t hardreset,
+static int ata_eh_recover(struct ata_port *ap, 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;
@@ -1469,7 +1508,8 @@ static int ata_eh_recover(struct ata_port *ap, ata_reset_fn_t softreset,
if (ehc->i.action & ATA_EH_RESET_MASK) {
ata_eh_freeze_port(ap);
- rc = ata_eh_reset(ap, softreset, hardreset, postreset);
+ rc = ata_eh_reset(ap, prereset, softreset, hardreset,
+ postreset);
if (rc) {
ata_port_printk(ap, KERN_ERR,
"reset failed, giving up\n");
@@ -1586,6 +1626,7 @@ static void ata_eh_finish(struct ata_port *ap)
/**
* ata_do_eh - do standard error handling
* @ap: host port to handle error for
+ * @prereset: prereset method (can be NULL)
* @softreset: softreset method (can be NULL)
* @hardreset: hardreset method (can be NULL)
* @postreset: postreset method (can be NULL)
@@ -1595,11 +1636,12 @@ static void ata_eh_finish(struct ata_port *ap)
* LOCKING:
* Kernel thread context (may sleep).
*/
-void ata_do_eh(struct ata_port *ap, ata_reset_fn_t softreset,
- ata_reset_fn_t hardreset, ata_postreset_fn_t postreset)
+void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset,
+ ata_reset_fn_t softreset, ata_reset_fn_t hardreset,
+ ata_postreset_fn_t postreset)
{
ata_eh_autopsy(ap);
ata_eh_report(ap);
- ata_eh_recover(ap, softreset, hardreset, postreset);
+ ata_eh_recover(ap, prereset, softreset, hardreset, postreset);
ata_eh_finish(ap);
}