summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Lord <liml@rtr.ca>2006-05-19 16:21:03 -0400
committerJeff Garzik <jeff@garzik.org>2006-05-20 00:31:45 -0400
commit9b358e305c1d783c8a4ebf00344e95deb9e38f3d (patch)
treeb1cfece40d968befb1df17661282471ddf59a9dc
parent2f880b65fdbc2d4915bddc59d75a176329570fdd (diff)
[PATCH] sata_mv: prevent unnecessary double-resets
The mv_err_intr() function is invoked from the driver's interrupt handler, as well as from the timeout function. This patch prevents it from triggering a one-after-the-other double reset of the controller when invoked from the timeout function. This also adds a check for a timeout race condition that has been observed to occur with this driver in earlier kernels. This should not be needed, in theory, but in practice it has caught bugs. Maybe nuke it at a later date. Signed-off-by: Mark Lord <liml@rtr.ca> Signed-off-by: Jeff Garzik <jeff@garzik.org>
-rw-r--r--drivers/scsi/sata_mv.c17
1 files changed, 10 insertions, 7 deletions
diff --git a/drivers/scsi/sata_mv.c b/drivers/scsi/sata_mv.c
index d5fdcb9a8842..87f26cd60fae 100644
--- a/drivers/scsi/sata_mv.c
+++ b/drivers/scsi/sata_mv.c
@@ -1291,6 +1291,7 @@ static u8 mv_get_crpb_status(struct ata_port *ap)
/**
* mv_err_intr - Handle error interrupts on the port
* @ap: ATA channel to manipulate
+ * @reset_allowed: bool: 0 == don't trigger from reset here
*
* In most cases, just clear the interrupt and move on. However,
* some cases require an eDMA reset, which is done right before
@@ -1301,7 +1302,7 @@ static u8 mv_get_crpb_status(struct ata_port *ap)
* LOCKING:
* Inherited from caller.
*/
-static void mv_err_intr(struct ata_port *ap)
+static void mv_err_intr(struct ata_port *ap, int reset_allowed)
{
void __iomem *port_mmio = mv_ap_base(ap);
u32 edma_err_cause, serr = 0;
@@ -1323,9 +1324,8 @@ static void mv_err_intr(struct ata_port *ap)
writelfl(0, port_mmio + EDMA_ERR_IRQ_CAUSE_OFS);
/* check for fatal here and recover if needed */
- if (EDMA_ERR_FATAL & edma_err_cause) {
+ if (reset_allowed && (EDMA_ERR_FATAL & edma_err_cause))
mv_stop_and_reset(ap);
- }
}
/**
@@ -1406,7 +1406,7 @@ static void mv_host_intr(struct ata_host_set *host_set, u32 relevant,
shift++; /* skip bit 8 in the HC Main IRQ reg */
}
if ((PORT0_ERR << shift) & relevant) {
- mv_err_intr(ap);
+ mv_err_intr(ap, 1);
err_mask |= AC_ERR_OTHER;
handled = 1;
}
@@ -2031,11 +2031,14 @@ static void mv_eng_timeout(struct ata_port *ap)
ap->host_set->mmio_base, ap, qc, qc->scsicmd,
&qc->scsicmd->cmnd);
- mv_err_intr(ap);
+ mv_err_intr(ap, 0);
mv_stop_and_reset(ap);
- qc->err_mask |= AC_ERR_TIMEOUT;
- ata_eh_qc_complete(qc);
+ WARN_ON(!(qc->flags & ATA_QCFLAG_ACTIVE));
+ if (qc->flags & ATA_QCFLAG_ACTIVE) {
+ qc->err_mask |= AC_ERR_TIMEOUT;
+ ata_eh_qc_complete(qc);
+ }
}
/**