[SCSI] qla2xxx: Handle outstanding mbx cmds on hung f/w scenarios.

Outstanding mailbox commands, have no way to recover on f/w hung, and we
timeout on waiting for mbx response. This in turn affects the recovery process
as follows:
- We might already be in dpc while waiting for mbx to complete, so recovery for
that pci function will never get invoked. Reset Timeout (10 sec) is far less
than mbx timeout (30 sec).
- Other mbx cmds will get stuck due to serial mbx access.

Solution is to identify fw-hung scenario and handle outstanding mbx commands to
have an early-exit instead of waiting for response.
Other mbx commands waiting for access will also do an early-exit if fw-hung is
still applicable.

Signed-off-by: Giridhar Malavali <giridhar.malavali@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
index 10f4815..2f39e30 100644
--- a/drivers/scsi/qla2xxx/qla_mbx.c
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -37,7 +37,7 @@
 	device_reg_t __iomem *reg;
 	uint8_t		abort_active;
 	uint8_t		io_lock_on;
-	uint16_t	command;
+	uint16_t	command = 0;
 	uint16_t	*iptr;
 	uint16_t __iomem *optr;
 	uint32_t	cnt;
@@ -83,6 +83,13 @@
 		return QLA_FUNCTION_TIMEOUT;
 	}
 
+	if (IS_QLA82XX(ha) && ha->flags.fw_hung) {
+		/* Setting Link-Down error */
+		mcp->mb[0] = MBS_LINK_DOWN_ERROR;
+		rval = QLA_FUNCTION_FAILED;
+		goto premature_exit;
+	}
+
 	ha->flags.mbox_busy = 1;
 	/* Save mailbox command for debug */
 	ha->mcp = mcp;
@@ -151,7 +158,8 @@
 				DEBUG2_3_11(printk(KERN_INFO
 				    "%s(%ld): Pending Mailbox timeout. "
 				    "Exiting.\n", __func__, base_vha->host_no));
-				return QLA_FUNCTION_TIMEOUT;
+				rval = QLA_FUNCTION_TIMEOUT;
+				goto premature_exit;
 			}
 			WRT_REG_DWORD(&reg->isp82.hint, HINT_MBX_INT_PENDING);
 		} else if (IS_FWI2_CAPABLE(ha))
@@ -176,7 +184,8 @@
 				DEBUG2_3_11(printk(KERN_INFO
 				    "%s(%ld): Pending Mailbox timeout. "
 				    "Exiting.\n", __func__, base_vha->host_no));
-				return QLA_FUNCTION_TIMEOUT;
+				rval = QLA_FUNCTION_TIMEOUT;
+				goto premature_exit;
 			}
 			WRT_REG_DWORD(&reg->isp82.hint, HINT_MBX_INT_PENDING);
 		} else if (IS_FWI2_CAPABLE(ha))
@@ -214,6 +223,15 @@
 		ha->flags.mbox_int = 0;
 		clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
+		if (IS_QLA82XX(ha) && ha->flags.fw_hung) {
+			ha->flags.mbox_busy = 0;
+			/* Setting Link-Down error */
+			mcp->mb[0] = MBS_LINK_DOWN_ERROR;
+			ha->mcp = NULL;
+			rval = QLA_FUNCTION_FAILED;
+			goto premature_exit;
+		}
+
 		if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE)
 			rval = QLA_FUNCTION_FAILED;
 
@@ -279,35 +297,51 @@
 			DEBUG2_3_11(printk("%s(%ld): timeout schedule "
 			"isp_abort_needed.\n", __func__,
 			base_vha->host_no));
-			qla_printk(KERN_WARNING, ha,
-			    "Mailbox command timeout occurred. Scheduling ISP "
-			    "abort. eeh_busy: 0x%x\n", ha->flags.eeh_busy);
-			set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
-			qla2xxx_wake_dpc(vha);
+
+			if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
+			    !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
+			    !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+
+				qla_printk(KERN_WARNING, ha,
+				    "Mailbox command timeout occured. "
+				    "Scheduling ISP " "abort. eeh_busy: 0x%x\n",
+				    ha->flags.eeh_busy);
+				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+				qla2xxx_wake_dpc(vha);
+			}
 		} else if (!abort_active) {
 			/* call abort directly since we are in the DPC thread */
 			DEBUG(printk("%s(%ld): timeout calling abort_isp\n",
 			    __func__, base_vha->host_no));
 			DEBUG2_3_11(printk("%s(%ld): timeout calling "
 			    "abort_isp\n", __func__, base_vha->host_no));
-			qla_printk(KERN_WARNING, ha,
-			    "Mailbox command timeout occurred. Issuing ISP "
-			    "abort.\n");
 
-			set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
-			clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
-			if (ha->isp_ops->abort_isp(base_vha)) {
-				/* Failed. retry later. */
-				set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+			if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
+			    !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
+			    !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+
+				qla_printk(KERN_WARNING, ha,
+				    "Mailbox command timeout occured. "
+				    "Issuing ISP abort.\n");
+
+				set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
+				clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+				if (ha->isp_ops->abort_isp(vha)) {
+					/* Failed. retry later. */
+					set_bit(ISP_ABORT_NEEDED,
+					    &vha->dpc_flags);
+				}
+				clear_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
+				DEBUG(printk("%s(%ld): finished abort_isp\n",
+				    __func__, vha->host_no));
+				DEBUG2_3_11(printk(
+				    "%s(%ld): finished abort_isp\n",
+				    __func__, vha->host_no));
 			}
-			clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
-			DEBUG(printk("%s(%ld): finished abort_isp\n", __func__,
-			    base_vha->host_no));
-			DEBUG2_3_11(printk("%s(%ld): finished abort_isp\n",
-			    __func__, base_vha->host_no));
 		}
 	}
 
+premature_exit:
 	/* Allow next mbx cmd to come in. */
 	complete(&ha->mbx_cmd_comp);