On Fri, Nov 05, 2010 at 11:40:00PM +0100, Mark Kettenis wrote: > Many older revisions of the Acer Labs M5229 UDMA IDE controller have a > hardware bug that makes DMA fail for LBA48 commands fail. As a result > accessing data on disks bigger than 137 GB beyond the 137 GB boundary > will fail. A workaround for this is to fall back to PIO, but this is > quite a bit slower than DMA (3 MB/s vs. 15 MB/s on my setup). A much > better approach would be to use DMA for everything below the 137 GB > boundary and PIO for everything above. I never found an elegant way > to do that though. > > Recently, Takeshi Nakayama did post a diff to the NetBSD sparc64 > mailing list that has a fairly elegant way to fix this: > > http://mail-index.netbsd.org/port-sparc64/2010/10/26/msg001398.html > > Here's the equivalent diff for our tree. With this diff, I can access > everything on a 160 GB disk. Accessing the tail end is quite a bit > slower than the start of the disk. For that reason NetBSD prints a > warning in dmesg. I chose not to do that for now, but I can probably > be convinced to add a printf here. > > ok? > > > Index: ata/ata_wdc.c > =================================================================== > RCS file: /cvs/src/sys/dev/ata/ata_wdc.c,v > retrieving revision 1.34 > diff -u -p -r1.34 ata_wdc.c > --- ata/ata_wdc.c 23 Jul 2010 07:47:12 -0000 1.34 > +++ ata/ata_wdc.c 5 Nov 2010 22:18:48 -0000 > @@ -167,7 +167,7 @@ _wdc_ata_bio_start(struct channel_softc > u_int8_t head, sect, cmd = 0; > int nblks; > int ata_delay; > - int dma_flags = 0; > + int error, dma_flags = 0; > > WDCDEBUG_PRINT(("_wdc_ata_bio_start %s:%d:%d\n", > chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), > @@ -249,10 +249,21 @@ again: > cmd = (ata_bio->flags & ATA_READ) ? > WDCC_READDMA : WDCC_WRITEDMA; > /* Init the DMA channel. */ > - if ((*chp->wdc->dma_init)(chp->wdc->dma_arg, > + error = (*chp->wdc->dma_init)(chp->wdc->dma_arg, > chp->channel, xfer->drive, > (char *)xfer->databuf + xfer->c_skip, > - ata_bio->nbytes, dma_flags) != 0) { > + ata_bio->nbytes, dma_flags); > + if (error) { > + if (error == EINVAL) { > + /* > + * We can't do DMA on this transfer > + * for some reason. Fall back to > + * PIO. > + */ > + xfer->c_flags &= ~C_DMA; > + error = 0; > + goto do_pio; > + } > ata_bio->error = ERR_DMA; > ata_bio->r_error = 0; > wdc_ata_bio_done(chp, xfer); > @@ -276,6 +287,7 @@ again: > /* wait for irq */ > goto intr; > } /* else not DMA */ > + do_pio: > ata_bio->nblks = min(nblks, ata_bio->multi); > ata_bio->nbytes = ata_bio->nblks * ata_bio->lp->d_secsize; > KASSERT(nblks == 1 || (ata_bio->flags & ATA_SINGLE) == 0); > Index: pci/pciide.c > =================================================================== > RCS file: /cvs/src/sys/dev/pci/pciide.c,v > retrieving revision 1.321 > diff -u -p -r1.321 pciide.c > --- pci/pciide.c 31 Aug 2010 17:13:44 -0000 1.321 > +++ pci/pciide.c 5 Nov 2010 22:18:51 -0000 > @@ -215,6 +215,7 @@ void ns_scx200_setup_channel(struct chan > void acer_chip_map(struct pciide_softc *, struct pci_attach_args *); > void acer_setup_channel(struct channel_softc *); > int acer_pci_intr(void *); > +int acer_dma_init(void *, int, int, void *, size_t, int); > > void pdc202xx_chip_map(struct pciide_softc *, struct pci_attach_args *); > void pdc202xx_setup_channel(struct channel_softc *); > @@ -5629,6 +5630,8 @@ acer_chip_map(struct pciide_softc *sc, s > } > sc->sc_wdcdev.cap |= WDC_CAPABILITY_IRQACK; > sc->sc_wdcdev.irqack = pciide_irqack; > + if (rev <= 0xC4) > + sc->sc_wdcdev.dma_init = acer_dma_init; > } > > sc->sc_wdcdev.PIO_cap = 4; > @@ -5821,6 +5824,17 @@ acer_pci_intr(void *arg) > } > } > return (rv); > +} > + > +int > +acer_dma_init(void *v, int channel, int drive, void *databuf, > + size_t datalen, int flags) > +{ > + /* Use PIO for LBA48 transfers. */ > + if (flags & WDC_DMA_LBA48) > + return (EINVAL); > + > + return (pciide_dma_init(v, channel, drive, databuf, datalen, flags)); > } > > void >
I don't have one to test, but that reads good to me. ok krw@ .... Ken
