[SCSI] bnx2i: Optimized the bnx2i_stop connection clean up procedure

For cases where the iSCSI disconnection procedure times out due to
the iSCSI daemon being slow or unresponsive, the bnx2i_stop routine
will now perform hardware cleanup via bnx2i_hw_ep_disconnect on all
active endpoints so that subsequent operations will perform properly.
Also moved the mutex locks inside ep_connect and ep_disconnect so
that proper exclusivity can resolve simultaneous calls to the
ep_disconnect routine.

v2: Removed the unnecessary read lock in the bnx2i_stop

Signed-off-by: Eddie Wai <eddie.wai@broadcom.com>
Reviewed-by: Michael Chan <mchan@broadcom.com>
Reviewed-by: Benjamin Li <benli@broadcom.com>
Acked-by: Anil Veerabhadrappa <anilgv@broadcom.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
diff --git a/drivers/scsi/bnx2i/bnx2i_init.c b/drivers/scsi/bnx2i/bnx2i_init.c
index af6a00a..f0f8361 100644
--- a/drivers/scsi/bnx2i/bnx2i_init.c
+++ b/drivers/scsi/bnx2i/bnx2i_init.c
@@ -176,6 +176,9 @@
 void bnx2i_stop(void *handle)
 {
 	struct bnx2i_hba *hba = handle;
+	struct list_head *pos, *tmp;
+	struct bnx2i_endpoint *bnx2i_ep;
+	int conns_active;
 
 	/* check if cleanup happened in GOING_DOWN context */
 	if (!test_and_clear_bit(ADAPTER_STATE_GOING_DOWN,
@@ -187,9 +190,33 @@
 	 *  control returns to network driver. So it is required to cleanup and
 	 * release all connection resources before returning from this routine.
 	 */
-	wait_event_interruptible_timeout(hba->eh_wait,
-					 (hba->ofld_conns_active == 0),
-					 hba->hba_shutdown_tmo);
+	while (hba->ofld_conns_active) {
+		conns_active = hba->ofld_conns_active;
+		wait_event_interruptible_timeout(hba->eh_wait,
+				(hba->ofld_conns_active != conns_active),
+				hba->hba_shutdown_tmo);
+		if (hba->ofld_conns_active == conns_active)
+			break;
+	}
+	if (hba->ofld_conns_active) {
+		/* Stage to force the disconnection
+		 * This is the case where the daemon is either slow or
+		 * not present
+		 */
+		printk(KERN_ALERT "bnx2i: Wait timeout, force all eps "
+			"to disconnect (%d)\n", hba->ofld_conns_active);
+		mutex_lock(&hba->net_dev_lock);
+		list_for_each_safe(pos, tmp, &hba->ep_active_list) {
+			bnx2i_ep = list_entry(pos, struct bnx2i_endpoint, link);
+			/* Clean up the chip only */
+			bnx2i_hw_ep_disconnect(bnx2i_ep);
+		}
+		mutex_unlock(&hba->net_dev_lock);
+		if (hba->ofld_conns_active)
+			printk(KERN_ERR "bnx2i: EP disconnect timeout (%d)!\n",
+				hba->ofld_conns_active);
+	}
+
 	/* This flag should be cleared last so that ep_disconnect() gracefully
 	 * cleans up connection context
 	 */