summaryrefslogtreecommitdiff
path: root/sys/dev/isp
diff options
context:
space:
mode:
authorMatt Jacob <mjacob@FreeBSD.org>2001-02-12 01:16:49 +0000
committerMatt Jacob <mjacob@FreeBSD.org>2001-02-12 01:16:49 +0000
commita00496ebe1b4e76405080e75133f98cc3fdef61c (patch)
tree07aa67540087e1a6e52d795708ce107bc846014f /sys/dev/isp
parent7e41d12a39b838001569caede5fcf971d94b7e9c (diff)
Notes
Diffstat (limited to 'sys/dev/isp')
-rw-r--r--sys/dev/isp/isp.c963
1 files changed, 593 insertions, 370 deletions
diff --git a/sys/dev/isp/isp.c b/sys/dev/isp/isp.c
index f7e0b232be4f..201997c5bb1d 100644
--- a/sys/dev/isp/isp.c
+++ b/sys/dev/isp/isp.c
@@ -72,7 +72,6 @@ static const char portdup[] =
"Target %d duplicates Target %d- killing off both";
static const char retained[] =
"Retaining Loop ID 0x%x for Target %d (Port 0x%x)";
-#ifdef ISP2100_FABRIC
static const char lretained[] =
"Retained login of Target %d (Loop ID 0x%x) Port 0x%x";
static const char plogout[] =
@@ -87,7 +86,6 @@ static const char pdbmfail2[] =
"PDB Port info for Device @ Port 0x%x does not match up (0x%x)";
static const char ldumped[] =
"Target %d (Loop ID 0x%x) Port 0x%x dumped after login info mismatch";
-#endif
static const char notresp[] =
"Not RESPONSE in RESPONSE Queue (type 0x%x) @ idx %d (next %d) nlooked %d";
static const char xact1[] =
@@ -101,7 +99,7 @@ static const char pskip[] =
static const char topology[] =
"Loop ID %d, AL_PA 0x%x, Port ID 0x%x, Loop State 0x%x, Topology '%s'";
static const char finmsg[] =
- "(%d.%d.%d): FIN dl%d resid%d STS 0x%x SKEY %c XS_ERR=0x%x";
+ "(%d.%d.%d): FIN dl%d resid %d STS 0x%x SKEY %c XS_ERR=0x%x";
/*
* Local function prototypes.
*/
@@ -115,16 +113,15 @@ static void isp_scsi_init __P((struct ispsoftc *));
static void isp_scsi_channel_init __P((struct ispsoftc *, int));
static void isp_fibre_init __P((struct ispsoftc *));
static void isp_mark_getpdb_all __P((struct ispsoftc *));
+static int isp_getmap __P((struct ispsoftc *, fcpos_map_t *));
static int isp_getpdb __P((struct ispsoftc *, int, isp_pdb_t *));
static u_int64_t isp_get_portname __P((struct ispsoftc *, int, int));
static int isp_fclink_test __P((struct ispsoftc *, int));
static char *isp2100_fw_statename __P((int));
-static int isp_same_lportdb __P((struct lportdb *, struct lportdb *));
-static int isp_pdb_sync __P((struct ispsoftc *, int));
-#ifdef ISP2100_FABRIC
+static int isp_pdb_sync __P((struct ispsoftc *));
+static int isp_scan_loop __P((struct ispsoftc *));
static int isp_scan_fabric __P((struct ispsoftc *));
static void isp_register_fc4_type __P((struct ispsoftc *));
-#endif
static void isp_fw_state __P((struct ispsoftc *));
static void isp_mboxcmd __P((struct ispsoftc *, mbreg_t *, int));
@@ -1206,6 +1203,36 @@ isp_fibre_init(isp)
* else failure.
*/
+static int
+isp_getmap(isp, map)
+ struct ispsoftc *isp;
+ fcpos_map_t *map;
+{
+ fcparam *fcp = (fcparam *) isp->isp_param;
+ mbreg_t mbs;
+
+ mbs.param[0] = MBOX_GET_FC_AL_POSITION_MAP;
+ mbs.param[1] = 0;
+ mbs.param[2] = DMA_MSW(fcp->isp_scdma);
+ mbs.param[3] = DMA_LSW(fcp->isp_scdma);
+ /*
+ * Unneeded. For the 2100, except for initializing f/w, registers
+ * 4/5 have to not be written to.
+ * mbs.param[4] = 0;
+ * mbs.param[5] = 0;
+ *
+ */
+ mbs.param[6] = 0;
+ mbs.param[7] = 0;
+ isp_mboxcmd(isp, &mbs, MBLOGALL & ~MBOX_COMMAND_PARAM_ERROR);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ MEMCPY(map, fcp->isp_scratch, sizeof (fcpos_map_t));
+ map->fwmap = mbs.param[1] != 0;
+ return (0);
+ }
+ return (-1);
+}
+
static void
isp_mark_getpdb_all(isp)
struct ispsoftc *isp;
@@ -1292,12 +1319,12 @@ isp_fclink_test(isp, usdelay)
"F Port (no FLOGI_ACC response)"
};
mbreg_t mbs;
- int count;
+ int count, check_for_fabric;
u_int8_t lwfs;
fcparam *fcp;
-#if defined(ISP2100_FABRIC)
+ struct lportdb *lp;
isp_pdb_t pdb;
-#endif
+
fcp = isp->isp_param;
/*
@@ -1390,19 +1417,37 @@ isp_fclink_test(isp, usdelay)
} else {
fcp->isp_topo = TOPO_NL_PORT;
}
- fcp->isp_alpa = mbs.param[2];
+ fcp->isp_portid = fcp->isp_alpa = mbs.param[2] & 0xff;
-#if defined(ISP2100_FABRIC)
+ /*
+ * Check to see if we're on a fabric by trying to see if we
+ * can talk to the fabric name server. This can be a bit
+ * tricky because if we're a 2100, we should check always
+ * (in case we're connected to an server doing aliasing).
+ */
fcp->isp_onfabric = 0;
- if (fcp->isp_topo != TOPO_N_PORT &&
- isp_getpdb(isp, FL_PORT_ID, &pdb) == 0) {
+
+ if (IS_2100(isp))
+ check_for_fabric = 1;
+ else if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_F_PORT)
+ check_for_fabric = 1;
+ else
+ check_for_fabric = 0;
+
+ if (check_for_fabric && isp_getpdb(isp, FL_PORT_ID, &pdb) == 0) {
int loopid = FL_PORT_ID;
- struct lportdb *lp;
if (IS_2100(isp)) {
fcp->isp_topo = TOPO_FL_PORT;
}
- fcp->isp_portid = mbs.param[2] | (((int)mbs.param[3]) << 16);
- fcp->isp_onfabric = 1;
+
+ if (BITS2WORD(pdb.pdb_portid_bits) == 0) {
+ /*
+ * Crock.
+ */
+ fcp->isp_topo = TOPO_NL_PORT;
+ goto not_on_fabric;
+ }
+ fcp->isp_portid = mbs.param[2] | ((int) mbs.param[3] << 16);
/*
* Save the Fabric controller's port database entry.
@@ -1431,12 +1476,11 @@ isp_fclink_test(isp, usdelay)
lp->portid = BITS2WORD(pdb.pdb_portid_bits);
lp->loopid = pdb.pdb_loopid;
lp->loggedin = lp->valid = 1;
- (void) isp_async(isp, ISPASYNC_LOGGED_INOUT, &loopid);
+ fcp->isp_onfabric = 1;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
isp_register_fc4_type(isp);
- } else
-#endif
- {
- fcp->isp_portid = mbs.param[2];
+ } else {
+not_on_fabric:
fcp->isp_onfabric = 0;
fcp->portdb[FL_PORT_ID].valid = 0;
}
@@ -1444,6 +1488,45 @@ isp_fclink_test(isp, usdelay)
isp_prt(isp, ISP_LOGINFO, topology, fcp->isp_loopid, fcp->isp_alpa,
fcp->isp_portid, fcp->isp_loopstate, toponames[fcp->isp_topo]);
+ /*
+ * Announce ourselves, too. This involves synthesizing an entry.
+ */
+ if (fcp->isp_iid_set == 0) {
+ fcp->isp_iid_set = 1;
+ fcp->isp_iid = fcp->isp_loopid;
+ lp = &fcp->portdb[fcp->isp_iid];
+ } else {
+ lp = &fcp->portdb[fcp->isp_iid];
+ if (fcp->isp_portid != lp->portid ||
+ fcp->isp_loopid != lp->loopid ||
+ fcp->isp_nodewwn != ISP_NODEWWN(isp) ||
+ fcp->isp_portwwn != ISP_PORTWWN(isp)) {
+ lp->valid = 0;
+ count = fcp->isp_iid;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &count);
+ }
+ }
+ lp->loopid = fcp->isp_loopid;
+ lp->portid = fcp->isp_portid;
+ lp->node_wwn = ISP_NODEWWN(isp);
+ lp->port_wwn = ISP_PORTWWN(isp);
+ switch (isp->isp_role) {
+ case ISP_ROLE_NONE:
+ lp->roles = 0;
+ break;
+ case ISP_ROLE_TARGET:
+ lp->roles = SVC3_TGT_ROLE >> SVC3_ROLE_SHIFT;
+ break;
+ case ISP_ROLE_INITIATOR:
+ lp->roles = SVC3_INI_ROLE >> SVC3_ROLE_SHIFT;
+ break;
+ case ISP_ROLE_BOTH:
+ lp->roles = (SVC3_INI_ROLE|SVC3_TGT_ROLE) >> SVC3_ROLE_SHIFT;
+ break;
+ }
+ lp->loggedin = lp->valid = 1;
+ count = fcp->isp_iid;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &count);
return (0);
}
@@ -1464,334 +1547,81 @@ isp2100_fw_statename(state)
}
}
-static int
-isp_same_lportdb(a, b)
- struct lportdb *a, *b;
-{
- /*
- * We decide two lports are the same if they have non-zero and
- * identical port WWNs and identical loop IDs.
- */
-
- if (a->port_wwn == 0 || a->port_wwn != b->port_wwn ||
- a->loopid != b->loopid || a->roles != b->roles) {
- return (0);
- } else {
- return (1);
- }
-}
-
/*
* Synchronize our soft copy of the port database with what the f/w thinks
* (with a view toward possibly for a specific target....)
*/
static int
-isp_pdb_sync(isp, target)
+isp_pdb_sync(isp)
struct ispsoftc *isp;
- int target;
{
- struct lportdb *lp, *tport;
+ struct lportdb *lp;
fcparam *fcp = isp->isp_param;
isp_pdb_t pdb;
- int loopid, frange, prange, lim;
+ int loopid, base, lim;
-#ifdef ISP2100_FABRIC
- /*
- * XXX: If we do this *after* building up our local port database,
- * XXX: the commands simply don't work.
- */
/*
- * (Re)discover all fabric devices
+ * Make sure we're okay for doing this right now.
*/
- if (fcp->isp_onfabric)
- (void) isp_scan_fabric(isp);
-#endif
-
-
- switch (fcp->isp_topo) {
- case TOPO_F_PORT:
- case TOPO_PTP_STUB:
- frange = prange = 0;
- break;
- case TOPO_N_PORT:
- frange = FC_SNS_ID+1;
- prange = 2;
- break;
- default:
- frange = FC_SNS_ID+1;
- prange = FL_PORT_ID;
- break;
+ if (fcp->isp_loopstate != LOOP_PDB_RCVD &&
+ fcp->isp_loopstate != LOOP_FSCAN_DONE &&
+ fcp->isp_loopstate != LOOP_LSCAN_DONE) {
+ return (-1);
}
- /*
- * make sure the temp port database is clean...
- */
- MEMZERO((void *)fcp->tport, sizeof (fcp->tport));
- tport = fcp->tport;
-
- /*
- * Run through the local loop ports and get port database info
- * for each loop ID.
- *
- * There's a somewhat unexplained situation where the f/w passes back
- * the wrong database entity- if that happens, just restart (up to
- * FL_PORT_ID times).
- */
- for (lim = loopid = 0; loopid < prange; loopid++) {
- lp = &tport[loopid];
- lp->node_wwn = isp_get_portname(isp, loopid, 1);
- if (fcp->isp_loopstate < LOOP_PDB_RCVD)
- return (-1);
- if (lp->node_wwn == 0)
- continue;
- lp->port_wwn = isp_get_portname(isp, loopid, 0);
- if (fcp->isp_loopstate < LOOP_PDB_RCVD)
- return (-1);
- if (lp->port_wwn == 0) {
- lp->node_wwn = 0;
- continue;
- }
-
- /*
- * Get an entry....
- */
- if (isp_getpdb(isp, loopid, &pdb) != 0) {
- if (fcp->isp_loopstate < LOOP_PDB_RCVD)
+ if (fcp->isp_topo == TOPO_FL_PORT || fcp->isp_topo == TOPO_NL_PORT ||
+ fcp->isp_topo == TOPO_N_PORT) {
+ if (fcp->isp_loopstate < LOOP_LSCAN_DONE) {
+ if (isp_scan_loop(isp) != 0) {
return (-1);
- continue;
- }
-
- if (fcp->isp_loopstate < LOOP_PDB_RCVD)
- return (-1);
-
- /*
- * If the returned database element doesn't match what we
- * asked for, restart the process entirely (up to a point...).
- */
- if (pdb.pdb_loopid != loopid) {
- loopid = 0;
- if (lim++ < FL_PORT_ID) {
- continue;
}
- isp_prt(isp, ISP_LOGWARN,
- "giving up on synchronizing the port database");
- return (-1);
- }
-
- /*
- * Save the pertinent info locally.
- */
- lp->node_wwn =
- (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
- (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
- (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
- (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
- (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
- (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
- (((u_int64_t)pdb.pdb_nodename[6]) << 8) |
- (((u_int64_t)pdb.pdb_nodename[7]));
- lp->port_wwn =
- (((u_int64_t)pdb.pdb_portname[0]) << 56) |
- (((u_int64_t)pdb.pdb_portname[1]) << 48) |
- (((u_int64_t)pdb.pdb_portname[2]) << 40) |
- (((u_int64_t)pdb.pdb_portname[3]) << 32) |
- (((u_int64_t)pdb.pdb_portname[4]) << 24) |
- (((u_int64_t)pdb.pdb_portname[5]) << 16) |
- (((u_int64_t)pdb.pdb_portname[6]) << 8) |
- (((u_int64_t)pdb.pdb_portname[7]));
- lp->roles =
- (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
- lp->portid = BITS2WORD(pdb.pdb_portid_bits);
- lp->loopid = pdb.pdb_loopid;
- /*
- * Do a quick check to see whether this matches the saved port
- * database for the same loopid. We do this here to save
- * searching later (if possible). Note that this fails over
- * time as things shuffle on the loop- we get the current
- * loop state (where loop id as an index matches loop id in
- * use) and then compare it to our saved database which
- * never shifts.
- */
- if (target >= 0 && isp_same_lportdb(lp, &fcp->portdb[target])) {
- lp->valid = 1;
}
}
+ fcp->isp_loopstate = LOOP_SYNCING_PDB;
/*
* If we get this far, we've settled our differences with the f/w
- * and we can say that the loop state is ready.
- */
- fcp->isp_loopstate = LOOP_READY;
-
- /*
- * Mark all of the permanent local loop database entries as invalid.
- */
- for (loopid = 0; loopid < prange; loopid++) {
- fcp->portdb[loopid].valid = 0;
- }
-
- /*
- * Now merge our local copy of the port database into our saved copy.
- * Notify the outer layers of new devices arriving.
+ * (for local loop device) and we can say that the loop state is ready.
*/
- for (loopid = 0; loopid < prange; loopid++) {
- int i;
-
- /*
- * If we don't have a non-zero Port WWN, we're not here.
- */
- if (tport[loopid].port_wwn == 0) {
- continue;
- }
-
- /*
- * If we've already marked our tmp copy as valid,
- * this means that we've decided that it's the
- * same as our saved data base. This didn't include
- * the 'valid' marking so we have set that here.
- */
- if (tport[loopid].valid) {
- fcp->portdb[loopid].valid = 1;
- continue;
- }
-
- /*
- * For the purposes of deciding whether this is the
- * 'same' device or not, we only search for an identical
- * Port WWN. Node WWNs may or may not be the same as
- * the Port WWN, and there may be multiple different
- * Port WWNs with the same Node WWN. It would be chaos
- * to have multiple identical Port WWNs, so we don't
- * allow that.
- */
-
- for (i = 0; i < FL_PORT_ID; i++) {
- int j;
- if (fcp->portdb[i].port_wwn == 0)
- continue;
- if (fcp->portdb[i].port_wwn != tport[loopid].port_wwn)
- continue;
- /*
- * We found this WWN elsewhere- it's changed
- * loopids then. We don't change it's actual
- * position in our cached port database- we
- * just change the actual loop ID we'd use.
- */
- if (fcp->portdb[i].loopid != loopid) {
- isp_prt(isp, ISP_LOGINFO, portshift, i,
- fcp->portdb[i].loopid,
- fcp->portdb[i].portid, loopid,
- tport[loopid].portid);
- }
- fcp->portdb[i].portid = tport[loopid].portid;
- fcp->portdb[i].loopid = loopid;
- fcp->portdb[i].valid = 1;
- fcp->portdb[i].roles = tport[loopid].roles;
-
- /*
- * Now make sure this Port WWN doesn't exist elsewhere
- * in the port database.
- */
- for (j = i+1; j < FL_PORT_ID; j++) {
- if (fcp->portdb[i].port_wwn !=
- fcp->portdb[j].port_wwn) {
- continue;
- }
- isp_prt(isp, ISP_LOGWARN, portdup, j, i);
- /*
- * Invalidate the 'old' *and* 'new' ones.
- * This is really harsh and not quite right,
- * but if this happens, we really don't know
- * who is what at this point.
- */
- fcp->portdb[i].valid = 0;
- fcp->portdb[j].valid = 0;
- }
- break;
- }
-
- /*
- * If we didn't traverse the entire port database,
- * then we found (and remapped) an existing entry.
- * No need to notify anyone- go for the next one.
- */
- if (i < FL_PORT_ID) {
- continue;
- }
-
- /*
- * We've not found this Port WWN anywhere. It's a new entry.
- * See if we can leave it where it is (with target == loopid).
- */
- if (fcp->portdb[loopid].port_wwn != 0) {
- for (lim = 0; lim < FL_PORT_ID; lim++) {
- if (fcp->portdb[lim].port_wwn == 0)
- break;
- }
- /* "Cannot Happen" */
- if (lim == FL_PORT_ID) {
- isp_prt(isp, ISP_LOGWARN, "Remap Overflow");
- continue;
- }
- i = lim;
- } else {
- i = loopid;
- }
-
- /*
- * NB: The actual loopid we use here is loopid- we may
- * in fact be at a completely different index (target).
- */
- fcp->portdb[i].loopid = loopid;
- fcp->portdb[i].port_wwn = tport[loopid].port_wwn;
- fcp->portdb[i].node_wwn = tport[loopid].node_wwn;
- fcp->portdb[i].roles = tport[loopid].roles;
- fcp->portdb[i].portid = tport[loopid].portid;
- fcp->portdb[i].valid = 1;
-
- /*
- * Tell the outside world we've arrived.
- */
- (void) isp_async(isp, ISPASYNC_LOGGED_INOUT, &i);
- }
-
- /*
- * Now find all previously used targets that are now invalid and
- * notify the outer layers that they're gone.
- */
- for (lp = fcp->portdb; lp < &fcp->portdb[prange]; lp++) {
- if (lp->valid || lp->port_wwn == 0) {
- continue;
- }
- /*
- * Tell the outside world we've gone away.
- */
- loopid = lp - fcp->portdb;
- (void) isp_async(isp, ISPASYNC_LOGGED_INOUT, &loopid);
- MEMZERO((void *) lp, sizeof (*lp));
+ if (fcp->isp_topo == TOPO_NL_PORT) {
+ fcp->loop_seen_once = 1;
+ fcp->isp_loopstate = LOOP_READY;
+ return (0);
}
-#ifdef ISP2100_FABRIC
/*
* Find all Fabric Entities that didn't make it from one scan to the
- * next and let the world know they went away..
+ * next and let the world know they went away. Scan the whole database.
*/
- for (lp = &fcp->portdb[frange]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
+ for (lp = &fcp->portdb[0]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
if (lp->was_fabric_dev && lp->fabric_dev == 0) {
loopid = lp - fcp->portdb;
- (void) isp_async(isp, ISPASYNC_LOGGED_INOUT, &loopid);
+ lp->valid = 0; /* should already be set */
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
MEMZERO((void *) lp, sizeof (*lp));
continue;
}
lp->was_fabric_dev = lp->fabric_dev;
}
+ if (fcp->isp_topo == TOPO_FL_PORT)
+ base = FC_SNS_ID+1;
+ else
+ base = 0;
+
+ if (fcp->isp_topo == TOPO_N_PORT)
+ lim = 1;
+ else
+ lim = MAX_FC_TARG;
+
/*
- * Now log in any fabric devices
+ * Now log in any fabric devices that the outer layer has
+ * left for us to see. This seems the most sane policy
+ * for the moment.
*/
- for (lp = &fcp->portdb[frange]; lp < &fcp->portdb[MAX_FC_TARG]; lp++) {
+ for (lp = &fcp->portdb[base]; lp < &fcp->portdb[lim]; lp++) {
u_int32_t portid;
mbreg_t mbs;
@@ -1860,6 +1690,11 @@ isp_pdb_sync(isp, target)
}
}
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ return (-1);
+ }
+
/*
* Force a logout if we were logged in.
*/
@@ -1876,6 +1711,10 @@ isp_pdb_sync(isp, target)
lp->portid);
}
lp->loggedin = 0;
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ return (-1);
+ }
}
/*
@@ -1894,6 +1733,10 @@ isp_pdb_sync(isp, target)
}
isp_mboxcmd(isp, &mbs, MBLOGALL & ~(MBOX_LOOP_ID_USED |
MBOX_PORT_ID_USED | MBOX_COMMAND_ERROR));
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ return (-1);
+ }
switch (mbs.param[0]) {
case MBOX_LOOP_ID_USED:
/*
@@ -1950,6 +1793,11 @@ isp_pdb_sync(isp, target)
goto dump_em;
}
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ return (-1);
+ }
+
if (pdb.pdb_loopid != lp->loopid) {
isp_prt(isp, ISP_LOGWARN, pdbmfail1,
lp->portid, pdb.pdb_loopid);
@@ -1988,7 +1836,7 @@ isp_pdb_sync(isp, target)
if (lp->node_wwn && lp->port_wwn) {
lp->valid = 1;
loopid = lp - fcp->portdb;
- (void) isp_async(isp, ISPASYNC_LOGGED_INOUT, &loopid);
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
continue;
}
dump_em:
@@ -2000,18 +1848,291 @@ dump_em:
mbs.param[2] = 0;
mbs.param[3] = 0;
isp_mboxcmd(isp, &mbs, MBLOGNONE);
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_SYNCING_PDB) {
+ return (-1);
+ }
}
-#endif
/*
* If we get here, we've for sure seen not only a valid loop
* but know what is or isn't on it, so mark this for usage
* in isp_start.
*/
fcp->loop_seen_once = 1;
+ fcp->isp_loopstate = LOOP_READY;
+ return (0);
+}
+
+static int
+isp_scan_loop(isp)
+ struct ispsoftc *isp;
+{
+ struct lportdb *lp;
+ fcparam *fcp = isp->isp_param;
+ isp_pdb_t pdb;
+ int loopid, lim, hival;
+
+ switch (fcp->isp_topo) {
+ case TOPO_NL_PORT:
+ hival = FL_PORT_ID;
+ break;
+ case TOPO_N_PORT:
+ hival = 2;
+ break;
+ case TOPO_FL_PORT:
+ hival = FC_PORT_ID;
+ break;
+ default:
+ fcp->isp_loopstate = LOOP_LSCAN_DONE;
+ return (0);
+ }
+ fcp->isp_loopstate = LOOP_SCANNING_LOOP;
+
+ /*
+ * make sure the temp port database is clean...
+ */
+ MEMZERO((void *)fcp->tport, sizeof (fcp->tport));
+
+ /*
+ * Run through the local loop ports and get port database info
+ * for each loop ID.
+ *
+ * There's a somewhat unexplained situation where the f/w passes back
+ * the wrong database entity- if that happens, just restart (up to
+ * FL_PORT_ID times).
+ */
+ for (lim = loopid = 0; loopid < hival; loopid++) {
+ lp = &fcp->tport[loopid];
+
+ /*
+ * Don't even try for ourselves...
+ */
+ if (loopid == fcp->isp_loopid)
+ continue;
+
+ lp->node_wwn = isp_get_portname(isp, loopid, 1);
+ if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
+ return (-1);
+ if (lp->node_wwn == 0)
+ continue;
+ lp->port_wwn = isp_get_portname(isp, loopid, 0);
+ if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
+ return (-1);
+ if (lp->port_wwn == 0) {
+ lp->node_wwn = 0;
+ continue;
+ }
+
+ /*
+ * Get an entry....
+ */
+ if (isp_getpdb(isp, loopid, &pdb) != 0) {
+ if (fcp->isp_loopstate < LOOP_SCANNING_LOOP)
+ return (-1);
+ continue;
+ }
+ if (fcp->isp_loopstate < LOOP_SCANNING_LOOP) {
+ return (-1);
+ }
+
+ /*
+ * If the returned database element doesn't match what we
+ * asked for, restart the process entirely (up to a point...).
+ */
+ if (pdb.pdb_loopid != loopid) {
+ loopid = 0;
+ if (lim++ < hival) {
+ continue;
+ }
+ isp_prt(isp, ISP_LOGWARN,
+ "giving up on synchronizing the port database");
+ return (-1);
+ }
+
+ /*
+ * Save the pertinent info locally.
+ */
+ lp->node_wwn =
+ (((u_int64_t)pdb.pdb_nodename[0]) << 56) |
+ (((u_int64_t)pdb.pdb_nodename[1]) << 48) |
+ (((u_int64_t)pdb.pdb_nodename[2]) << 40) |
+ (((u_int64_t)pdb.pdb_nodename[3]) << 32) |
+ (((u_int64_t)pdb.pdb_nodename[4]) << 24) |
+ (((u_int64_t)pdb.pdb_nodename[5]) << 16) |
+ (((u_int64_t)pdb.pdb_nodename[6]) << 8) |
+ (((u_int64_t)pdb.pdb_nodename[7]));
+ lp->port_wwn =
+ (((u_int64_t)pdb.pdb_portname[0]) << 56) |
+ (((u_int64_t)pdb.pdb_portname[1]) << 48) |
+ (((u_int64_t)pdb.pdb_portname[2]) << 40) |
+ (((u_int64_t)pdb.pdb_portname[3]) << 32) |
+ (((u_int64_t)pdb.pdb_portname[4]) << 24) |
+ (((u_int64_t)pdb.pdb_portname[5]) << 16) |
+ (((u_int64_t)pdb.pdb_portname[6]) << 8) |
+ (((u_int64_t)pdb.pdb_portname[7]));
+ lp->roles =
+ (pdb.pdb_prli_svc3 & SVC3_ROLE_MASK) >> SVC3_ROLE_SHIFT;
+ lp->portid = BITS2WORD(pdb.pdb_portid_bits);
+ lp->loopid = pdb.pdb_loopid;
+ }
+
+ /*
+ * Mark all of the permanent local loop database entries as invalid
+ * (except our own entry).
+ */
+ for (loopid = 0; loopid < hival; loopid++) {
+ if (loopid == fcp->isp_iid) {
+ fcp->portdb[loopid].valid = 1;
+ fcp->portdb[loopid].loopid = fcp->isp_loopid;
+ continue;
+ }
+ fcp->portdb[loopid].valid = 0;
+ }
+
+ /*
+ * Now merge our local copy of the port database into our saved copy.
+ * Notify the outer layers of new devices arriving.
+ */
+ for (loopid = 0; loopid < hival; loopid++) {
+ int i;
+
+ /*
+ * If we don't have a non-zero Port WWN, we're not here.
+ */
+ if (fcp->tport[loopid].port_wwn == 0) {
+ continue;
+ }
+
+ /*
+ * Skip ourselves.
+ */
+ if (loopid == fcp->isp_iid) {
+ continue;
+ }
+
+ /*
+ * For the purposes of deciding whether this is the
+ * 'same' device or not, we only search for an identical
+ * Port WWN. Node WWNs may or may not be the same as
+ * the Port WWN, and there may be multiple different
+ * Port WWNs with the same Node WWN. It would be chaos
+ * to have multiple identical Port WWNs, so we don't
+ * allow that.
+ */
+
+ for (i = 0; i < hival; i++) {
+ int j;
+ if (fcp->portdb[i].port_wwn == 0)
+ continue;
+ if (fcp->portdb[i].port_wwn !=
+ fcp->tport[loopid].port_wwn)
+ continue;
+ /*
+ * We found this WWN elsewhere- it's changed
+ * loopids then. We don't change it's actual
+ * position in our cached port database- we
+ * just change the actual loop ID we'd use.
+ */
+ if (fcp->portdb[i].loopid != loopid) {
+ isp_prt(isp, ISP_LOGINFO, portshift, i,
+ fcp->portdb[i].loopid,
+ fcp->portdb[i].portid, loopid,
+ fcp->tport[loopid].portid);
+ }
+ fcp->portdb[i].portid = fcp->tport[loopid].portid;
+ fcp->portdb[i].loopid = loopid;
+ fcp->portdb[i].valid = 1;
+ fcp->portdb[i].roles = fcp->tport[loopid].roles;
+
+ /*
+ * Now make sure this Port WWN doesn't exist elsewhere
+ * in the port database.
+ */
+ for (j = i+1; j < hival; j++) {
+ if (fcp->portdb[i].port_wwn !=
+ fcp->portdb[j].port_wwn) {
+ continue;
+ }
+ isp_prt(isp, ISP_LOGWARN, portdup, j, i);
+ /*
+ * Invalidate the 'old' *and* 'new' ones.
+ * This is really harsh and not quite right,
+ * but if this happens, we really don't know
+ * who is what at this point.
+ */
+ fcp->portdb[i].valid = 0;
+ fcp->portdb[j].valid = 0;
+ }
+ break;
+ }
+
+ /*
+ * If we didn't traverse the entire port database,
+ * then we found (and remapped) an existing entry.
+ * No need to notify anyone- go for the next one.
+ */
+ if (i < hival) {
+ continue;
+ }
+
+ /*
+ * We've not found this Port WWN anywhere. It's a new entry.
+ * See if we can leave it where it is (with target == loopid).
+ */
+ if (fcp->portdb[loopid].port_wwn != 0) {
+ for (lim = 0; lim < hival; lim++) {
+ if (fcp->portdb[lim].port_wwn == 0)
+ break;
+ }
+ /* "Cannot Happen" */
+ if (lim == hival) {
+ isp_prt(isp, ISP_LOGWARN, "Remap Overflow");
+ continue;
+ }
+ i = lim;
+ } else {
+ i = loopid;
+ }
+
+ /*
+ * NB: The actual loopid we use here is loopid- we may
+ * in fact be at a completely different index (target).
+ */
+ fcp->portdb[i].loopid = loopid;
+ fcp->portdb[i].port_wwn = fcp->tport[loopid].port_wwn;
+ fcp->portdb[i].node_wwn = fcp->tport[loopid].node_wwn;
+ fcp->portdb[i].roles = fcp->tport[loopid].roles;
+ fcp->portdb[i].portid = fcp->tport[loopid].portid;
+ fcp->portdb[i].valid = 1;
+
+ /*
+ * Tell the outside world we've arrived.
+ */
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &i);
+ }
+
+ /*
+ * Now find all previously used targets that are now invalid and
+ * notify the outer layers that they're gone.
+ */
+ for (lp = &fcp->portdb[0]; lp < &fcp->portdb[hival]; lp++) {
+ if (lp->valid || lp->port_wwn == 0) {
+ continue;
+ }
+
+ /*
+ * Tell the outside world we've gone
+ * away and erase our pdb entry.
+ *
+ */
+ loopid = lp - fcp->portdb;
+ (void) isp_async(isp, ISPASYNC_PROMENADE, &loopid);
+ MEMZERO((void *) lp, sizeof (*lp));
+ }
+ fcp->isp_loopstate = LOOP_LSCAN_DONE;
return (0);
}
-#ifdef ISP2100_FABRIC
static int
isp_scan_fabric(isp)
struct ispsoftc *isp;
@@ -2023,6 +2144,11 @@ isp_scan_fabric(isp)
mbreg_t mbs;
int hicap, first_portid_seen;
+ if (fcp->isp_onfabric == 0) {
+ fcp->isp_loopstate = LOOP_FSCAN_DONE;
+ return (0);
+ }
+
reqp = (sns_screq_t *) fcp->isp_scratch;
resp = (sns_scrsp_t *) (&((char *)fcp->isp_scratch)[0x100]);
/*
@@ -2030,8 +2156,9 @@ isp_scan_fabric(isp)
* anything yet with this value.
*/
first_portid = portid = fcp->isp_portid;
+ fcp->isp_loopstate = LOOP_SCANNING_FABRIC;
- for (first_portid_seen = hicap = 0; hicap < 1024; hicap++) {
+ for (first_portid_seen = hicap = 0; hicap < 65535; hicap++) {
MEMZERO((void *) reqp, SNS_GAN_REQ_SIZE);
reqp->snscb_rblen = SNS_GAN_RESP_SIZE >> 1;
reqp->snscb_addr[RQRSP_ADDR0015] =
@@ -2051,22 +2178,32 @@ isp_scan_fabric(isp)
mbs.param[7] = 0;
isp_mboxcmd(isp, &mbs, MBLOGALL);
if (mbs.param[0] != MBOX_COMMAND_COMPLETE) {
+ if (fcp->isp_loopstate == LOOP_SCANNING_FABRIC) {
+ fcp->isp_loopstate = LOOP_PDB_RCVD;
+ }
+ return (-1);
+ }
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate < LOOP_SCANNING_FABRIC) {
return (-1);
}
ISP_UNSWIZZLE_SNS_RSP(isp, resp, SNS_GAN_RESP_SIZE >> 1);
portid = (((u_int32_t) resp->snscb_port_id[0]) << 16) |
(((u_int32_t) resp->snscb_port_id[1]) << 8) |
(((u_int32_t) resp->snscb_port_id[2]));
- if (isp_async(isp, ISPASYNC_FABRIC_DEV, resp)) {
- return (-1);
- }
+ (void) isp_async(isp, ISPASYNC_FABRIC_DEV, resp);
if (first_portid == portid) {
+ fcp->isp_loopstate = LOOP_FSCAN_DONE;
return (0);
}
}
+
+ isp_prt(isp, ISP_LOGWARN, "broken fabric nameserver...*wheeze*...");
+
/*
* We either have a broken name server or a huge fabric if we get here.
*/
+ fcp->isp_loopstate = LOOP_FSCAN_DONE;
return (0);
}
@@ -2102,7 +2239,7 @@ isp_register_fc4_type(struct ispsoftc *isp)
isp_prt(isp, ISP_LOGDEBUG0, "Register FC4 types succeeded");
}
}
-#endif
+
/*
* Start a command. Locking is assumed done in the caller.
*/
@@ -2167,7 +2304,32 @@ isp_start(xs)
if (IS_FC(isp)) {
fcparam *fcp = isp->isp_param;
struct lportdb *lp;
-#if defined(ISP2100_FABRIC)
+ /*
+ * Check for f/w being in ready state. If the f/w
+ * isn't in ready state, then we don't know our
+ * loop ID and the f/w hasn't completed logging
+ * into all targets on the loop. If this is the
+ * case, then bounce the command. We pretend this is
+ * a SELECTION TIMEOUT error if we've never gone to
+ * FW_READY state at all- in this case we may not
+ * be hooked to a loop at all and we shouldn't hang
+ * the machine for this. Otherwise, defer this command
+ * until later.
+ */
+ if (fcp->isp_fwstate != FW_READY) {
+ /*
+ * Give ourselves at most a 250ms delay.
+ */
+ if (isp_fclink_test(isp, 250000)) {
+ XS_SETERR(xs, HBA_SELTIMEOUT);
+ if (fcp->loop_seen_once) {
+ return (CMD_RQLATER);
+ } else {
+ return (CMD_COMPLETE);
+ }
+ }
+ }
+
/*
* If we're not on a Fabric, we can't have a target
* above FL_PORT_ID-1.
@@ -2194,32 +2356,6 @@ isp_start(xs)
return (CMD_COMPLETE);
}
}
-#endif
- /*
- * Check for f/w being in ready state. If the f/w
- * isn't in ready state, then we don't know our
- * loop ID and the f/w hasn't completed logging
- * into all targets on the loop. If this is the
- * case, then bounce the command. We pretend this is
- * a SELECTION TIMEOUT error if we've never gone to
- * FW_READY state at all- in this case we may not
- * be hooked to a loop at all and we shouldn't hang
- * the machine for this. Otherwise, defer this command
- * until later.
- */
- if (fcp->isp_fwstate != FW_READY) {
- /*
- * Give ourselves at most a 250ms delay.
- */
- if (isp_fclink_test(isp, 250000)) {
- XS_SETERR(xs, HBA_SELTIMEOUT);
- if (fcp->loop_seen_once) {
- return (CMD_RQLATER);
- } else {
- return (CMD_COMPLETE);
- }
- }
- }
/*
* If our loop state is such that we haven't yet received
@@ -2239,15 +2375,44 @@ isp_start(xs)
}
/*
+ * If we're in the middle of loop or fabric scanning
+ * or merging the port databases, retry this command later.
+ */
+ if (fcp->isp_loopstate == LOOP_SCANNING_FABRIC ||
+ fcp->isp_loopstate == LOOP_SCANNING_LOOP ||
+ fcp->isp_loopstate == LOOP_SYNCING_PDB) {
+ return (CMD_RQLATER);
+ }
+
+ /*
+ * If our loop state is now such that we've just now
+ * received a Port Database Change notification, then
+ * we have to go off and (re)scan the fabric. We back
+ * out and try again later if this doesn't work.
+ */
+ if (fcp->isp_loopstate == LOOP_PDB_RCVD && fcp->isp_onfabric) {
+ if (isp_scan_fabric(isp)) {
+ return (CMD_RQLATER);
+ }
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate < LOOP_PDB_RCVD) {
+ return (CMD_RQLATER);
+ }
+ }
+
+ /*
* If our loop state is now such that we've just now
* received a Port Database Change notification, then
* we have to go off and (re)synchronize our port
* database.
*/
- if (fcp->isp_loopstate == LOOP_PDB_RCVD) {
- if (isp_pdb_sync(isp, target)) {
- XS_SETERR(xs, HBA_SELTIMEOUT);
- return (CMD_COMPLETE);
+ if (fcp->isp_loopstate < LOOP_READY) {
+ if (isp_pdb_sync(isp)) {
+ return (CMD_RQLATER);
+ }
+ if (fcp->isp_fwstate != FW_READY ||
+ fcp->isp_loopstate != LOOP_READY) {
+ return (CMD_RQLATER);
}
}
@@ -2271,7 +2436,7 @@ isp_start(xs)
return (CMD_COMPLETE);
}
/*
- * Now turn target into what the actual loop ID is.
+ * Now turn target into what the actual Loop ID is.
*/
target = lp->loopid;
}
@@ -2518,21 +2683,57 @@ isp_control(isp, ctl, arg)
break;
case ISPCTL_UPDATE_PARAMS:
+
isp_update(isp);
return (0);
case ISPCTL_FCLINK_TEST:
+
if (IS_FC(isp)) {
int usdelay = (arg)? *((int *) arg) : 250000;
return (isp_fclink_test(isp, usdelay));
}
break;
+ case ISPCTL_SCAN_FABRIC:
+
+ if (IS_FC(isp)) {
+ return (isp_scan_fabric(isp));
+ }
+ break;
+
+ case ISPCTL_SCAN_LOOP:
+
+ if (IS_FC(isp)) {
+ return (isp_scan_loop(isp));
+ }
+ break;
+
case ISPCTL_PDB_SYNC:
+
+ if (IS_FC(isp)) {
+ return (isp_pdb_sync(isp));
+ }
+ break;
+
+ case ISPCTL_SEND_LIP:
+
if (IS_FC(isp)) {
- return (isp_pdb_sync(isp, -1));
+ mbs.param[0] = MBOX_INIT_LIP;
+ isp_mboxcmd(isp, &mbs, MBLOGALL);
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
+ return (0);
+ }
+ }
+ break;
+
+ case ISPCTL_GET_POSMAP:
+
+ if (IS_FC(isp) && arg) {
+ return (isp_getmap(isp, arg));
}
break;
+
#ifdef ISP_TARGET_MODE
case ISPCTL_TOGGLE_TMODE:
{
@@ -2862,6 +3063,8 @@ isp_intr(arg)
(*XS_STSP(xs) == SCSI_CHECK) &&
(sp->req_scsi_status & RQCS_SV)) {
XS_SAVE_SENSE(xs, sp);
+ /* solely for the benefit of debug */
+ sp->req_state_flags |= RQSF_GOT_SENSE;
}
}
isp_prt(isp, ISP_LOGDEBUG2,
@@ -2898,14 +3101,15 @@ isp_intr(arg)
}
if (((isp->isp_dblev & (ISP_LOGDEBUG2|ISP_LOGDEBUG3))) ||
- ((isp->isp_dblev & ISP_LOGDEBUG1) && !XS_NOERR(xs))) {
+ ((isp->isp_dblev & ISP_LOGDEBUG1) && ((!XS_NOERR(xs)) ||
+ (*XS_STSP(xs) != SCSI_GOOD)))) {
char skey;
if (sp->req_state_flags & RQSF_GOT_SENSE) {
skey = XS_SNSKEY(xs) & 0xf;
if (skey < 10)
skey += '0';
else
- skey += 'a';
+ skey += 'a' - 10;
} else if (*XS_STSP(xs) == SCSI_CHECK) {
skey = '?';
} else {
@@ -2977,7 +3181,7 @@ isp_parse_async(isp, mbox)
"Internal FW Error @ RISC Addr 0x%x", mbox);
isp_reinit(isp);
#ifdef ISP_TARGET_MODE
- isp_target_async(isp, bus, mbox);
+ isp_target_async(isp, bus, ASYNC_SYSTEM_ERROR);
#endif
/* no point continuing after this */
return (-1);
@@ -3140,7 +3344,7 @@ isp_parse_async(isp, mbox)
isp->isp_sendmarker = 1;
FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
isp_mark_getpdb_all(isp);
- isp_async(isp, ISPASYNC_CHANGE_NOTIFY, (void *) 0);
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_PDB);
break;
case ASYNC_CHANGE_NOTIFY:
@@ -3149,18 +3353,19 @@ isp_parse_async(isp, mbox)
*/
FCPARAM(isp)->isp_loopstate = LOOP_PDB_RCVD;
isp_mark_getpdb_all(isp);
- isp_async(isp, ISPASYNC_CHANGE_NOTIFY, (void *) 1);
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_SNS);
break;
case ASYNC_PTPMODE:
if (FCPARAM(isp)->isp_onfabric)
- FCPARAM(isp)->isp_topo = TOPO_N_PORT;
- else
FCPARAM(isp)->isp_topo = TOPO_F_PORT;
+ else
+ FCPARAM(isp)->isp_topo = TOPO_N_PORT;
isp_mark_getpdb_all(isp);
isp->isp_sendmarker = 1;
FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER);
#ifdef ISP_TARGET_MODE
isp_target_async(isp, bus, mbox);
#endif
@@ -3169,16 +3374,19 @@ isp_parse_async(isp, mbox)
case ASYNC_CONNMODE:
mbox = ISP_READ(isp, OUTMAILBOX1);
+ isp_mark_getpdb_all(isp);
switch (mbox) {
case ISP_CONN_LOOP:
- isp_prt(isp, ISP_LOGINFO, "Point-to-Point->Loop mode");
+ isp_prt(isp, ISP_LOGINFO,
+ "Point-to-Point -> Loop mode");
break;
case ISP_CONN_PTP:
- isp_prt(isp, ISP_LOGINFO, "Loop->Point-to-Point mode");
+ isp_prt(isp, ISP_LOGINFO,
+ "Loop -> Point-to-Point mode");
break;
case ISP_CONN_BADLIP:
isp_prt(isp, ISP_LOGWARN,
- "Point-to-Point->Loop mode (1)");
+ "Point-to-Point -> Loop mode (BAD LIP)");
break;
case ISP_CONN_FATAL:
isp_prt(isp, ISP_LOGERR, "FATAL CONNECTION ERROR");
@@ -3188,11 +3396,19 @@ isp_parse_async(isp, mbox)
#endif
/* no point continuing after this */
return (-1);
-
case ISP_CONN_LOOPBACK:
isp_prt(isp, ISP_LOGWARN,
"Looped Back in Point-to-Point mode");
+ break;
+ default:
+ isp_prt(isp, ISP_LOGWARN,
+ "Unknown connection mode (0x%x)", mbox);
+ break;
}
+ isp_async(isp, ISPASYNC_CHANGE_NOTIFY, ISPASYNC_CHANGE_OTHER);
+ isp->isp_sendmarker = 1;
+ FCPARAM(isp)->isp_fwstate = FW_CONFIG_WAIT;
+ FCPARAM(isp)->isp_loopstate = LOOP_LIP_RCVD;
break;
default:
@@ -4219,8 +4435,9 @@ isp_fw_state(isp)
mbs.param[0] = MBOX_GET_FW_STATE;
isp_mboxcmd(isp, &mbs, MBLOGALL);
- if (mbs.param[0] == MBOX_COMMAND_COMPLETE)
+ if (mbs.param[0] == MBOX_COMMAND_COMPLETE) {
fcp->isp_fwstate = mbs.param[1];
+ }
}
}
@@ -4628,15 +4845,21 @@ isp_reinit(isp)
u_int32_t handle;
isp_reset(isp);
- if (isp->isp_state == ISP_RESETSTATE) {
- isp_init(isp);
- if (isp->isp_state == ISP_INITSTATE) {
- isp->isp_state = ISP_RUNSTATE;
- }
+ if (isp->isp_state != ISP_RESETSTATE) {
+ isp_prt(isp, ISP_LOGERR, "isp_reinit cannot reset card");
+ goto skip;
+ }
+ isp_init(isp);
+ if (isp->isp_role == ISP_ROLE_NONE) {
+ goto skip;
+ }
+ if (isp->isp_state == ISP_INITSTATE) {
+ isp->isp_state = ISP_RUNSTATE;
}
if (isp->isp_state != ISP_RUNSTATE) {
- isp_prt(isp, ISP_LOGERR, "isp_reinit cannot restart ISP");
+ isp_prt(isp, ISP_LOGERR, "isp_reinit cannot restart card");
}
+skip:
isp->isp_nactive = 0;
for (handle = 1; (int) handle <= isp->isp_maxcmds; handle++) {