kern/115337: [ata] Promise SATA300 TX4 breaks with zfs in 7.0-current

Alexander Sabourenkov screwdriver at lxnt.info
Thu Nov 1 14:50:03 PDT 2007


The following reply was made to PR kern/115337; it has been noted by GNATS.

From: Alexander Sabourenkov <screwdriver at lxnt.info>
To: bug-followup at FreeBSD.org
Cc: ss.alert at online.no
Subject: Re: kern/115337: [ata] Promise SATA300 TX4 breaks with zfs in 7.0-current
Date: Fri, 02 Nov 2007 01:37:18 +0300

 I have ported the workaround for the hardware bug that causes data
 corruption on Promise SATA300 TX4 cards to RELENG_7.
 
 Bug description:
 SATA300 TX4 hardware chokes if last PRD entry (in a dma transfer) is
 larger than 164 bytes. This was found while analysing vendor-supplied
 linux driver.
 
 Workaround:
 Split trailing PRD entry if it's larger that 164 bytes.
 
 Two supplied patches do fix problem on my machine.
 
 There is, however, a style problem with them. It seems like PRD entry
 count is limited at 256. I have not found a good way to guarantee that
 one entry is always available to do the split, thus the ugly solution of
 patching ata-dma.c.
 
 
 Patches, patched and original files are at http://lxnt.info/tx4/freebsd/.
 
 
 --- ata-chipset.c.orig	2007-11-02 01:05:49.000000000 +0300
 +++ ata-chipset.c	2007-11-02 01:05:49.000000000 +0300
 @@ -142,6 +142,7 @@
  static int ata_promise_mio_command(struct ata_request *request);
  static void ata_promise_mio_reset(device_t dev);
  static void ata_promise_mio_dmainit(device_t dev);
 +static void ata_promise_mio_dmasetprd(void *xsc, bus_dma_segment_t
 *segs, int nsegs, int error);
  static void ata_promise_mio_setmode(device_t dev, int mode);
  static void ata_promise_sx4_intr(void *data);
  static int ata_promise_sx4_command(struct ata_request *request);
 @@ -185,7 +186,6 @@
  static int ata_check_80pin(device_t dev, int mode);
  static int ata_mode2idx(int mode);
 
 -
  /*
   * generic ATA support functions
   */
 @@ -3759,8 +3759,44 @@
  static void
  ata_promise_mio_dmainit(device_t dev)
  {
 +    struct ata_channel *ch = device_get_softc(dev);
 +	
      /* note start and stop are not used here */
      ata_dmainit(dev);
 +
 +    if (ch->dma)
 +	ch->dma->setprd = ata_promise_mio_dmasetprd;
 +}
 +
 +static void
 +ata_promise_mio_dmasetprd(void *xsc, bus_dma_segment_t *segs, int
 nsegs, int error)
 +{
 +    #define PDC_MAXLASTSGSIZE 41*4
 +    struct ata_dmasetprd_args *args = xsc;
 +    struct ata_dma_prdentry *prd = args->dmatab;
 +    int i;
 +
 +    if ((args->error = error))
 +	return;
 +
 +    for (i = 0; i < nsegs; i++) {
 +	prd[i].addr = htole32(segs[i].ds_addr);
 +	prd[i].count = htole32(segs[i].ds_len);
 +    }
 +
 +    if (segs[i - 1].ds_len > PDC_MAXLASTSGSIZE) {
 +	/*
 +	printf("splitting trailing PRD of %ld (limit %d)\n", segs[i -
 1].ds_len, PDC_MAXLASTSGSIZE);
 +	*/
 +	prd[i - 1].count = htole32(segs[i - 1].ds_len - PDC_MAXLASTSGSIZE);
 +	prd[i].count = htole32(PDC_MAXLASTSGSIZE);
 +	prd[i].addr = htole32(segs[i - 1].ds_addr + PDC_MAXLASTSGSIZE);
 +	i ++;
 +	nsegs ++;
 +    }
 +
 +    prd[i - 1].count |= htole32(ATA_DMA_EOT);
 +    args->nsegs = nsegs;
  }
 
  static void
 
 --- ata-dma.c.orig	2007-11-02 01:05:53.000000000 +0300
 +++ ata-dma.c	2007-11-02 01:05:53.000000000 +0300
 @@ -113,7 +113,7 @@
      if
 (bus_dma_tag_create(ch->dma->dmatag,ch->dma->alignment,ch->dma->boundary,
  			   ch->dma->max_address, BUS_SPACE_MAXADDR,
  			   NULL, NULL, ch->dma->max_iosize,
 -			   ATA_DMA_ENTRIES, ch->dma->segsize,
 +			   ATA_DMA_ENTRIES - 1, ch->dma->segsize,
  			   0, NULL, NULL, &ch->dma->data_tag))
  	goto error;
 
 
 -- 
 
 ./lxnt
 


More information about the freebsd-bugs mailing list