DF-3607 / fix.diff
--- a/sys/dev/disk/ncr/ncr.c +++ b/sys/dev/disk/ncr/ncr.c @@ -5165,6 +5165,18 @@ ncr_timeout (void *arg) if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) { kprintf ("%s: timeout nccb=%p (skip)\n", ncr_name (np), cp); cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip); } + + /* + ** DF-3607: If the script processor is still + ** connected with this nccb (DSA points at it), + ** completing it here would leave the chip armed + ** with a dead data structure. Recover the hard + ** way: reset the bus so ncr_init() invalidates + ** the chip state BEFORE completing the jobs. + */ + if (INL (nc_dsa) == CCB_PHYS (cp, phys)) { + kprintf ("%s: timeout on connected nccb=%p," + " resetting bus\n", ncr_name (np), cp); + ncr_init (np, "connected ccb timeout", HS_FAIL); + break; + } switch (cp->host_status) { case HS_BUSY: @@ -5701,6 +5713,16 @@ static void ncr_int_ma (ncb_p np, u_char dstat) if (cp != np->header.cp) { kprintf ("%s: SCSI phase error fixup: CCB address mismatch " "(%p != %p) np->nccb = %p\n", ncr_name (np), (void *)cp, (void *)np->header.cp, (void *)np->link_nccb); /* return;*/ } + + /* + ** DF-3607: never restart the script processor from an nccb that + ** has already been completed (e.g. by the timeout daemon) and + ** possibly re-used for another command. Resuming would DMA from + ** stale scatter descriptors into released buffers. + */ + if (cp->magic != CCB_MAGIC || cp->ccb == NULL) { + kprintf ("%s: SCSI phase error fixup on dead nccb (%p)," + " resetting bus\n", ncr_name (np), cp); + ncr_init (np, "phase fixup on dead ccb", HS_FAIL); + return; + } /* ** find the interrupted script command, ** and the address at which to continue. |