diff options
author | Michael Shalayeff <mickey@cvs.openbsd.org> | 2003-03-06 22:31:24 +0000 |
---|---|---|
committer | Michael Shalayeff <mickey@cvs.openbsd.org> | 2003-03-06 22:31:24 +0000 |
commit | 1e7b5aa559ece45586cc7360ac3a7c84c61a13b9 (patch) | |
tree | 57cc26764e29829795791d417bd605b81387823e /sys/dev/ic/cac.c | |
parent | 0e2adb3170759081e98d14c803364105ba154cbc (diff) |
this is a fix for kernel panics and craches along w/ other stability
improvements worked out over past few months w/ different people.
the main bug was fixes w/ help of harding@motd.ca .
10x to all and enjoy the better driver (;
Diffstat (limited to 'sys/dev/ic/cac.c')
-rw-r--r-- | sys/dev/ic/cac.c | 167 |
1 files changed, 77 insertions, 90 deletions
diff --git a/sys/dev/ic/cac.c b/sys/dev/ic/cac.c index 5d56772ddf8..ade55c7949d 100644 --- a/sys/dev/ic/cac.c +++ b/sys/dev/ic/cac.c @@ -1,4 +1,4 @@ -/* $OpenBSD: cac.c,v 1.11 2002/03/14 01:26:54 millert Exp $ */ +/* $OpenBSD: cac.c,v 1.12 2003/03/06 22:31:21 mickey Exp $ */ /* $NetBSD: cac.c,v 1.15 2000/11/08 19:20:35 ad Exp $ */ /* @@ -150,37 +150,36 @@ cac_init(struct cac_softc *sc, int startfw) { struct cac_controller_info cinfo; int error, rseg, size, i; - bus_dma_segment_t seg; + bus_dma_segment_t seg[1]; struct cac_ccb *ccb; - + SIMPLEQ_INIT(&sc->sc_ccb_free); SIMPLEQ_INIT(&sc->sc_ccb_queue); size = sizeof(struct cac_ccb) * CAC_MAX_CCBS; - if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &seg, 1, + if ((error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, seg, 1, &rseg, BUS_DMA_NOWAIT)) != 0) { printf("%s: unable to allocate CCBs, error = %d\n", sc->sc_dv.dv_xname, error); return (-1); } - if ((error = bus_dmamem_map(sc->sc_dmat, &seg, rseg, size, - (caddr_t *)&sc->sc_ccbs, - BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + if ((error = bus_dmamem_map(sc->sc_dmat, seg, rseg, size, + &sc->sc_ccbs, BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { printf("%s: unable to map CCBs, error = %d\n", sc->sc_dv.dv_xname, error); return (-1); } - if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + if ((error = bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, BUS_DMA_NOWAIT, &sc->sc_dmamap)) != 0) { printf("%s: unable to create CCB DMA map, error = %d\n", sc->sc_dv.dv_xname, error); return (-1); } - if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, + if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, sc->sc_ccbs, size, NULL, BUS_DMA_NOWAIT)) != 0) { printf("%s: unable to load CCB DMA map, error = %d\n", sc->sc_dv.dv_xname, error); @@ -199,16 +198,15 @@ cac_init(struct cac_softc *sc, int startfw) &ccb->ccb_dmamap_xfer); if (error) { - printf("%s: can't create ccb dmamap (%d)\n", + printf("%s: can't create ccb dmamap (%d)\n", sc->sc_dv.dv_xname, error); break; } - ccb->ccb_flags = 0; ccb->ccb_paddr = sc->sc_ccbs_paddr + i * sizeof(struct cac_ccb); SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_free, ccb, ccb_chain); } - + /* Start firmware background tasks, if needed. */ if (startfw) { if (cac_cmd(sc, CAC_CMD_START_FIRMWARE, &cinfo, sizeof(cinfo), @@ -219,9 +217,9 @@ cac_init(struct cac_softc *sc, int startfw) } } - if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, + if (cac_cmd(sc, CAC_CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo), 0, 0, CAC_CCB_DATA_IN, NULL)) { - printf("%s: CAC_CMD_GET_CTRL_INFO failed\n", + printf("%s: CAC_CMD_GET_CTRL_INFO failed\n", sc->sc_dv.dv_xname); return (-1); } @@ -255,8 +253,7 @@ cac_init(struct cac_softc *sc, int startfw) /* Set our `shutdownhook' before we start any device activity. */ if (cac_sdh == NULL) cac_sdh = shutdownhook_establish(cac_shutdown, NULL); - - (*sc->sc_cl->cl_intr_enable)(sc, 1); + return (0); } @@ -268,7 +265,7 @@ cac_flush(sc) memset(buf, 0, sizeof(buf)); buf[0] = 1; - return cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0, + return cac_cmd(sc, CAC_CMD_FLUSH_CACHE, buf, sizeof(buf), 0, 0, CAC_CCB_DATA_OUT, NULL); } @@ -284,10 +281,10 @@ cac_shutdown(void *cookie) for (i = 0; i < cac_cd.cd_ndevs; i++) { if ((sc = (struct cac_softc *)device_lookup(&cac_cd, i)) == NULL) - continue; + continue; cac_flush(sc); } -} +} /* * Handle an interrupt from the controller: process finished CCBs and @@ -299,16 +296,18 @@ cac_intr(v) { struct cac_softc *sc = v; struct cac_ccb *ccb; - int ret = 0; + int istat, ret = 0; - if (!(sc->sc_cl->cl_intr_pending)(sc)) + if (!(istat = (sc->sc_cl->cl_intr_pending)(sc))) return 0; - while ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) { - ret++; - cac_ccb_done(sc, ccb); - cac_ccb_start(sc, NULL); - } + (*sc->sc_cl->cl_intr_enable)(sc, 0); + if (istat & CAC_INTR_FIFO_NEMPTY) + while ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) { + ret = 1; + cac_ccb_done(sc, ccb); + } + cac_ccb_start(sc, NULL); return (ret); } @@ -342,16 +341,18 @@ cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, ccb->ccb_dmamap_xfer->dm_mapsize, (flags & CAC_CCB_DATA_IN) != 0 ? BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); - + sgb = ccb->ccb_seg; - nsegs = min(ccb->ccb_dmamap_xfer->dm_nsegs, CAC_SG_SIZE); + nsegs = ccb->ccb_dmamap_xfer->dm_nsegs; + if (nsegs > CAC_SG_SIZE) + panic("cac_cmd: nsegs botch"); size = 0; for (i = 0; i < nsegs; i++, sgb++) { size += ccb->ccb_dmamap_xfer->dm_segs[i].ds_len; - sgb->length = + sgb->length = htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_len); - sgb->addr = + sgb->addr = htole32(ccb->ccb_dmamap_xfer->dm_segs[i].ds_addr); } } else { @@ -360,22 +361,24 @@ cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, } ccb->ccb_hdr.drive = drive; - ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + + ccb->ccb_hdr.priority = 0; + ccb->ccb_hdr.size = htole16((sizeof(struct cac_req) + sizeof(struct cac_sgb) * CAC_SG_SIZE) >> 2); - ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); + ccb->ccb_req.next = 0; ccb->ccb_req.command = command; - ccb->ccb_req.sgcount = nsegs; + ccb->ccb_req.error = 0; ccb->ccb_req.blkno = htole32(blkno); - + ccb->ccb_req.bcount = htole16(howmany(size, DEV_BSIZE)); + ccb->ccb_req.sgcount = nsegs; + ccb->ccb_req.reserved = 0; + ccb->ccb_flags = flags; ccb->ccb_datasize = size; ccb->ccb_xs = xs; + (*sc->sc_cl->cl_intr_enable)(sc, 0); if (!xs || xs->flags & SCSI_POLL) { - int s; - - s = splbio(); /* Synchronous commands musn't wait. */ if ((*sc->sc_cl->cl_fifo_full)(sc)) { @@ -386,10 +389,10 @@ cac_cmd(struct cac_softc *sc, int command, void *data, int datasize, (*sc->sc_cl->cl_submit)(sc, ccb); rv = cac_ccb_poll(sc, ccb, 2000); } - splx(s); + (*sc->sc_cl->cl_intr_enable)(sc, 1); } else rv = cac_ccb_start(sc, ccb); - + return (rv); } @@ -400,17 +403,13 @@ int cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) { struct cac_ccb *ccb; - - timo *= 10; + int t = timo * 10; do { - for (; timo > 0; timo--) { + for (; t--; DELAY(100)) if ((ccb = (*sc->sc_cl->cl_completed)(sc)) != NULL) break; - DELAY(100); - } - - if (timo <= 0) { + if (t < 0) { printf("%s: timeout\n", sc->sc_dv.dv_xname); return (EBUSY); } @@ -421,24 +420,24 @@ cac_ccb_poll(struct cac_softc *sc, struct cac_ccb *wantccb, int timo) } /* - * Enqueue the specifed command (if any) and attempt to start all enqueued + * Enqueue the specifed command (if any) and attempt to start all enqueued * commands. Must be called at splbio. */ int cac_ccb_start(struct cac_softc *sc, struct cac_ccb *ccb) { - if (ccb != NULL) SIMPLEQ_INSERT_TAIL(&sc->sc_ccb_queue, ccb, ccb_chain); - while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL) { - if ((*sc->sc_cl->cl_fifo_full)(sc)) - return (EBUSY); + while ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_queue)) != NULL && + !(*sc->sc_cl->cl_fifo_full)(sc)) { SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_queue, ccb, ccb_chain); ccb->ccb_flags |= CAC_CCB_ACTIVE; (*sc->sc_cl->cl_submit)(sc, ccb); } - + + (*sc->sc_cl->cl_intr_enable)(sc, 1); + return (0); } @@ -452,16 +451,13 @@ cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) int error = 0; if ((ccb->ccb_flags & CAC_CCB_ACTIVE) == 0) { -#ifdef CAC_DEBUG printf("%s: CCB not active, xs=%p\n", sc->sc_dv.dv_xname, xs); -#endif if (xs) { xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); } return; } - ccb->ccb_flags &= ~CAC_CCB_ACTIVE; if ((ccb->ccb_flags & (CAC_CCB_DATA_IN | CAC_CCB_DATA_OUT)) != 0) { bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap_xfer, 0, @@ -486,11 +482,10 @@ cac_ccb_done(struct cac_softc *sc, struct cac_ccb *ccb) if (xs) { if (error) xs->error = XS_DRIVER_STUFFUP; - else { + else xs->resid = 0; - xs->flags |= ITSDONE; - } + xs->flags |= ITSDONE; scsi_done(xs); } } @@ -502,14 +497,11 @@ struct cac_ccb * cac_ccb_alloc(struct cac_softc *sc, int nosleep) { struct cac_ccb *ccb; - int s; - s = splbio(); if ((ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free)) != NULL) SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); else ccb = NULL; - splx(s); return (ccb); } @@ -519,12 +511,9 @@ cac_ccb_alloc(struct cac_softc *sc, int nosleep) void cac_ccb_free(struct cac_softc *sc, struct cac_ccb *ccb) { - int s; ccb->ccb_flags = 0; - s = splbio(); SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_chain); - splx(s); } int @@ -589,7 +578,7 @@ cac_scsi_cmd(xs) u_int32_t blockno, blockcnt, size; struct scsi_rw *rw; struct scsi_rw_big *rwb; - int op, flags, s, error; + int op, flags, s, error, poll; const char *p; if (target >= sc->sc_nunits || link->lun != 0) { @@ -597,7 +586,9 @@ cac_scsi_cmd(xs) return (COMPLETE); } + s = splbio(); xs->error = XS_NOERROR; + xs->free_list.le_next = NULL; dinfo = &sc->sc_dinfos[target]; switch (xs->cmd->opcode) { @@ -621,8 +612,7 @@ cac_scsi_cmd(xs) case INQUIRY: if (cac_get_dinfo(sc, target)) { xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - return (COMPLETE); + break; } bzero(&inq, sizeof inq); inq.device = T_DIRECT; @@ -646,8 +636,7 @@ cac_scsi_cmd(xs) case MODE_SENSE: if (cac_get_dinfo(sc, target)) { xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - return (COMPLETE); + break; } bzero(&mpd, sizeof mpd); switch (((struct scsi_mode_sense *)xs->cmd)->page) { @@ -675,6 +664,7 @@ cac_scsi_cmd(xs) sc->sc_dv.dv_xname, ((struct scsi_mode_sense *)xs->cmd)->page); xs->error = XS_DRIVER_STUFFUP; + splx(s); return (TRY_AGAIN_LATER); } break; @@ -682,8 +672,7 @@ cac_scsi_cmd(xs) case READ_CAPACITY: if (cac_get_dinfo(sc, target)) { xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - return (COMPLETE); + break; } bzero(&rcd, sizeof rcd); _lto4b( CAC_GET2(dinfo->ncylinders) * CAC_GET1(dinfo->nheads) * @@ -693,24 +682,17 @@ cac_scsi_cmd(xs) break; case PREVENT_ALLOW: - return (COMPLETE); + break; case SYNCHRONIZE_CACHE: - s = splbio(); - if (cac_flush(sc)) { - splx(s); + if (cac_flush(sc)) xs->error = XS_DRIVER_STUFFUP; - scsi_done(xs); - } else - splx(s); - return (COMPLETE); - + break; case READ_COMMAND: case READ_BIG: case WRITE_COMMAND: case WRITE_BIG: - s = splbio(); flags = 0; /* A read or write operation. */ @@ -727,12 +709,11 @@ cac_scsi_cmd(xs) size = CAC_GET2(dinfo->ncylinders) * CAC_GET1(dinfo->nheads) * CAC_GET1(dinfo->nsectors); if (blockno >= size || blockno + blockcnt > size) { - splx(s); printf("%s: out of bounds %u-%u >= %u\n", sc->sc_dv.dv_xname, blockno, blockcnt, size); xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); - return (COMPLETE); + break; } switch (xs->cmd->opcode) { @@ -748,26 +729,28 @@ cac_scsi_cmd(xs) break; } + poll = xs->flags & SCSI_POLL; if ((error = cac_cmd(sc, op, xs->data, blockcnt * DEV_BSIZE, target, blockno, flags, xs))) { - splx(s); if (error == ENOMEM) { - xs->error = XS_DRIVER_STUFFUP; + xs->error = XS_BUSY; + splx(s); return (TRY_AGAIN_LATER); - } else if (xs->flags & SCSI_POLL) { + } else if (poll) { xs->error = XS_TIMEOUT; + splx(s); return (TRY_AGAIN_LATER); } else { xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); - return (COMPLETE); + break; } } splx(s); - if (xs->flags & SCSI_POLL) + if (poll) return (COMPLETE); else return (SUCCESSFULLY_QUEUED); @@ -775,6 +758,7 @@ cac_scsi_cmd(xs) default: xs->error = XS_DRIVER_STUFFUP; } + splx(s); return (COMPLETE); } @@ -814,8 +798,11 @@ cac_l0_completed(sc) #ifdef CAC_DEBUG printf("compl-%x ", off); #endif - ccb = (struct cac_ccb *)(sc->sc_ccbs + - ((off & ~3) - sc->sc_ccbs_paddr)); + if (off & 3 && ccb->ccb_req.error == 0) + ccb->ccb_req.error = CAC_RET_CMD_INVALID; + + off = (off & ~3) - sc->sc_ccbs_paddr; + ccb = (struct cac_ccb *)(sc->sc_ccbs + off); bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, 0, sc->sc_dmamap->dm_mapsize, |