diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2000-11-10 09:42:16 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2000-11-10 09:42:16 +0000 |
commit | f51858efd4992b548e0fe55a5bf82ab802fbe44d (patch) | |
tree | d0d967bebc4852547e1e0bb97a2e0b868adce917 /sys/dev/ic | |
parent | 901a2870a712cf0a705a50206332b672c79889ce (diff) |
improved queue handling + some small bug fixes
Diffstat (limited to 'sys/dev/ic')
-rw-r--r-- | sys/dev/ic/gdt_common.c | 374 | ||||
-rw-r--r-- | sys/dev/ic/gdtvar.h | 4 |
2 files changed, 202 insertions, 176 deletions
diff --git a/sys/dev/ic/gdt_common.c b/sys/dev/ic/gdt_common.c index d3ff2542dec..5b3368858e0 100644 --- a/sys/dev/ic/gdt_common.c +++ b/sys/dev/ic/gdt_common.c @@ -1,4 +1,4 @@ -/* $OpenBSD: gdt_common.c,v 1.8 2000/08/19 14:25:15 nate Exp $ */ +/* $OpenBSD: gdt_common.c,v 1.9 2000/11/10 09:42:14 niklas Exp $ */ /* * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved. @@ -53,6 +53,12 @@ #include <dev/ic/gdtreg.h> #include <dev/ic/gdtvar.h> +#ifdef GDT_DEBUG +int gdt_maxcmds = GDT_MAXCMDS; +#undef GDT_MAXCMDS +#define GDT_MAXCMDS gdt_maxcmds +#endif + int gdt_async_event __P((struct gdt_softc *, int)); void gdt_chain __P((struct gdt_softc *)); void gdt_clear_events __P((struct gdt_softc *)); @@ -136,11 +142,12 @@ gdt_attach(gdt) /* Fill in the prototype scsi_link. */ gdt->sc_link.adapter_softc = gdt; gdt->sc_link.adapter = &gdt_switch; - gdt->sc_link.adapter_target = 7; gdt->sc_link.device = &gdt_dev; - gdt->sc_link.openings = GDT_MAXCMDS; /* XXX what is optimal? */ + /* XXX what is optimal? */ + gdt->sc_link.openings = GDT_MAXCMDS; gdt->sc_link.adapter_buswidth = (gdt->sc_class & GDT_FC) ? GDT_MAXID : GDT_MAX_HDRIVES; + gdt->sc_link.adapter_target = gdt->sc_link.adapter_buswidth; if (!gdt_internal_cmd(gdt, GDT_SCREENSERVICE, GDT_INIT, 0, 0, 0)) { printf("screen service initialization error %d\n", @@ -295,12 +302,12 @@ gdt_attach(gdt) gdt->sc_more_proc = 1; } } else { - /* XXX Not implemented yet */ + /* XXX Not implemented yet */ } /* Read more information */ if (gdt->sc_more_proc) { - /* XXX Not implemented yet */ + /* XXX Not implemented yet */ } if (!gdt_internal_cmd(gdt, GDT_SCSIRAWSERVICE, GDT_INIT, 0, 0, 0)) { @@ -447,6 +454,8 @@ gdt_dequeue(gdt) struct scsi_xfer *xs; xs = LIST_FIRST(&gdt->sc_queue); + if (xs == NULL) + return (NULL); LIST_REMOVE(xs, free_list); if (LIST_FIRST(&gdt->sc_queue) == NULL) @@ -455,7 +464,10 @@ gdt_dequeue(gdt) return (xs); } -/* Start a SCSI operation on a cache device */ +/* + * Start a SCSI operation on a cache device. + * XXX Polled operation is not yet complete. What kind of locking do we need? + */ int gdt_scsi_cmd(xs) struct scsi_xfer *xs; @@ -464,203 +476,206 @@ gdt_scsi_cmd(xs) struct gdt_softc *gdt = link->adapter_softc; u_int8_t target = link->target; struct gdt_ccb *ccb; - int dontqueue = 0; u_int32_t blockno, blockcnt; struct scsi_rw *rw; struct scsi_rw_big *rwb; bus_dmamap_t xfer; - int error; + int error, retval = SUCCESSFULLY_QUEUED; gdt_lock_t lock; GDT_DPRINTF(GDT_D_CMD, ("gdt_scsi_cmd ")); + xs->error = XS_NOERROR; + if (target >= GDT_MAX_HDRIVES || !gdt->sc_hdr[target].hd_present || link->lun != 0) { + /* + * XXX Should be XS_SENSE but that would require setting up a + * faked sense too. + */ xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); return (COMPLETE); } - xs->error = XS_NOERROR; - ccb = NULL; - - lock = GDT_LOCK_GDT(gdt); - if (!gdt_polling && !(xs->flags & SCSI_POLL) && - gdt->sc_test_busy(gdt)) { - /* Don't double enqueue if we came from gdt_chain. */ - if (xs != LIST_FIRST(&gdt->sc_queue)) - gdt_enqueue(gdt, xs, 0); - GDT_UNLOCK_GDT(gdt, lock); - return (SUCCESSFULLY_QUEUED); - } - GDT_UNLOCK_GDT(gdt, lock); + lock = GDT_LOCK_GDT(lock); - switch (xs->cmd->opcode) { - case TEST_UNIT_READY: - case REQUEST_SENSE: - case INQUIRY: - case MODE_SENSE: - case START_STOP: - case READ_CAPACITY: -#if 0 - case VERIFY: -#endif - return (gdt_internal_cache_cmd(xs) ? COMPLETE : - TRY_AGAIN_LATER); + /* Don't double enqueue if we came from gdt_chain. */ + if (xs != LIST_FIRST(&gdt->sc_queue)) + gdt_enqueue(gdt, xs, 0); - case PREVENT_ALLOW: - GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW ")); - /* XXX Not yet implemented */ + while ((xs = gdt_dequeue(gdt))) { xs->error = XS_NOERROR; - return (COMPLETE); - - case READ_COMMAND: - case READ_BIG: - case WRITE_COMMAND: - case WRITE_BIG: - case SYNCHRONIZE_CACHE: - lock = GDT_LOCK_GDT(gdt); - - /* - * When chaining commands we may get called with the - * first command in the queue, recognize this case - * easily. - */ - if (xs == LIST_FIRST(&gdt->sc_queue)) - xs = gdt_dequeue(gdt); - else { - /* A new command chain, start from the beginning. */ - gdt->sc_cmd_off = 0; - - /* Don't resort to queuing if we are polling. */ - dontqueue = xs->flags & SCSI_POLL; + ccb = NULL; + if (!gdt_polling && !(xs->flags & SCSI_POLL) && + gdt->sc_test_busy(gdt)) { /* - * The queue, if existent, must be processed first, - * before the new command can be run. + * Put it back in front. XXX Should we instead + * set xs->error to XS_BUSY? */ - if (LIST_FIRST(&gdt->sc_queue) != NULL) { - /* If order cannot be preserved, punt. */ - if (dontqueue) { - GDT_UNLOCK_GDT(gdt, lock); - xs->error = XS_DRIVER_STUFFUP; - return (TRY_AGAIN_LATER); - } - - /* - * Enqueue the new command, ponder on the front - * command of the queue instead. - */ - gdt_enqueue(gdt, xs, 0); - xs = gdt_dequeue(gdt); - } + gdt_enqueue(gdt, xs, 1); + break; } - if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { - /* A read or write operation. */ - if (xs->cmdlen == 6) { - rw = (struct scsi_rw *)xs->cmd; - blockno = _3btol(rw->addr) & - (SRW_TOPADDR << 16 | 0xffff); - blockcnt = rw->length ? rw->length : 0x100; - } else { - rwb = (struct scsi_rw_big *)xs->cmd; - blockno = _4btol(rwb->addr); - blockcnt = _2btol(rwb->length); - } - if (blockno >= gdt->sc_hdr[target].hd_size || - blockno + blockcnt > gdt->sc_hdr[target].hd_size) { + switch (xs->cmd->opcode) { + case TEST_UNIT_READY: + case REQUEST_SENSE: + case INQUIRY: + case MODE_SENSE: + case START_STOP: + case READ_CAPACITY: +#if 0 + case VERIFY: +#endif + if (!gdt_internal_cache_cmd(xs)) { GDT_UNLOCK_GDT(gdt, lock); - printf("%s: out of bounds %u-%u >= %u\n", - gdt->sc_dev.dv_xname, blockno, blockcnt, - gdt->sc_hdr[target].hd_size); - scsi_done(xs); - xs->error = XS_DRIVER_STUFFUP; - return (COMPLETE); + return (TRY_AGAIN_LATER); } - } + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + case PREVENT_ALLOW: + GDT_DPRINTF(GDT_D_CMD, ("PREVENT/ALLOW ")); + /* XXX Not yet implemented */ + xs->error = XS_NOERROR; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; - ccb = gdt_get_ccb(gdt, xs->flags); + default: + GDT_DPRINTF(GDT_D_CMD, + ("unknown opc %d ", xs->cmd->opcode)); + /* XXX Not yet implemented */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + + case READ_COMMAND: + case READ_BIG: + case WRITE_COMMAND: + case WRITE_BIG: + case SYNCHRONIZE_CACHE: + /* + * A new command chain, start from the beginning. + */ + gdt->sc_cmd_off = 0; - /* - * Are we out of commands, then queue. If we cannot queue, - * then punt. - */ - if (ccb == NULL) { - if (dontqueue) { - GDT_UNLOCK_GDT(gdt, lock); - scsi_done(xs); - xs->error = XS_DRIVER_STUFFUP; - return (COMPLETE); + if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { + /* A read or write operation. */ + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + blockno = _3btol(rw->addr) & + (SRW_TOPADDR << 16 | 0xffff); + blockcnt = + rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + blockno = _4btol(rwb->addr); + blockcnt = _2btol(rwb->length); + } + if (blockno >= gdt->sc_hdr[target].hd_size || + blockno + blockcnt > + gdt->sc_hdr[target].hd_size) { + printf( + "%s: out of bounds %u-%u >= %u\n", + gdt->sc_dev.dv_xname, blockno, + blockcnt, + gdt->sc_hdr[target].hd_size); + /* + * XXX Should be XS_SENSE but that + * would require setting up a faked + * sense too. + */ + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } } - if (xs->error) { - GDT_UNLOCK_GDT(gdt, lock); + + ccb = gdt_get_ccb(gdt, xs->flags); + /* + * Are we out of commands, something is wrong. + * + */ + if (ccb == NULL) { + printf("%s: no ccb in gdt_scsi_cmd", + gdt->sc_dev.dv_xname); + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; scsi_done(xs); - return (COMPLETE); + goto ready; } - /* Put back on the queue, in the front. */ - gdt_enqueue(gdt, xs, 1); - GDT_UNLOCK_GDT(gdt, lock); - return (SUCCESSFULLY_QUEUED); - } - - ccb->gc_blockno = blockno; - ccb->gc_blockcnt = blockcnt; - ccb->gc_xs = xs; - ccb->gc_timeout = xs->timeout; - ccb->gc_service = GDT_CACHESERVICE; - gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI); - - if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { - xfer = ccb->gc_dmamap_xfer; - error = bus_dmamap_load(gdt->sc_dmat, xfer, xs->data, - xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ? - BUS_DMA_NOWAIT : BUS_DMA_WAITOK); - if (error) { - printf("%s: gdt_scsi_cmd: ", - gdt->sc_dev.dv_xname); - if (error == EFBIG) - printf("more than %d dma segs\n", - GDT_MAXOFFSETS); - else - printf("error %d loading dma map\n", - error); - - xs->error = XS_DRIVER_STUFFUP; - gdt_free_ccb(gdt, ccb); - return (COMPLETE); + ccb->gc_blockno = blockno; + ccb->gc_blockcnt = blockcnt; + ccb->gc_xs = xs; + ccb->gc_timeout = xs->timeout; + ccb->gc_service = GDT_CACHESERVICE; + gdt_ccb_set_cmd(ccb, GDT_GCF_SCSI); + + if (xs->cmd->opcode != SYNCHRONIZE_CACHE) { + xfer = ccb->gc_dmamap_xfer; + error = bus_dmamap_load(gdt->sc_dmat, xfer, + xs->data, xs->datalen, NULL, + (xs->flags & SCSI_NOSLEEP) ? + BUS_DMA_NOWAIT : BUS_DMA_WAITOK); + if (error) { + printf("%s: gdt_scsi_cmd: ", + gdt->sc_dev.dv_xname); + if (error == EFBIG) + printf( + "more than %d dma segs\n", + GDT_MAXOFFSETS); + else + printf("error %d " + "loading dma map\n", + error); + + gdt_free_ccb(gdt, ccb); + xs->error = XS_DRIVER_STUFFUP; + xs->flags |= ITSDONE; + scsi_done(xs); + goto ready; + } + bus_dmamap_sync(gdt->sc_dmat, xfer, + (xs->flags & SCSI_DATA_IN) ? + BUS_DMASYNC_PREREAD : + BUS_DMASYNC_PREWRITE); } - bus_dmamap_sync(gdt->sc_dmat, xfer, - (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : - BUS_DMASYNC_PREWRITE); - } - gdt_enqueue_ccb(gdt, ccb); - /* XXX what if enqueue did not start a transfer? */ - if (gdt_polling || (xs->flags & SCSI_POLL)) { - if (!gdt_wait(gdt, ccb, ccb->gc_timeout)) { - GDT_UNLOCK_GDT(gdt, lock); - printf("%s: command %d timed out\n", - gdt->sc_dev.dv_xname, ccb->gc_cmd_index); - xs->error = XS_TIMEOUT; - return (TRY_AGAIN_LATER); + gdt_enqueue_ccb(gdt, ccb); + /* XXX what if enqueue did not start a transfer? */ + if (gdt_polling || (xs->flags & SCSI_POLL)) { + if (!gdt_wait(gdt, ccb, ccb->gc_timeout)) { + GDT_UNLOCK_GDT(gdt, lock); + printf("%s: command %d timed out\n", + gdt->sc_dev.dv_xname, + ccb->gc_cmd_index); + xs->error = XS_TIMEOUT; + return (TRY_AGAIN_LATER); + } + xs->flags |= ITSDONE; + scsi_done(xs); } } - GDT_UNLOCK_GDT(gdt, lock); - - if (gdt_polling || (xs->flags & SCSI_POLL)) { - scsi_done(xs); - return (COMPLETE); + ready: + /* + * Don't process the queue if we are polling. + */ + if (xs->flags & SCSI_POLL) { + retval = COMPLETE; + break; } - return (SUCCESSFULLY_QUEUED); - - default: - GDT_DPRINTF(GDT_D_CMD, ("unknown opc %d ", xs->cmd->opcode)); - /* XXX Not yet implemented */ - xs->error = XS_DRIVER_STUFFUP; - return (COMPLETE); } + + GDT_UNLOCK_GDT(gdt, lock); + return (retval); } /* XXX Currently only for cacheservice, returns 0 if busy */ @@ -679,8 +694,9 @@ gdt_exec_ccb(ccb) GDT_DPRINTF(GDT_D_CMD, ("gdt_exec_ccb(%p, %p) ", xs, ccb)); gdt->sc_cmd_cnt = 0; + /* - * XXX Yeah I know it's a always-true condition, but that may change + * XXX Yeah I know it's an always-true condition, but that may change * later. */ if (gdt->sc_cmd_cnt == 0) @@ -795,8 +811,6 @@ gdt_copy_internal_data(xs, data, size) copy_cnt = MIN(size, xs->datalen); bcopy(data, xs->data, copy_cnt); } - - } /* Emulated SCSI operation on cache device */ @@ -886,7 +900,7 @@ gdt_internal_cache_cmd(xs) default: printf("%s: mode sense page %d not simulated\n", gdt->sc_dev.dv_xname, - ((struct scsi_mode_sense *)xs->cmd)->page); + ((struct scsi_mode_sense *)xs->cmd)->page); xs->error = XS_DRIVER_STUFFUP; return (0); } @@ -959,12 +973,22 @@ gdt_sync_event(gdt, service, index, xs) ("gdt_sync_event(%p, %d, %d, %p) ", gdt, service, index, xs)); if (service == GDT_SCREENSERVICE) { + GDT_DPRINTF(GDT_D_INTR, ("service == GDT_SCREENSERVICE ")); /* XXX To be implemented */ return (0); } else { - if (gdt->sc_status == GDT_S_OK) { + switch (gdt->sc_status) { + case GDT_S_OK: + GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_OK ")); /* XXX To be implemented */ - } else { + break; + case GDT_S_BSY: + GDT_DPRINTF(GDT_D_INTR, ("sc_status == GDT_S_BSY ")); + /* XXX To be implemented */ + return (2); + default: + GDT_DPRINTF(GDT_D_INTR, ("sc_status is %d ", + gdt->sc_status)); /* XXX To be implemented */ return (0); } @@ -979,7 +1003,8 @@ gdt_intr(arg) { struct gdt_softc *gdt = arg; struct gdt_intr_ctx ctx; - int chain = 1, sync_val = 0; + int chain = 1; + int sync_val = 0; struct scsi_xfer *xs; int prev_cmd; struct gdt_ccb *ccb; @@ -1022,7 +1047,8 @@ gdt_intr(arg) goto finish; case GDT_SPEZINDEX: - /* XXX Not yet implemented */ + printf("%s: uninitialized or unknown service (%d %d)\n", + gdt->sc_dev.dv_xname, ctx.info, ctx.info2); chain = 0; goto finish; } diff --git a/sys/dev/ic/gdtvar.h b/sys/dev/ic/gdtvar.h index 16aebe9d1bd..f436b15a9a4 100644 --- a/sys/dev/ic/gdtvar.h +++ b/sys/dev/ic/gdtvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: gdtvar.h,v 1.4 2000/08/05 18:52:34 niklas Exp $ */ +/* $OpenBSD: gdtvar.h,v 1.5 2000/11/10 09:42:14 niklas Exp $ */ /* * Copyright (c) 1999, 2000 Niklas Hallqvist. All rights reserved. @@ -51,7 +51,7 @@ extern int gdt_debug; #define GDT_RETRIES 100000000 /* 100000 * 1us = 100s */ #define GDT_TIMEOUT 100000000 /* 100000 * 1us = 100s */ #define GDT_POLL_TIMEOUT 10000000 /* 10000 * 1us = 10s */ -#define GDT_WATCH_TIMEOUT 10000000 /* 10000 * 1us = 10s */ +#define GDT_WATCH_TIMEOUT 10000 /* 10000 * 1ms = 10s */ #define GDT_SCRATCH_SZ 4096 /* 4KB scratch buffer */ /* Context structure for interrupt services */ |