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

Reply via email to