diff --git a/sys/dev/ic/isp_netbsd.c b/sys/dev/ic/isp_netbsd.c index 097f94ee1bc9..6f79a2e07422 100644 --- a/sys/dev/ic/isp_netbsd.c +++ b/sys/dev/ic/isp_netbsd.c @@ -1,5 +1,4 @@ -/* $NetBSD: isp_netbsd.c,v 1.16 1999/09/30 23:06:18 thorpej Exp $ */ -/* release_6_5_99 */ +/* $NetBSD: isp_netbsd.c,v 1.17 1999/10/14 02:31:11 mjacob Exp $ */ /* * Platform (NetBSD) dependent common attachment code for Qlogic adapters. * Matthew Jacob @@ -32,18 +31,20 @@ */ #include +#include static void ispminphys __P((struct buf *)); +static int32_t ispcmd_slow __P((ISP_SCSI_XFER_T *)); static int32_t ispcmd __P((ISP_SCSI_XFER_T *)); +static int +ispioctl __P((struct scsipi_link *, u_long, caddr_t, int, struct proc *)); static struct scsipi_device isp_dev = { NULL, NULL, NULL, NULL }; static int isp_poll __P((struct ispsoftc *, ISP_SCSI_XFER_T *, int)); static void isp_watch __P((void *)); +static void isp_command_requeue __P((void *)); static void isp_internal_restart __P((void *)); -#define FC_OPENINGS RQUEST_QUEUE_LEN / (MAX_FC_TARG-1) -#define PI_OPENINGS RQUEST_QUEUE_LEN / (MAX_TARGETS-1) - /* * Complete attachment of hardware, include subdevices. */ @@ -52,14 +53,15 @@ isp_attach(isp) struct ispsoftc *isp; { - isp->isp_osinfo._adapter.scsipi_cmd = ispcmd; isp->isp_osinfo._adapter.scsipi_minphys = ispminphys; + isp->isp_osinfo._adapter.scsipi_ioctl = ispioctl; isp->isp_state = ISP_RUNSTATE; isp->isp_osinfo._link.scsipi_scsi.channel = SCSI_CHANNEL_ONLY_ONE; isp->isp_osinfo._link.adapter_softc = isp; isp->isp_osinfo._link.device = &isp_dev; isp->isp_osinfo._link.adapter = &isp->isp_osinfo._adapter; + isp->isp_osinfo._link.openings = isp->isp_maxcmds; TAILQ_INIT(&isp->isp_osinfo.waitq); if (IS_FC(isp)) { @@ -67,6 +69,7 @@ isp_attach(isp) * Give it another chance here to come alive... */ fcparam *fcp = isp->isp_param; + isp->isp_osinfo._adapter.scsipi_cmd = ispcmd; if (fcp->isp_fwstate != FW_READY) { (void) isp_control(isp, ISPCTL_FCLINK_TEST, NULL); } @@ -79,12 +82,11 @@ isp_attach(isp) #else isp->isp_osinfo._link.scsipi_scsi.max_lun = 15; #endif - isp->isp_osinfo._link.openings = FC_OPENINGS; isp->isp_osinfo._link.scsipi_scsi.adapter_target = ((fcparam *)isp->isp_param)->isp_loopid; } else { sdparam *sdp = isp->isp_param; - isp->isp_osinfo._link.openings = PI_OPENINGS; + isp->isp_osinfo._adapter.scsipi_cmd = ispcmd_slow; isp->isp_osinfo._link.scsipi_scsi.max_target = MAX_TARGETS-1; if (isp->isp_bustype == ISP_BT_SBUS) { isp->isp_osinfo._link.scsipi_scsi.max_lun = 7; @@ -101,6 +103,8 @@ isp_attach(isp) } isp->isp_osinfo._link.scsipi_scsi.adapter_target = sdp->isp_initiator_id; + isp->isp_osinfo.discovered[0] = 1 << sdp->isp_initiator_id; + isp->isp_osinfo.discovered[1] = 1 << sdp->isp_initiator_id; if (IS_12X0(isp)) { isp->isp_osinfo._link_b = isp->isp_osinfo._link; sdp++; @@ -109,15 +113,12 @@ isp_attach(isp) isp->isp_osinfo._link_b.scsipi_scsi.channel = 1; } } - if (isp->isp_osinfo._link.openings < 2) - isp->isp_osinfo._link.openings = 2; isp->isp_osinfo._link.type = BUS_SCSI; /* * Send a SCSI Bus Reset (used to be done as part of attach, * but now left to the OS outer layers). */ - if (IS_SCSI(isp)) { int bus = 0; (void) isp_control(isp, ISPCTL_RESET_BUS, &bus); @@ -132,7 +133,7 @@ isp_attach(isp) * Start the watchdog. */ isp->isp_dogactive = 1; - timeout(isp_watch, isp, 30 * hz); + timeout(isp_watch, isp, WATCH_INTERVAL * hz); /* * And attach children (if any). @@ -164,44 +165,86 @@ ispminphys(bp) minphys(bp); } -static int +static int32_t +ispcmd_slow(xs) + ISP_SCSI_XFER_T *xs; +{ + /* + * Have we completed discovery for this adapter? + */ + if ((xs->xs_control & XS_CTL_DISCOVERY) == 0) { + struct ispsoftc *isp = XS_ISP(xs); + sdparam *sdp = isp->isp_param; + int s = splbio(); + int chan = XS_CHANNEL(xs), chmax = IS_12X0(isp)? 2 : 1; + u_int16_t f = DPARM_DEFAULT; + + sdp += chan; + if (xs->sc_link->quirks & SDEV_NOSYNC) { + f ^= DPARM_SYNC; + } + if (xs->sc_link->quirks & SDEV_NOWIDE) { + f ^= DPARM_WIDE; + } + if (xs->sc_link->quirks & SDEV_NOTAG) { + f ^= DPARM_TQING; + } + sdp->isp_devparam[XS_TGT(xs)].dev_flags = f; + sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; + isp->isp_osinfo.discovered[chan] |= (1 << XS_TGT(xs)); + f = 0xffff ^ (1 << sdp->isp_initiator_id); + for (chan = 0; chan < chmax; chan++) { + if (isp->isp_osinfo.discovered[chan] == f) + break; + } + if (chan == chmax) { + isp->isp_osinfo._adapter.scsipi_cmd = ispcmd; + isp->isp_update = 1; + if (IS_12X0(isp)) + isp->isp_update |= 2; + } + (void) splx(s); + } + return (ispcmd(xs)); +} + +static int +ispioctl(sc_link, cmd, addr, flag, p) + struct scsipi_link *sc_link; + u_long cmd; + caddr_t addr; + int flag; + struct proc *p; +{ + struct ispsoftc *isp = sc_link->adapter_softc; + int s, chan, retval = ENOTTY; + + switch (cmd) { + case SCBUSIORESET: + chan = sc_link->scsipi_scsi.channel; + s = splbio(); + if (isp_control(isp, ISPCTL_RESET_BUS, &chan)) + retval = EIO; + else + retval = 0; + (void) splx(s); + break; + default: + break; + } + return (retval); +} + + +static int32_t ispcmd(xs) ISP_SCSI_XFER_T *xs; { struct ispsoftc *isp; - int result; - int s; + int result, s; isp = XS_ISP(xs); s = splbio(); - /* - * This is less efficient than I would like in that the - * majority of cases will have to do some pointer deferences - * to find out that things don't need to be updated. - */ -#if 0 /* XXX THORPEJ */ - if ((xs->xs_control & XS_CTL_DISCOVERY) == 0 && - (isp->isp_type & ISP_HA_SCSI)) { - sdparam *sdp = isp->isp_param; - sdp += XS_CHANNEL(xs); - if (sdp->isp_devparam[XS_TGT(xs)].dev_flags != - sdp->isp_devparam[XS_TGT(xs)].cur_dflags) { - u_int16_t f = DPARM_WIDE|DPARM_SYNC|DPARM_TQING; - if (xs->sc_link->quirks & SDEV_NOSYNC) - f &= ~DPARM_SYNC; - if (xs->sc_link->quirks & SDEV_NOWIDE) - f &= ~DPARM_WIDE; - if (xs->sc_link->quirks & SDEV_NOTAG) - f &= ~DPARM_TQING; - sdp->isp_devparam[XS_TGT(xs)].dev_flags &= - ~(DPARM_WIDE|DPARM_SYNC|DPARM_TQING); - sdp->isp_devparam[XS_TGT(xs)].dev_flags |= f; - sdp->isp_devparam[XS_TGT(xs)].dev_update = 1; - isp->isp_update |= (1 << XS_CHANNEL(xs)); - } - } -#endif /* XXX THORPEJ */ - if (isp->isp_state < ISP_RUNSTATE) { DISABLE_INTS(isp); isp_init(isp); @@ -209,7 +252,7 @@ ispcmd(xs) ENABLE_INTS(isp); (void) splx(s); XS_SETERR(xs, HBA_BOTCH); - return (CMD_COMPLETE); + return (COMPLETE); } isp->isp_state = ISP_RUNSTATE; ENABLE_INTS(isp); @@ -218,7 +261,6 @@ ispcmd(xs) /* * Check for queue blockage... */ - if (isp->isp_osinfo.blocked) { if (xs->xs_control & XS_CTL_POLL) { xs->error = XS_DRIVER_STUFFUP; @@ -227,37 +269,70 @@ ispcmd(xs) } TAILQ_INSERT_TAIL(&isp->isp_osinfo.waitq, xs, adapter_q); splx(s); - return (CMD_QUEUED); + return (SUCCESSFULLY_QUEUED); } - DISABLE_INTS(isp); result = ispscsicmd(xs); ENABLE_INTS(isp); - if (result != CMD_QUEUED || (xs->xs_control & XS_CTL_POLL) == 0) { + + if ((xs->xs_control & XS_CTL_POLL) == 0) { + switch (result) { + case CMD_QUEUED: + result = SUCCESSFULLY_QUEUED; + break; + case CMD_EAGAIN: + result = TRY_AGAIN_LATER; + break; + case CMD_RQLATER: + result = SUCCESSFULLY_QUEUED; + timeout(isp_command_requeue, xs, hz); + break; + case CMD_COMPLETE: + result = COMPLETE; + break; + } (void) splx(s); return (result); } + switch (result) { + case CMD_QUEUED: + result = SUCCESSFULLY_QUEUED; + break; + case CMD_RQLATER: + case CMD_EAGAIN: + if (XS_NOERR(xs)) { + xs->error = XS_DRIVER_STUFFUP; + } + result = TRY_AGAIN_LATER; + break; + case CMD_COMPLETE: + result = COMPLETE; + break; + + } /* * If we can't use interrupts, poll on completion. */ - if (isp_poll(isp, xs, XS_TIME(xs))) { - /* - * If no other error occurred but we didn't finish, - * something bad happened. - */ - if (XS_IS_CMD_DONE(xs) == 0) { - isp->isp_nactive--; - if (isp->isp_nactive < 0) - isp->isp_nactive = 0; - if (XS_NOERR(xs)) { - isp_lostcmd(isp, xs); - XS_SETERR(xs, HBA_BOTCH); + if (result == SUCCESSFULLY_QUEUED) { + if (isp_poll(isp, xs, XS_TIME(xs))) { + /* + * If no other error occurred but we didn't finish, + * something bad happened. + */ + if (XS_IS_CMD_DONE(xs) == 0) { + if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { + isp_restart(isp); + } + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } } } + result = COMPLETE; } (void) splx(s); - return (CMD_COMPLETE); + return (result); } static int @@ -287,30 +362,31 @@ isp_watch(arg) { int i; struct ispsoftc *isp = arg; - ISP_SCSI_XFER_T *xs; - ISP_ILOCKVAL_DECL; + struct scsipi_xfer *xs; + int s; /* * Look for completely dead commands (but not polled ones). */ - ISP_ILOCK(isp); - for (i = 0; i < RQUEST_QUEUE_LEN; i++) { - if ((xs = (ISP_SCSI_XFER_T *) isp->isp_xflist[i]) == NULL) { + s = splbio(); + for (i = 0; i < isp->isp_maxcmds; i++) { + xs = isp->isp_xflist[i]; + if (xs == NULL) { continue; } - if (XS_TIME(xs) == 0) { + if (xs->timeout == 0 || (xs->xs_control & XS_CTL_POLL)) { continue; } - XS_TIME(xs) -= (WATCH_INTERVAL * 1000); + xs->timeout -= (WATCH_INTERVAL * 1000); /* * Avoid later thinking that this * transaction is not being timed. * Then give ourselves to watchdog * periods of grace. */ - if (XS_TIME(xs) == 0) { - XS_TIME(xs) = 1; - } else if (XS_TIME(xs) > -(2 * WATCH_INTERVAL * 1000)) { + if (xs->timeout == 0) { + xs->timeout = 1; + } else if (xs->timeout > -(2 * WATCH_INTERVAL * 1000)) { continue; } if (isp_control(isp, ISPCTL_ABORT_CMD, xs)) { @@ -322,7 +398,7 @@ isp_watch(arg) } timeout(isp_watch, isp, WATCH_INTERVAL * hz); isp->isp_dogactive = 1; - ISP_IUNLOCK(isp); + (void) splx(s); } /* @@ -354,6 +430,35 @@ isp_uninit(isp) ISP_IUNLOCK(isp); } +/* + * Restart function for a command to be requeued later. + */ +static void +isp_command_requeue(arg) + void *arg; +{ + struct scsipi_xfer *xs = arg; + struct ispsoftc *isp = XS_ISP(xs); + int s = splbio(); + switch (ispcmd_slow(xs)) { + case SUCCESSFULLY_QUEUED: + printf("%s: isp_command_reque: queued %d.%d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + break; + case TRY_AGAIN_LATER: + printf("%s: EAGAIN for %d.%d\n", + isp->isp_name, XS_TGT(xs), XS_LUN(xs)); + /* FALLTHROUGH */ + case COMPLETE: + xs->xs_status |= XS_STS_DONE; + if (XS_NOERR(xs)) { + XS_SETERR(xs, HBA_BOTCH); + } + scsipi_done(xs); + break; + } + (void) splx(s); +} /* * Restart function after a LOOP UP event (e.g.), * done as a timeout for some hysteresis. @@ -398,64 +503,64 @@ isp_async(isp, cmd, arg) int s = splbio(); switch (cmd) { case ISPASYNC_NEW_TGT_PARAMS: - if (isp->isp_type & ISP_HA_SCSI) { - sdparam *sdp = isp->isp_param; - char *wt; - int mhz, flags, period; + if (IS_SCSI(isp) && isp->isp_dblev) { + sdparam *sdp = isp->isp_param; + char *wt; + int mhz, flags, period; - tgt = *((int *) arg); - bus = (tgt >> 16) & 0xffff; - tgt &= 0xffff; + tgt = *((int *) arg); + bus = (tgt >> 16) & 0xffff; + tgt &= 0xffff; - flags = sdp->isp_devparam[tgt].cur_dflags; - period = sdp->isp_devparam[tgt].cur_period; - if ((flags & DPARM_SYNC) && period && - (sdp->isp_devparam[tgt].cur_offset) != 0) { - if (sdp->isp_lvdmode) { - switch (period) { - case 0xa: - mhz = 40; - break; - case 0xb: - mhz = 33; - break; - case 0xc: - mhz = 25; - break; - default: - mhz = 1000 / (period * 4); - break; - } - } else { + flags = sdp->isp_devparam[tgt].cur_dflags; + period = sdp->isp_devparam[tgt].cur_period; + if ((flags & DPARM_SYNC) && period && + (sdp->isp_devparam[tgt].cur_offset) != 0) { + if (sdp->isp_lvdmode) { + switch (period) { + case 0xa: + mhz = 40; + break; + case 0xb: + mhz = 33; + break; + case 0xc: + mhz = 25; + break; + default: mhz = 1000 / (period * 4); + break; } } else { - mhz = 0; - } - switch (flags & (DPARM_WIDE|DPARM_TQING)) { - case DPARM_WIDE: - wt = ", 16 bit wide\n"; - break; - case DPARM_TQING: - wt = ", Tagged Queueing Enabled\n"; - break; - case DPARM_WIDE|DPARM_TQING: - wt = ", 16 bit wide, Tagged Queueing Enabled\n"; - break; - default: - wt = "\n"; - break; - } - if (mhz) { - printf("%s: Bus %d Target %d at %dMHz Max " - "Offset %d%s", isp->isp_name, bus, tgt, mhz, - sdp->isp_devparam[tgt].cur_offset, wt); - } else { - printf("%s: Bus %d Target %d Async Mode%s", - isp->isp_name, bus, tgt, wt); + mhz = 1000 / (period * 4); } + } else { + mhz = 0; + } + switch (flags & (DPARM_WIDE|DPARM_TQING)) { + case DPARM_WIDE: + wt = ", 16 bit wide\n"; + break; + case DPARM_TQING: + wt = ", Tagged Queueing Enabled\n"; + break; + case DPARM_WIDE|DPARM_TQING: + wt = ", 16 bit wide, Tagged Queueing Enabled\n"; + break; + default: + wt = "\n"; + break; + } + if (mhz) { + printf("%s: Bus %d Target %d at %dMHz Max " + "Offset %d%s", isp->isp_name, bus, tgt, mhz, + sdp->isp_devparam[tgt].cur_offset, wt); + } else { + printf("%s: Bus %d Target %d Async Mode%s", + isp->isp_name, bus, tgt, wt); } break; + } case ISPASYNC_BUS_RESET: if (arg) bus = *((int *) arg); @@ -477,7 +582,7 @@ isp_async(isp, cmd, arg) printf("%s: Loop UP\n", isp->isp_name); break; case ISPASYNC_PDB_CHANGED: - if (IS_FC(isp)) { + if (IS_FC(isp) && isp->isp_dblev) { const char *fmt = "%s: Target %d (Loop 0x%x) Port ID 0x%x " "role %s %s\n Port WWN 0x%08x%08x\n Node WWN 0x%08x%08x\n"; const static char *roles[4] = {