aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/isp
diff options
context:
space:
mode:
authorKenneth D. Merry <ken@FreeBSD.org>2017-05-03 13:17:01 +0000
committerKenneth D. Merry <ken@FreeBSD.org>2017-05-03 13:17:01 +0000
commit57b6261f94d5b633223299f7b1e12f6ec8275baa (patch)
tree6e9f55a6dc8ad861b4f43d2d1332ad11e3d5d784 /sys/dev/isp
parentfca08fe6a22dbe2f868bb13f9b62e775a4a57ed0 (diff)
Notes
Diffstat (limited to 'sys/dev/isp')
-rw-r--r--sys/dev/isp/isp.c7
-rw-r--r--sys/dev/isp/isp_freebsd.c130
-rw-r--r--sys/dev/isp/ispmbox.h6
-rw-r--r--sys/dev/isp/ispvar.h4
4 files changed, 111 insertions, 36 deletions
diff --git a/sys/dev/isp/isp.c b/sys/dev/isp/isp.c
index c75b95ed55ec..f1fb54ad118e 100644
--- a/sys/dev/isp/isp.c
+++ b/sys/dev/isp/isp.c
@@ -2771,10 +2771,11 @@ isp_getpdb(ispsoftc_t *isp, int chan, uint16_t id, isp_pdb_t *pdb)
pdb->portid = BITS2WORD_24XX(un.bill.pdb_portid_bits);
ISP_MEMCPY(pdb->portname, un.bill.pdb_portname, 8);
ISP_MEMCPY(pdb->nodename, un.bill.pdb_nodename, 8);
- isp_prt(isp, ISP_LOGDEBUG1,
- "Chan %d handle 0x%x Port 0x%06x flags 0x%x curstate %x",
+ isp_prt(isp, ISP_LOGDEBUG0,
+ "Chan %d handle 0x%x Port 0x%06x flags 0x%x curstate %x laststate %x",
chan, id, pdb->portid, un.bill.pdb_flags,
- un.bill.pdb_curstate);
+ un.bill.pdb_curstate, un.bill.pdb_laststate);
+
if (un.bill.pdb_curstate < PDB2400_STATE_PLOGI_DONE || un.bill.pdb_curstate > PDB2400_STATE_LOGGED_IN) {
mbs.param[0] = MBOX_NOT_LOGGED_IN;
return (mbs.param[0]);
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c
index 5ce037a154c7..4b9e0b386bca 100644
--- a/sys/dev/isp/isp_freebsd.c
+++ b/sys/dev/isp/isp_freebsd.c
@@ -2567,8 +2567,7 @@ isp_watchdog(void *arg)
}
isp_destroy_handle(isp, handle);
isp_prt(isp, ISP_LOGERR, "%s: timeout for handle 0x%x", __func__, handle);
- xs->ccb_h.status &= ~CAM_STATUS_MASK;
- xs->ccb_h.status |= CAM_CMD_TIMEOUT;
+ XS_SETERR(xs, CAM_CMD_TIMEOUT);
isp_done(xs);
} else {
if (ohandle != ISP_HANDLE_FREE) {
@@ -2739,9 +2738,6 @@ isp_loop_dead(ispsoftc_t *isp, int chan)
if (lp->state == FC_PORTDB_STATE_NIL)
continue;
- /*
- * XXX: CLEAN UP AND COMPLETE ANY PENDING COMMANDS FIRST!
- */
for (i = 0; i < isp->isp_maxcmds; i++) {
struct ccb_scsiio *xs;
@@ -2757,6 +2753,25 @@ isp_loop_dead(ispsoftc_t *isp, int chan)
isp_prt(isp, ISP_LOGWARN, "command handle 0x%x for %d.%d.%jx orphaned by loop down timeout",
isp->isp_xflist[i].handle, chan, XS_TGT(xs),
(uintmax_t)XS_LUN(xs));
+
+ /*
+ * Just like in isp_watchdog, abort the outstanding
+ * command or immediately free its resources if it is
+ * not active
+ */
+ if (isp_control(isp, ISPCTL_ABORT_CMD, xs) == 0) {
+ continue;
+ }
+
+ if (XS_XFRLEN(xs)) {
+ ISP_DMAFREE(isp, xs, isp->isp_xflist[i].handle);
+ }
+ isp_destroy_handle(isp, isp->isp_xflist[i].handle);
+ isp_prt(isp, ISP_LOGWARN, "command handle 0x%x for %d.%d.%jx could not be aborted and was destroyed",
+ isp->isp_xflist[i].handle, chan, XS_TGT(xs),
+ (uintmax_t)XS_LUN(xs));
+ XS_SETERR(xs, HBA_BUSRESET);
+ isp_done(xs);
}
isp_prt(isp, ISP_LOGCONFIG, prom3, chan, dbidx, lp->portid, "Loop Down Timeout");
@@ -3562,7 +3577,7 @@ isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
static const char prom[] = "Chan %d [%d] WWPN 0x%16jx PortID 0x%06x handle 0x%x %s %s";
char buf[64];
char *msg = NULL;
- target_id_t tgt;
+ target_id_t tgt = 0;
fcportdb_t *lp;
struct isp_fc *fc;
struct cam_path *tmppath;
@@ -3639,30 +3654,50 @@ isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
}
break;
}
+ case ISPASYNC_LOOP_RESET:
+ {
+ uint16_t lipp;
+ fcparam *fcp;
+ va_start(ap, cmd);
+ bus = va_arg(ap, int);
+ va_end(ap);
+
+ lipp = ISP_READ(isp, OUTMAILBOX1);
+ fcp = FCPARAM(isp, bus);
+
+ isp_prt(isp, ISP_LOGINFO, "Chan %d LOOP Reset, LIP primitive %x", bus, lipp);
+ /*
+ * Per FCP-4, a Reset LIP should result in a CRN reset. Other
+ * LIPs and loop up/down events should never reset the CRN. For
+ * an as of yet unknown reason, 24xx series cards (and
+ * potentially others) can interrupt with a LIP Reset status
+ * when no LIP reset came down the wire. Additionally, the LIP
+ * primitive accompanying this status would not be a valid LIP
+ * Reset primitive, but some variation of an invalid AL_PA
+ * LIP. As a result, we have to verify the AL_PD in the LIP
+ * addresses our port before blindly resetting.
+ */
+ if (FCP_IS_DEST_ALPD(fcp, (lipp & 0x00FF)))
+ isp_fcp_reset_crn(isp, bus, /*tgt*/0, /*tgt_set*/ 0);
+ isp_loop_changed(isp, bus);
+ break;
+ }
case ISPASYNC_LIP:
if (msg == NULL)
msg = "LIP Received";
/* FALLTHROUGH */
- case ISPASYNC_LOOP_RESET:
- if (msg == NULL)
- msg = "LOOP Reset";
- /* FALLTHROUGH */
case ISPASYNC_LOOP_DOWN:
if (msg == NULL)
msg = "LOOP Down";
- va_start(ap, cmd);
- bus = va_arg(ap, int);
- va_end(ap);
- isp_fcp_reset_crn(isp, bus, /*tgt*/0, /*tgt_set*/ 0);
- isp_loop_changed(isp, bus);
- isp_prt(isp, ISP_LOGINFO, "Chan %d %s", bus, msg);
- break;
+ /* FALLTHROUGH */
case ISPASYNC_LOOP_UP:
+ if (msg == NULL)
+ msg = "LOOP Up";
va_start(ap, cmd);
bus = va_arg(ap, int);
va_end(ap);
isp_loop_changed(isp, bus);
- isp_prt(isp, ISP_LOGINFO, "Chan %d Loop UP", bus);
+ isp_prt(isp, ISP_LOGINFO, "Chan %d %s", bus, msg);
break;
case ISPASYNC_DEV_ARRIVED:
va_start(ap, cmd);
@@ -3692,6 +3727,7 @@ isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
}
break;
case ISPASYNC_DEV_CHANGED:
+ case ISPASYNC_DEV_STAYED:
va_start(ap, cmd);
bus = va_arg(ap, int);
lp = va_arg(ap, fcportdb_t *);
@@ -3699,18 +3735,23 @@ isp_async(ispsoftc_t *isp, ispasync_t cmd, ...)
fc = ISP_FC_PC(isp, bus);
tgt = FC_PORTDB_TGT(isp, bus, lp);
isp_gen_role_str(buf, sizeof (buf), lp->new_prli_word3);
- isp_prt(isp, ISP_LOGCONFIG, prom, bus, tgt, lp->port_wwn, lp->new_portid, lp->handle, buf, "changed");
-changed:
+ if (cmd == ISPASYNC_DEV_CHANGED)
+ isp_prt(isp, ISP_LOGCONFIG, prom, bus, tgt, lp->port_wwn, lp->new_portid, lp->handle, buf, "changed");
+ else
+ isp_prt(isp, ISP_LOGCONFIG, prom, bus, tgt, lp->port_wwn, lp->portid, lp->handle, buf, "stayed");
+
if (lp->is_target !=
((FCPARAM(isp, bus)->role & ISP_ROLE_INITIATOR) &&
(lp->new_prli_word3 & PRLI_WD3_TARGET_FUNCTION))) {
lp->is_target = !lp->is_target;
if (lp->is_target) {
- isp_fcp_reset_crn(isp, bus, tgt, /*tgt_set*/ 1);
+ if (cmd == ISPASYNC_DEV_CHANGED)
+ isp_fcp_reset_crn(isp, bus, tgt, /*tgt_set*/ 1);
isp_make_here(isp, lp, bus, tgt);
} else {
isp_make_gone(isp, lp, bus, tgt);
- isp_fcp_reset_crn(isp, bus, tgt, /*tgt_set*/ 1);
+ if (cmd == ISPASYNC_DEV_CHANGED)
+ isp_fcp_reset_crn(isp, bus, tgt, /*tgt_set*/ 1);
}
}
if (lp->is_initiator !=
@@ -3726,16 +3767,6 @@ changed:
xpt_async(AC_CONTRACT, fc->path, &ac);
}
break;
- case ISPASYNC_DEV_STAYED:
- va_start(ap, cmd);
- bus = va_arg(ap, int);
- lp = va_arg(ap, fcportdb_t *);
- va_end(ap);
- fc = ISP_FC_PC(isp, bus);
- tgt = FC_PORTDB_TGT(isp, bus, lp);
- isp_gen_role_str(buf, sizeof (buf), lp->prli_word3);
- isp_prt(isp, ISP_LOGCONFIG, prom, bus, tgt, lp->port_wwn, lp->portid, lp->handle, buf, "stayed");
- goto changed;
case ISPASYNC_DEV_GONE:
va_start(ap, cmd);
bus = va_arg(ap, int);
@@ -3781,10 +3812,45 @@ changed:
va_end(ap);
if (evt == ISPASYNC_CHANGE_PDB) {
+ int tgt_set = 0;
msg = "Port Database Changed";
isp_prt(isp, ISP_LOGINFO,
"Chan %d %s (nphdl 0x%x state 0x%x reason 0x%x)",
bus, msg, nphdl, nlstate, reason);
+ /*
+ * Port database syncs are not sufficient for
+ * determining that logins or logouts are done on the
+ * loop, but this information is directly available from
+ * the reason code from the incoming mbox. We must reset
+ * the fcp crn on these events according to FCP-4
+ */
+ switch (reason) {
+ case PDB24XX_AE_IMPL_LOGO_1:
+ case PDB24XX_AE_IMPL_LOGO_2:
+ case PDB24XX_AE_IMPL_LOGO_3:
+ case PDB24XX_AE_PLOGI_RCVD:
+ case PDB24XX_AE_PRLI_RCVD:
+ case PDB24XX_AE_PRLO_RCVD:
+ case PDB24XX_AE_LOGO_RCVD:
+ case PDB24XX_AE_PLOGI_DONE:
+ case PDB24XX_AE_PRLI_DONE:
+ /*
+ * If the event is not global, twiddle tgt and
+ * tgt_set to nominate only the target
+ * associated with the nphdl.
+ */
+ if (nphdl != PDB24XX_AE_GLOBAL) {
+ /* Break if we don't yet have the pdb */
+ if (!isp_find_pdb_by_handle(isp, bus, nphdl, &lp))
+ break;
+ tgt = FC_PORTDB_TGT(isp, bus, lp);
+ tgt_set = 1;
+ }
+ isp_fcp_reset_crn(isp, bus, tgt, tgt_set);
+ break;
+ default:
+ break; /* NOP */
+ }
} else if (evt == ISPASYNC_CHANGE_SNS) {
msg = "Name Server Database Changed";
isp_prt(isp, ISP_LOGINFO, "Chan %d %s (PortID 0x%06x)",
diff --git a/sys/dev/isp/ispmbox.h b/sys/dev/isp/ispmbox.h
index f672b429b59f..e747c5544fbc 100644
--- a/sys/dev/isp/ispmbox.h
+++ b/sys/dev/isp/ispmbox.h
@@ -1421,6 +1421,10 @@ typedef struct {
/*
* Port Database Changed Async Event information for 24XX cards
*/
+/* N-Port Handle */
+#define PDB24XX_AE_GLOBAL 0xFFFF
+
+/* Reason Codes */
#define PDB24XX_AE_OK 0x00
#define PDB24XX_AE_IMPL_LOGO_1 0x01
#define PDB24XX_AE_IMPL_LOGO_2 0x02
@@ -1440,7 +1444,7 @@ typedef struct {
#define PDB24XX_AE_FLOGI_TIMO 0x10
#define PDB24XX_AE_ABX_LOGO 0x11
#define PDB24XX_AE_PLOGI_DONE 0x12
-#define PDB24XX_AE_PRLI_DONJE 0x13
+#define PDB24XX_AE_PRLI_DONE 0x13
#define PDB24XX_AE_OPN_1 0x14
#define PDB24XX_AE_OPN_2 0x15
#define PDB24XX_AE_TXERR 0x16
diff --git a/sys/dev/isp/ispvar.h b/sys/dev/isp/ispvar.h
index a19430ca1261..89c132b39875 100644
--- a/sys/dev/isp/ispvar.h
+++ b/sys/dev/isp/ispvar.h
@@ -502,6 +502,10 @@ typedef struct {
#define TOPO_IS_FABRIC(x) ((x) == TOPO_FL_PORT || (x) == TOPO_F_PORT)
+#define FCP_AL_DA_ALL 0xFF
+#define FCP_AL_PA(fcp) ((uint8_t)(fcp->isp_portid))
+#define FCP_IS_DEST_ALPD(fcp, alpd) (FCP_AL_PA((fcp)) == FCP_AL_DA_ALL || FCP_AL_PA((fcp)) == alpd)
+
/*
* Soft Structure per host adapter
*/