Subject: Re: port-mac68k/9679 -- Using scsi/libscsi lets mac68k sbc driver dump core
To: Dave Huang <khym@azeotrope.org>
From: Chuck Silvers <chuq@chuq.com>
List: netbsd-bugs
Date: 01/14/2006 12:02:51
--sdtB3X0nJg68CQEu
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Wed, Jan 11, 2006 at 06:24:43PM -0600, Dave Huang wrote:
> I wonder if the sbc driver has problems if it's asked to read more
> data than is actually available. The scsi utility is attempting to
> read 255 bytes, even though chances are that the mode page isn't that
> long.

well, it's part of the problem.  the sbc_pdma_in() is expecting to read
255 bytes, so is does the PDMA thing instead of PIO.  since datalen is
more than 128, it tries to do the first 128 bytes using 4-byte reads.
however, there are only 35 bytes of data, so when it tries to read the
ninth 4-byte value it gets a bus error since there are only 3 bytes left.

so unless there's some way to know ahead of time how many bytes will
actually be read, it's not safe to use the 4-byte reads.  all of the
other sbc PDMA data movers use the "nofault" hook to catch bus errors,
and sbc_pdma_in() should too.  the attached patch will at least prevent
the panic, though it will miss transferring the last bytes if the number
of bytes transferred is not a multiple of 4.

we could try falling back to the byte-by-byte loop after a bus error on
the unrolled loop, does that seem likely to work?

-Chuck

--sdtB3X0nJg68CQEu
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="diff.sbc"

Index: arch/mac68k/dev/sbc.c
===================================================================
RCS file: /cvsroot/src/sys/arch/mac68k/dev/sbc.c,v
retrieving revision 1.48
diff -u -p -r1.48 sbc.c
--- arch/mac68k/dev/sbc.c	24 Dec 2005 23:24:00 -0000	1.48
+++ arch/mac68k/dev/sbc.c	14 Jan 2006 19:42:40 -0000
@@ -246,6 +246,7 @@ sbc_pdma_in(struct ncr5380_softc *ncr_sc
 	struct sbc_softc *sc = (struct sbc_softc *)ncr_sc;
 	volatile u_int32_t *long_data = (u_int32_t *)sc->sc_drq_addr;
 	volatile u_int8_t *byte_data = (u_int8_t *)sc->sc_nodrq_addr;
+	label_t faultbuf;
 	int resid, s;
 
 	if (datalen < ncr_sc->sc_min_dma_len ||
@@ -261,9 +262,22 @@ sbc_pdma_in(struct ncr5380_softc *ncr_sc
 	*ncr_sc->sci_mode |= SCI_MODE_DMA;
 	*ncr_sc->sci_irecv = 0;
 
+	resid = datalen;
+
+	/*
+	 * Setup for a possible bus error caused by SCSI controller
+	 * switching out of DATA OUT before we're done with the
+	 * current transfer.  (See comment before sbc_drq_intr().)
+	 */
+	nofault = &faultbuf;
+	if (setjmp(nofault)) {
+		printf("sbc_pdma_in: caught bus error\n");
+		goto interrupt;
+	}
+
 #define R4	*((u_int32_t *)data)++ = *long_data
 #define R1	*((u_int8_t *)data)++ = *byte_data
-	for (resid = datalen; resid >= 128; resid -= 128) {
+	for (; resid >= 128; resid -= 128) {
 		if (sbc_ready(ncr_sc))
 			goto interrupt;
 		R4; R4; R4; R4; R4; R4; R4; R4;
@@ -281,6 +295,7 @@ sbc_pdma_in(struct ncr5380_softc *ncr_sc
 #undef R1
 
 interrupt:
+	nofault = NULL;
 	SCI_CLR_INTR(ncr_sc);
 	*ncr_sc->sci_mode &= ~SCI_MODE_DMA;
 	*ncr_sc->sci_icmd = 0;

--sdtB3X0nJg68CQEu--