[SCSI] bnx2fc: Fix NULL pointer deref during arm_cq.

There exists a race condition between CQ doorbell unmap and IO completion path
that arms the CQ which causes a NULL dereference. Protect the ctx_base with
cq_lock to avoid this. Also, wait for the CQ doorbell to be successfully mapped
before arming the CQ.

Also, do not count uncolicited CQ completions for free_sqes.

Signed-off-by: Bhanu Prakash Gollapudi <bprakash@broadcom.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
diff --git a/drivers/scsi/bnx2fc/bnx2fc_hwi.c b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
index 72cfb14..b241f3d 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_hwi.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_hwi.c
@@ -1009,6 +1009,7 @@
 	u32 cq_cons;
 	struct fcoe_cqe *cqe;
 	u32 num_free_sqes = 0;
+	u32 num_cqes = 0;
 	u16 wqe;
 
 	/*
@@ -1058,10 +1059,11 @@
 				wake_up_process(fps->iothread);
 			else
 				bnx2fc_process_cq_compl(tgt, wqe);
+			num_free_sqes++;
 		}
 		cqe++;
 		tgt->cq_cons_idx++;
-		num_free_sqes++;
+		num_cqes++;
 
 		if (tgt->cq_cons_idx == BNX2FC_CQ_WQES_MAX) {
 			tgt->cq_cons_idx = 0;
@@ -1070,8 +1072,10 @@
 				1 - tgt->cq_curr_toggle_bit;
 		}
 	}
-	if (num_free_sqes) {
-		bnx2fc_arm_cq(tgt);
+	if (num_cqes) {
+		/* Arm CQ only if doorbell is mapped */
+		if (tgt->ctx_base)
+			bnx2fc_arm_cq(tgt);
 		atomic_add(num_free_sqes, &tgt->free_sqes);
 	}
 	spin_unlock_bh(&tgt->cq_lock);
diff --git a/drivers/scsi/bnx2fc/bnx2fc_tgt.c b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
index 3d28fbe..2f7a7da 100644
--- a/drivers/scsi/bnx2fc/bnx2fc_tgt.c
+++ b/drivers/scsi/bnx2fc/bnx2fc_tgt.c
@@ -133,9 +133,9 @@
 		printk(KERN_ERR PFX "map doorbell failed - no mem\n");
 		/* upload will take care of cleaning up sess resc */
 		lport->tt.rport_logoff(rdata);
-	}
-	/* Arm CQ */
-	bnx2fc_arm_cq(tgt);
+	} else
+		/* Arm CQ */
+		bnx2fc_arm_cq(tgt);
 	return;
 
 ofld_err:
@@ -806,14 +806,14 @@
 static void bnx2fc_free_session_resc(struct bnx2fc_hba *hba,
 						struct bnx2fc_rport *tgt)
 {
+	void __iomem *ctx_base_ptr;
+
 	BNX2FC_TGT_DBG(tgt, "Freeing up session resources\n");
 
-	if (tgt->ctx_base) {
-		iounmap(tgt->ctx_base);
-		tgt->ctx_base = NULL;
-	}
-
 	spin_lock_bh(&tgt->cq_lock);
+	ctx_base_ptr = tgt->ctx_base;
+	tgt->ctx_base = NULL;
+
 	/* Free LCQ */
 	if (tgt->lcq) {
 		dma_free_coherent(&hba->pcidev->dev, tgt->lcq_mem_size,
@@ -867,4 +867,7 @@
 		tgt->sq = NULL;
 	}
 	spin_unlock_bh(&tgt->cq_lock);
+
+	if (ctx_base_ptr)
+		iounmap(ctx_base_ptr);
 }