[SCSI] mpt fusion: lock down ScsiLookup

ScsiLookup is an array of pending scmd pointers that the scsi lld
maintains. This array is touched from queuecommand, eh threads, and
interrupt context. This array should put under locks, hence this patch
to synchronize its access.  I've added some nice little function
wrappers for this, and moved the ScsiLookup array over to MPT_ADAPTER
struct.

Signed-off-by: Eric Moore <Eric.Moore@lsi.com>
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
diff --git a/drivers/message/fusion/mptbase.h b/drivers/message/fusion/mptbase.h
index 9456564..eb65c49 100644
--- a/drivers/message/fusion/mptbase.h
+++ b/drivers/message/fusion/mptbase.h
@@ -704,6 +704,8 @@
 	struct work_struct	 fc_rescan_work;
 	char			 fc_rescan_work_q_name[KOBJ_NAME_LEN];
 	struct workqueue_struct *fc_rescan_work_q;
+	struct scsi_cmnd	**ScsiLookup;
+	spinlock_t		  scsi_lookup_lock;
 } MPT_ADAPTER;
 
 /*
@@ -817,7 +819,6 @@
 	MPT_ADAPTER		 *ioc;
 	int			  port;
 	u32			  pad0;
-	struct scsi_cmnd	**ScsiLookup;
 	MPT_LOCAL_REPLY		 *pLocal;		/* used for internal commands */
 	struct timer_list	  timer;
 		/* Pool of memory for holding SCpnts before doing
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index df1e6fa..3cdd4e9 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -1282,14 +1282,15 @@
 	/* SCSI needs scsi_cmnd lookup table!
 	 * (with size equal to req_depth*PtrSz!)
 	 */
-	hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
-	if (!hd->ScsiLookup) {
+	ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
+	if (!ioc->ScsiLookup) {
 		error = -ENOMEM;
 		goto out_mptfc_probe;
 	}
+	spin_lock_init(&ioc->scsi_lookup_lock);
 
 	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
-		 ioc->name, hd->ScsiLookup));
+		 ioc->name, ioc->ScsiLookup));
 
 	/* Clear the TM flags
 	 */
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index 4c641c6..ba4f5e7 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -3221,15 +3221,16 @@
 	/* SCSI needs scsi_cmnd lookup table!
 	 * (with size equal to req_depth*PtrSz!)
 	 */
-	hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
-	if (!hd->ScsiLookup) {
+	ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
+	if (!ioc->ScsiLookup) {
 		error = -ENOMEM;
 		spin_unlock_irqrestore(&ioc->FreeQlock, flags);
 		goto out_mptsas_probe;
 	}
+	spin_lock_init(&ioc->scsi_lookup_lock);
 
 	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
-		 ioc->name, hd->ScsiLookup));
+		 ioc->name, ioc->ScsiLookup));
 
 	/* Clear the TM flags
 	 */
diff --git a/drivers/message/fusion/mptscsih.c b/drivers/message/fusion/mptscsih.c
index b1f68ca..dc5e996 100644
--- a/drivers/message/fusion/mptscsih.c
+++ b/drivers/message/fusion/mptscsih.c
@@ -80,6 +80,10 @@
 /*
  *  Other private/forward protos...
  */
+static struct scsi_cmnd * mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
+static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i);
+static void	mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd);
+static int	SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd);
 int		mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
 static void	mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
 int		mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
@@ -90,7 +94,6 @@
 static void	mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
 static int	mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
 static int	mptscsih_tm_wait_for_completion(MPT_SCSI_HOST * hd, ulong timeout );
-static int	SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc);
 
 static int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, int lun, int ctx2abort, ulong timeout);
 
@@ -658,12 +661,11 @@
 		printk (MYIOC_s_ERR_FMT
 		    "req_idx=%x req_idx_MR=%x mf=%p mr=%p sc=%p\n",
 		    ioc->name, req_idx, req_idx_MR, mf, mr,
-		    hd->ScsiLookup[req_idx_MR]);
+		    mptscsih_get_scsi_lookup(ioc, req_idx_MR));
 		return 0;
 	}
 
-	sc = hd->ScsiLookup[req_idx];
-	hd->ScsiLookup[req_idx] = NULL;
+	sc = mptscsih_getclear_scsi_lookup(ioc, req_idx);
 	if (sc == NULL) {
 		MPIHeader_t *hdr = (MPIHeader_t *)mf;
 
@@ -969,48 +971,32 @@
 mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
 {
 	MPT_ADAPTER *ioc = hd->ioc;
-	struct scsi_cmnd	*SCpnt;
-	MPT_FRAME_HDR	*mf;
+	struct scsi_cmnd *sc;
+	SCSIIORequest_t	*mf = NULL;
 	int		 ii;
-	int		 max = ioc->req_depth;
+	int		 channel, id;
 
-	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": flush_ScsiLookup called\n", ioc->name));
-	for (ii= 0; ii < max; ii++) {
-		if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
-
-			/* Command found.
-			 */
-
-			/* Null ScsiLookup index
-			 */
-			hd->ScsiLookup[ii] = NULL;
-
-			mf = MPT_INDEX_2_MFPTR(ioc, ii);
-			dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": flush: ScsiDone (mf=%p,sc=%p)\n",
-			    ioc->name, mf, SCpnt));
-
-			/* Free Chain buffers */
-			mptscsih_freeChainBuffers(ioc, ii);
-
-			/* Free Message frames */
-			mpt_free_msg_frame(ioc, mf);
-
-			if ((unsigned char *)mf != SCpnt->host_scribble)
-				continue;
-
-			/* Set status, free OS resources (SG DMA buffers)
-			 * Do OS callback
-			 */
-			scsi_dma_unmap(SCpnt);
-
-			SCpnt->result = DID_RESET << 16;
-			SCpnt->host_scribble = NULL;
-
-			SCpnt->scsi_done(SCpnt);	/* Issue the command callback */
-		}
+	for (ii= 0; ii < ioc->req_depth; ii++) {
+		sc = mptscsih_getclear_scsi_lookup(ioc, ii);
+		if (!sc)
+			continue;
+		mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(ioc, ii);
+		if (!mf)
+			continue;
+		channel = mf->Bus;
+		id = mf->TargetID;
+		mptscsih_freeChainBuffers(ioc, ii);
+		mpt_free_msg_frame(ioc, (MPT_FRAME_HDR *)mf);
+		if ((unsigned char *)mf != sc->host_scribble)
+			continue;
+		scsi_dma_unmap(sc);
+		sc->result = DID_RESET << 16;
+		sc->host_scribble = NULL;
+		sdev_printk(MYIOC_s_INFO_FMT, sc->device,
+		    "completing cmds: fw_channel %d, fw_id %d, sc=%p,"
+		    " mf = %p, idx=%x\n", ioc->name, channel, id, sc, mf, ii);
+		sc->scsi_done(sc);
 	}
-
-	return;
 }
 
 /*
@@ -1032,16 +1018,14 @@
 {
 	SCSIIORequest_t	*mf = NULL;
 	int		 ii;
-	int		 max = hd->ioc->req_depth;
 	struct scsi_cmnd *sc;
 	struct scsi_lun  lun;
 	MPT_ADAPTER *ioc = hd->ioc;
+	unsigned long	flags;
 
-	dsprintk(ioc, printk(MYIOC_s_DEBUG_FMT ": search_running channel %d id %d lun %d max %d\n",
-	    ioc->name, vdevice->vtarget->channel, vdevice->vtarget->id, vdevice->lun, max));
-
-	for (ii=0; ii < max; ii++) {
-		if ((sc = hd->ScsiLookup[ii]) != NULL) {
+	spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+	for (ii = 0; ii < ioc->req_depth; ii++) {
+		if ((sc = ioc->ScsiLookup[ii]) != NULL) {
 
 			mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(ioc, ii);
 			if (mf == NULL)
@@ -1060,13 +1044,12 @@
 			    memcmp(lun.scsi_lun, mf->LUN, 8))
 				continue;
 
-			/* Cleanup
-			 */
-			hd->ScsiLookup[ii] = NULL;
-			mptscsih_freeChainBuffers(ioc, ii);
-			mpt_free_msg_frame(ioc, (MPT_FRAME_HDR *)mf);
 			if ((unsigned char *)mf != sc->host_scribble)
 				continue;
+			ioc->ScsiLookup[ii] = NULL;
+			spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+			mptscsih_freeChainBuffers(ioc, ii);
+			mpt_free_msg_frame(ioc, (MPT_FRAME_HDR *)mf);
 			scsi_dma_unmap(sc);
 			sc->host_scribble = NULL;
 			sc->result = DID_NO_CONNECT << 16;
@@ -1074,8 +1057,10 @@
 			   "fw_id %d, sc=%p, mf = %p, idx=%x\n", ioc->name, vdevice->vtarget->channel,
 			   vdevice->vtarget->id, sc, mf, ii);
 			sc->scsi_done(sc);
+			spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
 		}
 	}
+	spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
 	return;
 }
 
@@ -1143,10 +1128,10 @@
 
 	sz1=0;
 
-	if (hd->ScsiLookup != NULL) {
+	if (ioc->ScsiLookup != NULL) {
 		sz1 = ioc->req_depth * sizeof(void *);
-		kfree(hd->ScsiLookup);
-		hd->ScsiLookup = NULL;
+		kfree(ioc->ScsiLookup);
+		ioc->ScsiLookup = NULL;
 	}
 
 	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT
@@ -1463,7 +1448,7 @@
 	}
 
 	SCpnt->host_scribble = (unsigned char *)mf;
-	hd->ScsiLookup[my_idx] = SCpnt;
+	mptscsih_set_scsi_lookup(ioc, my_idx, SCpnt);
 
 	mpt_put_msg_frame(ioc->DoneCtx, ioc, mf);
 	dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
@@ -1472,7 +1457,6 @@
 	return 0;
 
  fail:
-	hd->ScsiLookup[my_idx] = NULL;
 	mptscsih_freeChainBuffers(ioc, my_idx);
 	mpt_free_msg_frame(ioc, mf);
 	return SCSI_MLQUEUE_HOST_BUSY;
@@ -1834,7 +1818,7 @@
 
 	/* Find this command
 	 */
-	if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
+	if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(ioc, SCpnt)) < 0) {
 		/* Cmd not found in ScsiLookup.
 		 * Do OS callback.
 		 */
@@ -1870,7 +1854,7 @@
 	    vdevice->vtarget->channel, vdevice->vtarget->id, vdevice->lun,
 	    ctx2abort, mptscsih_get_tm_timeout(ioc));
 
-	if (SCPNT_TO_LOOKUP_IDX(SCpnt) == scpnt_idx &&
+	if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx &&
 	    SCpnt->serial_number == sn)
 		retval = FAILED;
 
@@ -2551,21 +2535,101 @@
 	}
 }
 
-static int
-SCPNT_TO_LOOKUP_IDX(struct scsi_cmnd *sc)
+/**
+ * mptscsih_get_scsi_lookup
+ *
+ * retrieves scmd entry from ScsiLookup[] array list
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @i: index into the array
+ *
+ * Returns the scsi_cmd pointer
+ *
+ **/
+static struct scsi_cmnd *
+mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i)
 {
-	MPT_SCSI_HOST *hd;
-	int i;
+	unsigned long	flags;
+	struct scsi_cmnd *scmd;
 
-	hd = shost_priv(sc->device->host);
+	spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+	scmd = ioc->ScsiLookup[i];
+	spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
 
-	for (i = 0; i < hd->ioc->req_depth; i++) {
-		if (hd->ScsiLookup[i] == sc) {
-			return i;
+	return scmd;
+}
+
+/**
+ * mptscsih_getclear_scsi_lookup
+ *
+ * retrieves and clears scmd entry from ScsiLookup[] array list
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @i: index into the array
+ *
+ * Returns the scsi_cmd pointer
+ *
+ **/
+static struct scsi_cmnd *
+mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i)
+{
+	unsigned long	flags;
+	struct scsi_cmnd *scmd;
+
+	spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+	scmd = ioc->ScsiLookup[i];
+	ioc->ScsiLookup[i] = NULL;
+	spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+
+	return scmd;
+}
+
+/**
+ * mptscsih_set_scsi_lookup
+ *
+ * writes a scmd entry into the ScsiLookup[] array list
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @i: index into the array
+ * @scmd: scsi_cmnd pointer
+ *
+ **/
+static void
+mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd)
+{
+	unsigned long	flags;
+
+	spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+	ioc->ScsiLookup[i] = scmd;
+	spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+}
+
+/**
+ * SCPNT_TO_LOOKUP_IDX
+ *
+ * search's for a given scmd in the ScsiLookup[] array list
+ *
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @scmd: scsi_cmnd pointer
+ *
+ **/
+static int
+SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *sc)
+{
+	unsigned long	flags;
+	int i, index=-1;
+
+	spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
+	for (i = 0; i < ioc->req_depth; i++) {
+		if (ioc->ScsiLookup[i] == sc) {
+			index = i;
+			goto out;
 		}
 	}
 
-	return -1;
+ out:
+	spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
+	return index;
 }
 
 /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
@@ -2574,7 +2638,6 @@
 {
 	MPT_SCSI_HOST	*hd;
 	unsigned long	 flags;
-	int 		ii;
 
 	dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT
 	    ": IOC %s_reset routed to SCSI host driver!\n",
@@ -2630,11 +2693,6 @@
 		 * Init all control structures.
 		 */
 
-		/* ScsiLookup initialization
-		 */
-		for (ii=0; ii < ioc->req_depth; ii++)
-			hd->ScsiLookup[ii] = NULL;
-
 		/* 2. Chain Buffer initialization
 		 */
 
@@ -2772,7 +2830,7 @@
 
 	del_timer(&hd->timer);
 	req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
-	hd->ScsiLookup[req_idx] = NULL;
+	mptscsih_set_scsi_lookup(ioc, req_idx, NULL);
 	pReq = (SCSIIORequest_t *) mf;
 
 	if (mf != hd->cmdPtr) {
diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c
index ed6a778..42fdf7d 100644
--- a/drivers/message/fusion/mptspi.c
+++ b/drivers/message/fusion/mptspi.c
@@ -1446,14 +1446,15 @@
 	/* SCSI needs scsi_cmnd lookup table!
 	 * (with size equal to req_depth*PtrSz!)
 	 */
-	hd->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
-	if (!hd->ScsiLookup) {
+	ioc->ScsiLookup = kcalloc(ioc->req_depth, sizeof(void *), GFP_ATOMIC);
+	if (!ioc->ScsiLookup) {
 		error = -ENOMEM;
 		goto out_mptspi_probe;
 	}
+	spin_lock_init(&ioc->scsi_lookup_lock);
 
 	dprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiLookup @ %p\n",
-		 ioc->name, hd->ScsiLookup));
+		 ioc->name, ioc->ScsiLookup));
 
 	/* Clear the TM flags
 	 */