When an error is reported on a write, data may have been transfered

to the device's cache anyway and so cmdh_prdbc reports a completed
transfer. If we use it to update ata_bio->bcount this has 2 conseqences:
- the automatic LBA48 workaround doesn't qick in because bcount is used
  to compute the last sector of the transfer (wd(4) part of kern/40569)
- wd(4) will report a B_ERROR buffer with a b_resid of 0, which panics
  a DIAGNOSTIC kernel
Fix by ignoring cmdh_prdbc if we had a write with errors, and in this case
leave ata_bio->bcount at its initial value.

While there use NOERROR instead of 0 for ata_bio->error (cosmetic).

thanks to Matthias Scheler for tests.
This commit is contained in:
bouyer 2009-02-12 11:44:11 +00:00
parent d237abe695
commit c54134b00b

View File

@ -1,4 +1,4 @@
/* $NetBSD: ahcisata_core.c,v 1.18 2008/10/03 13:02:08 bouyer Exp $ */
/* $NetBSD: ahcisata_core.c,v 1.19 2009/02/12 11:44:11 bouyer Exp $ */
/*
* Copyright (c) 2006 Manuel Bouyer.
@ -31,7 +31,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.18 2008/10/03 13:02:08 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: ahcisata_core.c,v 1.19 2009/02/12 11:44:11 bouyer Exp $");
#include <sys/types.h>
#include <sys/malloc.h>
@ -1065,7 +1065,7 @@ ahci_bio_complete(struct ata_channel *chp, struct ata_xfer *xfer, int is)
ata_bio->error = TIMEOUT;
} else {
callout_stop(&chp->ch_callout);
ata_bio->error = 0;
ata_bio->error = NOERROR;
}
chp->ch_queue->active_xfer = NULL;
@ -1095,7 +1095,14 @@ ahci_bio_complete(struct ata_channel *chp, struct ata_xfer *xfer, int is)
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
AHCIDEBUG_PRINT(("ahci_bio_complete bcount %ld",
ata_bio->bcount), DEBUG_XFERS);
ata_bio->bcount -= le32toh(achp->ahcic_cmdh[slot].cmdh_prdbc);
/*
* if it was a write, complete data buffer may have been transfered
* before error detection; in this case don't use cmdh_prdbc
* as it won't reflect what was written to media. Assume nothing
* was transfered and leave bcount as-is.
*/
if ((ata_bio->flags & ATA_READ) || ata_bio->error == NOERROR)
ata_bio->bcount -= le32toh(achp->ahcic_cmdh[slot].cmdh_prdbc);
AHCIDEBUG_PRINT((" now %ld\n", ata_bio->bcount), DEBUG_XFERS);
(*chp->ch_drive[drive].drv_done)(chp->ch_drive[drive].drv_softc);
atastart(chp);