* (mailto:sjralston1@netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptbase.c,v 1.123 2002/10/17 20:15:56 pdelaney Exp $
+ * $Id: mptbase.c,v 1.125 2002/12/03 21:26:32 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
static int mpt_GetScsiPortSettings(MPT_ADAPTER *ioc, int portnum);
static int mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum);
static int mpt_findImVolumes(MPT_ADAPTER *ioc);
+static void mpt_read_ioc_pg_1(MPT_ADAPTER *ioc);
static void mpt_timer_expired(unsigned long data);
static int SendEventNotification(MPT_ADAPTER *ioc, u8 EvSwitch);
static int SendEventAck(MPT_ADAPTER *ioc, EventNotificationReply_t *evnp);
*/
if ((mf) && ((mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))
|| (mf < ioc->req_frames)) ) {
- printk(MYIOC_s_WARN_FMT
+ printk(MYIOC_s_WARN_FMT
"mpt_interrupt: Invalid mf (%p) req_idx (%d)!\n", ioc->name, (void *)mf, req_idx);
cb_idx = 0;
pa = 0;
}
if ((pa) && (mr) && ((mr >= MPT_INDEX_2_RFPTR(ioc, ioc->req_depth))
|| (mr < ioc->reply_frames)) ) {
- printk(MYIOC_s_WARN_FMT
+ printk(MYIOC_s_WARN_FMT
"mpt_interrupt: Invalid rf (%p)!\n", ioc->name, (void *)mr);
cb_idx = 0;
pa = 0;
freeme = 0;
}
if (cb_idx > (MPT_MAX_PROTOCOL_DRIVERS-1)) {
- printk(MYIOC_s_WARN_FMT
+ printk(MYIOC_s_WARN_FMT
"mpt_interrupt: Invalid cb_idx (%d)!\n", ioc->name, cb_idx);
cb_idx = 0;
pa = 0;
CONFIGPARMS *pCfg;
unsigned long flags;
- dprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
+ dcprintk((MYIOC_s_INFO_FMT "config_complete (mf=%p,mr=%p)\n",
ioc->name, mf, reply));
+ DBG_DUMP_REPLY_FRAME(reply)
+
pCfg = * ((CONFIGPARMS **)((u8 *) mf + ioc->req_sz - sizeof(void *)));
if (pCfg) {
u16 status;
status = le16_to_cpu(pReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- dprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n",
+ dcprintk((KERN_NOTICE " IOCStatus=%04xh, IOCLogInfo=%08xh\n",
status, le32_to_cpu(pReply->IOCLogInfo)));
pCfg->status = status;
* mpt_add_sge - Place a simple SGE at address pAddr.
* @pAddr: virtual address for SGE
* @flagslength: SGE flags and data transfer length
- * @dma_addr: Physical address
+ * @dma_addr: Physical address
*
* This routine places a MPT request frame back on the MPT adapter's
* FreeQ.
* @pAddr: virtual address for SGE
* @next: nextChainOffset value (u32's)
* @length: length of next SGL segment
- * @dma_addr: Physical address
+ * @dma_addr: Physical address
*
* This routine places a MPT request frame back on the MPT adapter's
* FreeQ.
u32 tmp = dma_addr & 0xFFFFFFFF;
pChain->Length = cpu_to_le16(length);
- pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
pChain->NextChainOffset = next;
return r;
if (!pci_set_dma_mask(pdev, mask)) {
- dprintk((KERN_INFO MYNAM
+ dprintk((KERN_INFO MYNAM
": 64 BIT PCI BUS DMA ADDRESSING SUPPORTED\n"));
} else if (pci_set_dma_mask(pdev, (u64) 0xffffffff)) {
printk(KERN_WARNING MYNAM ": 32 BIT PCI BUS DMA ADDRESSING NOT SUPPORTED\n");
ioc->active = 0;
CHIPREG_WRITE32(&ioc->chip->IntStatus, 0);
+ /* tack onto tail of our MPT adapter list */
+ Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER);
+
+ /* Set lookup ptr. */
+ mpt_adapters[ioc->id] = ioc;
+
ioc->pci_irq = -1;
if (pdev->irq) {
r = request_irq(pdev->irq, mpt_interrupt, SA_SHIRQ, ioc->name, ioc);
printk(MYIOC_s_ERR_FMT "Unable to allocate interrupt %s!\n",
ioc->name, __irq_itoa(pdev->irq));
#endif
+ Q_DEL_ITEM(ioc);
+ mpt_adapters[ioc->id] = NULL;
iounmap(mem);
kfree(ioc);
return -EBUSY;
#endif
}
- /* tack onto tail of our MPT adapter list */
- Q_ADD_TAIL(&MptAdapters, ioc, MPT_ADAPTER);
-
- /* Set lookup ptr. */
- mpt_adapters[ioc->id] = ioc;
-
/* NEW! 20010220 -sralston
* Check for "bound ports" (929, 929X, 1030, 1035) to reduce redundant resets.
*/
- if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030)
+ if ((ioc->chip_type == FC929) || (ioc->chip_type == C1030)
|| (ioc->chip_type == C1035) || (ioc->chip_type == FC929X))
mpt_detect_bound_ports(ioc, pdev);
printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
/* Handle the alt IOC too */
if ((alt_ioc_ready) && (ioc->alt_ioc->upload_fw)){
+ ddlprintk((MYIOC_s_INFO_FMT
+ "Alt-ioc firmware upload required!\n",
+ ioc->name));
r = mpt_do_upload(ioc->alt_ioc, sleepFlag);
if (r != 0)
printk(KERN_WARNING MYNAM ": firmware upload failure!\n");
*/
mpt_GetScsiPortSettings(ioc, 0);
- /* Get version and length of SDP 1
+ /* Get version and length of SDP 1
*/
mpt_readScsiDevicePageHeaders(ioc, 0);
- /* Find IM volumes
+ /* Find IM volumes
*/
if (ioc->facts.MsgVersion >= 0x0102)
mpt_findImVolumes(ioc);
+
+ /* Check, and possibly reset, the coalescing value
+ */
+ mpt_read_ioc_pg_1(ioc);
}
GetIoUnitPage2(ioc);
ddlprintk((KERN_INFO MYNAM ": Pushing FW onto adapter\n"));
if ((state = mpt_downloadboot(this, NO_SLEEP)) < 0) {
- printk(KERN_WARNING MYNAM
+ printk(KERN_WARNING MYNAM
": firmware downloadboot failure (%d)!\n", state);
}
}
sz_first = this->alloc_total;
+ if (this->alt_ioc != NULL) {
+ this->alt_ioc->alt_ioc = NULL;
+ this->alt_ioc = NULL;
+ }
+
mpt_adapter_disable(this, 1);
if (this->pci_irq != -1) {
*
* Returns:
* 1 - DIAG reset and READY
- * 0 - READY initially OR soft reset and READY
- * -1 - Any failure on KickStart
+ * 0 - READY initially OR soft reset and READY
+ * -1 - Any failure on KickStart
* -2 - Msg Unit Reset Failed
* -3 - IO Unit Reset Failed
* -4 - IOC owned by a PEER
else
statefault = 4;
}
- }
+ }
/*
* Check to see if IOC is in FAULT state.
facts->RequestFrameSize = le16_to_cpu(facts->RequestFrameSize);
/*
- * FC f/w version changed between 1.1 and 1.2
+ * FC f/w version changed between 1.1 and 1.2
* Old: u16{Major(4),Minor(4),SubMinor(8)}
* New: u32{Major(8),Minor(8),Unit(8),Dev(8)}
*/
if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw))
ioc_init.Flags = MPI_IOCINIT_FLAGS_DISCARD_FW_IMAGE;
- else
+ else
ioc->upload_fw = 1;
}
- ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n",
+ ddlprintk((MYIOC_s_INFO_FMT "flags %d, upload_fw %d \n",
ioc->name, ioc_init.Flags, ioc->upload_fw));
if ((int)ioc->chip_type <= (int)FC929) {
* Outputs: frags - number of fragments needed
* Return NULL if failed.
*/
-void *
-mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz)
+void *
+mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz)
{
fw_image_t **cached_fw = NULL;
u8 *mem = NULL;
int bytes_left, bytes, num_frags;
int sz, ii;
- /* cached_fw
+ /* cached_fw
*/
sz = ioc->num_fw_frags * sizeof(void *);
mem = kmalloc(sz, GFP_ATOMIC);
ioc->num_fw_frags = ioc->req_sz - sizeof(FWUpload_t) + sizeof(dma_addr_t) + sizeof(u32) -1;
ioc->num_fw_frags /= sizeof(dma_addr_t) + sizeof(u32);
- ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc,
- ioc->facts.FWImageSize, &num_frags, &alloc_sz);
+ ioc->cached_fw = (fw_image_t **) mpt_alloc_fw_memory(ioc,
+ ioc->facts.FWImageSize, &num_frags, &alloc_sz);
if (ioc->cached_fw == NULL) {
/* Major Failure.
sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
}
- mpt_add_sge(&request[sgeoffset],
- MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size,
+ mpt_add_sge(&request[sgeoffset],
+ MPT_SGE_FLAGS_SSIMPLE_READ |(u32) ioc->cached_fw[ii]->size,
ioc->cached_fw[ii]->fw_dma);
sgeoffset += sizeof(u32) + sizeof(dma_addr_t);
* 0 - no reset due to History bit, READY
* -1 - no reset due to History bit but not READY
* OR reset but failed to come READY
- * -2 - no reset, could not enter DIAG mode
- * -3 - reset but bad FW bit
+ * -2 - no reset, could not enter DIAG mode
+ * -3 - reset but bad FW bit
*/
static int
KickStart(MPT_ADAPTER *ioc, int force, int sleepFlag)
ioc->name, diag0val, diag1val));
#endif
/* Write the PreventIocBoot bit */
-#if 1
if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) {
-#else
- if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
-#endif
diag0val |= MPI_DIAG_PREVENT_IOC_BOOT;
CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val);
}
/*
* Disable the ARM (Bug fix)
- *
+ *
*/
CHIPREG_WRITE32(&ioc->chip->Diagnostic, diag0val | MPI_DIAG_DISABLE_ARM);
mdelay (1);
/* FIXME? Examine results here? */
}
-#if 1
if ((ioc->cached_fw) || (ioc->alt_ioc && ioc->alt_ioc->cached_fw)) {
-#else
- if (ioc->facts.Flags & MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT) {
-#endif
/* If the DownloadBoot operation fails, the
* IOC will be left unusable. This is a fatal error
* case. _diag_reset will return < 0
#ifdef MPT_DEBUG
if (ioc->alt_ioc)
diag1val = CHIPREG_READ32(&ioc->alt_ioc->chip->Diagnostic);
- dprintk((MYIOC_s_INFO_FMT
+ dprintk((MYIOC_s_INFO_FMT
"DbG2b: diag0=%08x, diag1=%08x\n",
ioc->name, diag0val, diag1val));
#endif
}
}
if ((count = mpt_downloadboot(ioc, sleepFlag)) < 0) {
- printk(KERN_WARNING MYNAM
+ printk(KERN_WARNING MYNAM
": firmware downloadboot failure (%d)!\n", count);
}
if ((r = WaitForDoorbellAck(ioc, 2, sleepFlag)) < 0)
return r;
- /* FW ACK'd request, wait for READY state
+ /* FW ACK'd request, wait for READY state
*/
cntdn = HZ * 15;
count = 0;
}
spin_unlock_irqrestore(&ioc->FreeQlock, flags);
+#ifdef MFCNT
+ ioc->mfcnt = 0;
+#endif
if (ioc->sense_buf_pool == NULL) {
sz = (ioc->req_depth * MPT_SENSE_BUFFER_ALLOC);
int ii;
int data, rc = 0;
- /* Allocate memory
+ /* Allocate memory
*/
if (!ioc->spi_data.nvram) {
int sz;
ioc->spi_data.sdp0version = cfg.hdr->PageVersion;
ioc->spi_data.sdp0length = cfg.hdr->PageLength;
+ dcprintk((MYIOC_s_INFO_FMT "Headers: 0: version %d length %d\n",
+ ioc->name, ioc->spi_data.sdp0version, ioc->spi_data.sdp0length));
+
+ dcprintk((MYIOC_s_INFO_FMT "Headers: 1: version %d length %d\n",
+ ioc->name, ioc->spi_data.sdp1version, ioc->spi_data.sdp1length));
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
+ * mpt_findImVolumes - Identify IDs of hidden disks and RAID Volumes
* @ioc: Pointer to a Adapter Strucutre
* @portnum: IOC port number
*
mpt_findImVolumes(MPT_ADAPTER *ioc)
{
IOCPage2_t *pIoc2 = NULL;
- IOCPage3_t *pIoc3 = NULL;
ConfigPageIoc2RaidVol_t *pIocRv = NULL;
- u8 *mem;
dma_addr_t ioc2_dma;
- dma_addr_t ioc3_dma;
CONFIGPARMS cfg;
ConfigPageHeader_t header;
int jj;
int rc = 0;
int iocpage2sz;
- int iocpage3sz = 0;
u8 nVols, nPhys;
u8 vid, vbus, vioc;
/* No physical disks. Done.
*/
} else {
- /* There is at least one physical disk.
- * Read and save IOC Page 3
- */
- header.PageVersion = 0;
- header.PageLength = 0;
- header.PageNumber = 3;
- header.PageType = MPI_CONFIG_PAGETYPE_IOC;
- cfg.hdr = &header;
- cfg.physAddr = -1;
- cfg.pageAddr = 0;
- cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
- cfg.dir = 0;
- cfg.timeout = 0;
- if (mpt_config(ioc, &cfg) != 0)
- goto done_and_free;
-
- if (header.PageLength == 0)
- goto done_and_free;
-
- /* Read Header good, alloc memory
- */
- iocpage3sz = header.PageLength * 4;
- pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma);
- if (!pIoc3)
- goto done_and_free;
-
- /* Read the Page and save the data
- * into malloc'd memory.
- */
- cfg.physAddr = ioc3_dma;
- cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
- if (mpt_config(ioc, &cfg) == 0) {
- mem = kmalloc(iocpage3sz, GFP_ATOMIC);
- if (mem) {
- memcpy(mem, (u8 *)pIoc3, iocpage3sz);
- ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem;
- }
- }
+ mpt_read_ioc_pg_3(ioc);
}
done_and_free:
pIoc2 = NULL;
}
+ return rc;
+}
+
+int
+mpt_read_ioc_pg_3(MPT_ADAPTER *ioc)
+{
+ IOCPage3_t *pIoc3 = NULL;
+ u8 *mem;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t header;
+ dma_addr_t ioc3_dma;
+ int iocpage3sz = 0;
+
+ /* Free the old page
+ */
+ if (ioc->spi_data.pIocPg3) {
+ kfree(ioc->spi_data.pIocPg3);
+ ioc->spi_data.pIocPg3 = NULL;
+ }
+
+ /* There is at least one physical disk.
+ * Read and save IOC Page 3
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 3;
+ header.PageType = MPI_CONFIG_PAGETYPE_IOC;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ return 0;
+
+ if (header.PageLength == 0)
+ return 0;
+
+ /* Read Header good, alloc memory
+ */
+ iocpage3sz = header.PageLength * 4;
+ pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma);
+ if (!pIoc3)
+ return 0;
+
+ /* Read the Page and save the data
+ * into malloc'd memory.
+ */
+ cfg.physAddr = ioc3_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ if (mpt_config(ioc, &cfg) == 0) {
+ mem = kmalloc(iocpage3sz, GFP_ATOMIC);
+ if (mem) {
+ memcpy(mem, (u8 *)pIoc3, iocpage3sz);
+ ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem;
+ }
+ }
+
if (pIoc3) {
pci_free_consistent(ioc->pcidev, iocpage3sz, pIoc3, ioc3_dma);
pIoc3 = NULL;
}
- return rc;
+ return 0;
}
+static void
+mpt_read_ioc_pg_1(MPT_ADAPTER *ioc)
+{
+ IOCPage1_t *pIoc1 = NULL;
+ CONFIGPARMS cfg;
+ ConfigPageHeader_t header;
+ dma_addr_t ioc1_dma;
+ int iocpage1sz = 0;
+ u32 tmp;
+
+ /* Check the Coalescing Timeout in IOC Page 1
+ */
+ header.PageVersion = 0;
+ header.PageLength = 0;
+ header.PageNumber = 1;
+ header.PageType = MPI_CONFIG_PAGETYPE_IOC;
+ cfg.hdr = &header;
+ cfg.physAddr = -1;
+ cfg.pageAddr = 0;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_HEADER;
+ cfg.dir = 0;
+ cfg.timeout = 0;
+ if (mpt_config(ioc, &cfg) != 0)
+ return;
+
+ if (header.PageLength == 0)
+ return;
+
+ /* Read Header good, alloc memory
+ */
+ iocpage1sz = header.PageLength * 4;
+ pIoc1 = pci_alloc_consistent(ioc->pcidev, iocpage1sz, &ioc1_dma);
+ if (!pIoc1)
+ return;
+
+ /* Read the Page and check coalescing timeout
+ */
+ cfg.physAddr = ioc1_dma;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ if (mpt_config(ioc, &cfg) == 0) {
+
+ tmp = le32_to_cpu(pIoc1->Flags) & MPI_IOCPAGE1_REPLY_COALESCING;
+ if (tmp == MPI_IOCPAGE1_REPLY_COALESCING) {
+ tmp = le32_to_cpu(pIoc1->CoalescingTimeout);
+
+ dprintk((MYIOC_s_INFO_FMT "Coalescing Enabled Timeout = %d\n",
+ ioc->name, tmp));
+
+ if (tmp > MPT_COALESCING_TIMEOUT) {
+ pIoc1->CoalescingTimeout = cpu_to_le32(MPT_COALESCING_TIMEOUT);
+
+ /* Write NVRAM and current
+ */
+ cfg.dir = 1;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_CURRENT;
+ if (mpt_config(ioc, &cfg) == 0) {
+ dprintk((MYIOC_s_INFO_FMT "Reset Current Coalescing Timeout to = %d\n",
+ ioc->name, MPT_COALESCING_TIMEOUT));
+
+ cfg.action = MPI_CONFIG_ACTION_PAGE_WRITE_NVRAM;
+ if (mpt_config(ioc, &cfg) == 0) {
+ dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout to = %d\n",
+ ioc->name, MPT_COALESCING_TIMEOUT));
+ } else {
+ dprintk((MYIOC_s_INFO_FMT "Reset NVRAM Coalescing Timeout Failed\n",
+ ioc->name));
+ }
+
+ } else {
+ dprintk((MYIOC_s_WARN_FMT "Reset of Current Coalescing Timeout Failed!\n",
+ ioc->name));
+ }
+ }
+
+ } else {
+ dprintk((MYIOC_s_WARN_FMT "Coalescing Disabled\n", ioc->name));
+ }
+ }
+
+ if (pIoc1) {
+ pci_free_consistent(ioc->pcidev, iocpage1sz, pIoc1, ioc1_dma);
+ pIoc1 = NULL;
+ }
+
+ return;
+}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
*/
in_isr = in_interrupt();
if (in_isr) {
- dprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
+ dcprintk((MYIOC_s_WARN_FMT "Config request not allowed in ISR context!\n",
ioc->name));
return -EPERM;
}
/* Get and Populate a free Frame
*/
if ((mf = mpt_get_msg_frame(mpt_base_index, ioc->id)) == NULL) {
- dprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
+ dcprintk((MYIOC_s_WARN_FMT "mpt_config: no msg frames!\n",
ioc->name));
return -EAGAIN;
}
mpt_add_sge((char *)&pReq->PageBufferSGE, flagsLength, pCfg->physAddr);
- dprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n",
+ dcprintk((MYIOC_s_INFO_FMT "Sending Config request type %d, page %d and action %d\n",
ioc->name, pReq->Header.PageType, pReq->Header.PageNumber, pReq->Action));
/* Append pCfg pointer to end of mf
{
MPT_ADAPTER *ioc = (MPT_ADAPTER *) data;
- dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name));
+ dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired! \n", ioc->name));
/* Perform a FW reload */
if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0)
* Hard reset clean-up will wake up
* process and free all resources.
*/
- dprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name));
+ dcprintk((MYIOC_s_WARN_FMT "mpt_timer_expired complete!\n", ioc->name));
return;
}
} else {
CONFIGPARMS *pNext;
- /* Search the configQ for internal commands.
+ /* Search the configQ for internal commands.
* Flush the Q, and wake up all suspended threads.
*/
#if 1
*/
if (isense_idx == ii)
len += sprintf(buf+len, " Fusion MPT isense driver\n");
- } else
- break;
+ }
}
MPT_PROC_READ_RETURN(buf,start,offset,request,eof,len);
EXPORT_SYMBOL(mpt_stm_index);
EXPORT_SYMBOL(mpt_HardResetHandler);
EXPORT_SYMBOL(mpt_config);
+EXPORT_SYMBOL(mpt_read_ioc_pg_3);
EXPORT_SYMBOL(mpt_alloc_fw_memory);
EXPORT_SYMBOL(mpt_free_fw_memory);
fusion_exit(void)
{
MPT_ADAPTER *this;
+ struct pci_dev *pdev = NULL;
dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n"));
this->active = 0;
+ pdev = (struct pci_dev *)this->pcidev;
+ mptscsih_sync_irq(pdev->irq);
+
/* Clear any lingering interrupt */
CHIPREG_WRITE32(&this->chip->IntStatus, 0);
+ CHIPREG_READ32(&this->chip->IntStatus);
+
Q_DEL_ITEM(this);
mpt_adapter_dispose(this);
}
* (mailto:sjralston1@netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
- * $Id: mptscsih.c,v 1.103 2002/10/17 20:15:59 pdelaney Exp $
+ * $Id: mptscsih.c,v 1.104 2002/12/03 21:26:34 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
static int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static void mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
static int mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
-static int mptscsih_io_direction(Scsi_Cmnd *cmd);
static int mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt,
SCSIIORequest_t *pReq, int req_idx);
-static int mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex);
static void mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx);
static int mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init);
static void copy_sense_data(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * mptscsih_io_done - Main SCSI IO callback routine registered to
- * Fusion MPT (base) driver
- * @ioc: Pointer to MPT_ADAPTER structure
- * @mf: Pointer to original MPT request frame
- * @r: Pointer to MPT reply frame (NULL if TurboReply)
+ * Private inline routines...
+ */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/* 19991030 -sralston
+ * Return absolute SCSI data direction:
+ * 1 = _DATA_OUT
+ * 0 = _DIR_NONE
+ * -1 = _DATA_IN
*
- * This routine is called from mpt.c::mpt_interrupt() at the completion
- * of any SCSI IO request.
- * This routine is registered with the Fusion MPT (base) driver at driver
- * load/init time via the mpt_register() API call.
+ * Changed: 3-20-2002 pdelaney to use the default data
+ * direction and the defines set up in the
+ * 2.4 kernel series
+ * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1)
+ * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3)
+ * -1 = _DATA_IN changed to SCSI_DATA_READ (2)
+ * If the direction is unknown, fall through to original code.
*
- * Returns 1 indicating alloc'd request frame ptr should be freed.
+ * Mid-layer bug fix(): sg interface generates the wrong data
+ * direction in some cases. Set the direction the hard way for
+ * the most common commands.
*/
-static int
-mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+static inline int
+mptscsih_io_direction(Scsi_Cmnd *cmd)
{
- Scsi_Cmnd *sc;
- MPT_SCSI_HOST *hd;
- SCSIIORequest_t *pScsiReq;
- SCSIIOReply_t *pScsiReply;
-#ifndef MPT_SCSI_USE_NEW_EH
- unsigned long flags;
-#endif
- u16 req_idx;
-
- hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
-
- if ((mf == NULL) ||
- (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
- printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n",
- ioc->name, mf?"BAD":"NULL", (void *) mf);
- /* return 1; CHECKME SteveR. Don't free. */
- return 0;
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ return SCSI_DATA_WRITE;
+ break;
+ case READ_6:
+ case READ_10:
+ return SCSI_DATA_READ;
+ break;
}
- req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- sc = hd->ScsiLookup[req_idx];
- if (sc == NULL) {
- MPIHeader_t *hdr = (MPIHeader_t *)mf;
-
- atomic_dec(&queue_depth);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
+ if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN)
+ return cmd->sc_data_direction;
+#endif
+ switch (cmd->cmnd[0]) {
+ /* _DATA_OUT commands */
+ case WRITE_6: case WRITE_10: case WRITE_12:
+ case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
+ case WRITE_VERIFY: case WRITE_VERIFY_12:
+ case COMPARE: case COPY: case COPY_VERIFY:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
+ case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
+ case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
+ case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case REASSIGN_BLOCKS:
+ case PERSISTENT_RESERVE_OUT:
+ case 0xea:
+ case 0xa3:
+ return SCSI_DATA_WRITE;
- /* writeSDP1 will use the ScsiDoneCtx
- * There is no processing for the reply.
- * Just return to the calling function.
- */
- if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
- printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name);
+ /* No data transfer commands */
+ case SEEK_6: case SEEK_10:
+ case RESERVE: case RELEASE:
+ case TEST_UNIT_READY:
+ case START_STOP:
+ case ALLOW_MEDIUM_REMOVAL:
+ return SCSI_DATA_NONE;
- mptscsih_freeChainBuffers(hd, req_idx);
- return 1;
- }
+ /* Conditional data transfer commands */
+ case FORMAT_UNIT:
+ if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
- dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
- ioc->name, mf, mr, sc, req_idx));
+ case VERIFY:
+ if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
- atomic_dec(&queue_depth);
+ case RESERVE_10:
+ if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */
+ return SCSI_DATA_WRITE;
+ else
+ return SCSI_DATA_NONE;
- sc->result = DID_OK << 16; /* Set default reply as OK */
- pScsiReq = (SCSIIORequest_t *) mf;
- pScsiReply = (SCSIIOReply_t *) mr;
+ /* Must be data _IN! */
+ default:
+ return SCSI_DATA_READ;
+ }
+} /* mptscsih_io_direction() */
- if (pScsiReply == NULL) {
- /* special context reply handling */
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_add_sge - Place a simple SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @flagslength: SGE flags and data transfer length
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+static inline void
+mptscsih_add_sge(char *pAddr, u32 flagslength, dma_addr_t dma_addr)
+{
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ SGESimple64_t *pSge = (SGESimple64_t *) pAddr;
+ u32 tmp = dma_addr & 0xFFFFFFFF;
- /* If regular Inquiry cmd - save inquiry data
- */
- if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) {
- int dlen;
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32) ((u64)dma_addr >> 32);
+ pSge->Address.High = cpu_to_le32(tmp);
- dlen = le32_to_cpu(pScsiReq->DataLength);
- if (dlen >= SCSI_STD_INQUIRY_BYTES) {
- mptscsih_initTarget(hd,
- hd->port,
- sc->target,
- pScsiReq->LUN[1],
- sc->buffer,
- dlen);
- }
- }
-#ifdef MPT_SAVE_AUTOSENSE
- clear_sense_flag(hd, pScsiReq);
-#endif
} else {
- u32 xfer_cnt;
- u16 status;
- u8 scsi_state;
+ SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
+ pSge->FlagsLength = cpu_to_le32(flagslength);
+ pSge->Address = cpu_to_le32(dma_addr);
+ }
+} /* mptscsih_add_sge() */
- status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- scsi_state = pScsiReply->SCSIState;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_add_chain - Place a chain SGE at address pAddr.
+ * @pAddr: virtual address for SGE
+ * @next: nextChainOffset value (u32's)
+ * @length: length of next SGL segment
+ * @dma_addr: Physical address
+ *
+ * This routine places a MPT request frame back on the MPT adapter's
+ * FreeQ.
+ */
+static inline void
+mptscsih_add_chain(char *pAddr, u8 next, u16 length, dma_addr_t dma_addr)
+{
+ if (sizeof(dma_addr_t) == sizeof(u64)) {
+ SGEChain64_t *pChain = (SGEChain64_t *) pAddr;
+ u32 tmp = dma_addr & 0xFFFFFFFF;
- dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n",
- ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1],
- mf, mr, sc));
- dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh"
- ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
- status, scsi_state, pScsiReply->SCSIStatus,
- le32_to_cpu(pScsiReply->IOCLogInfo)));
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
- if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
- copy_sense_data(sc, hd, mf, pScsiReply);
+ pChain->NextChainOffset = next;
- /*
- * Look for + dump FCP ResponseInfo[]!
- */
- if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
- dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n",
- le32_to_cpu(pScsiReply->ResponseInfo)));
- }
+ pChain->Address.Low = cpu_to_le32(tmp);
+ tmp = (u32) ((u64)dma_addr >> 32);
+ pChain->Address.High = cpu_to_le32(tmp);
+ } else {
+ SGEChain32_t *pChain = (SGEChain32_t *) pAddr;
+ pChain->Length = cpu_to_le16(length);
+ pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
+ pChain->NextChainOffset = next;
+ pChain->Address = cpu_to_le32(dma_addr);
+ }
+} /* mptscsih_add_chain() */
- switch(status) {
- case MPI_IOCSTATUS_BUSY: /* 0x0002 */
- /* CHECKME!
- * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
- * But not: DID_BUS_BUSY lest one risk
- * killing interrupt handler:-(
- */
- sc->result = STS_BUSY;
- break;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_getFreeChainBuffes - Function to get a free chain
+ * from the MPT_SCSI_HOST FreeChainQ.
+ * @hd: Pointer to the MPT_SCSI_HOST instance
+ * @req_idx: Index of the SCSI IO request frame. (output)
+ *
+ * return SUCCESS or FAILED
+ */
+static inline int
+mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex)
+{
+ MPT_FRAME_HDR *chainBuf = NULL;
+ unsigned long flags;
+ int rc = FAILED;
+ int chain_idx = MPT_HOST_NO_CHAIN;
- case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
- case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
- sc->result = DID_BAD_TARGET << 16;
- break;
+ spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
+ if (!Q_IS_EMPTY(&hd->FreeChainQ)) {
- case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
- /* Spoof to SCSI Selection Timeout! */
- sc->result = DID_NO_CONNECT << 16;
+ int offset;
- if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
- hd->sel_timeout[pScsiReq->TargetID]++;
- break;
+ chainBuf = hd->FreeChainQ.head;
+ Q_DEL_ITEM(&chainBuf->u.frame.linkage);
+ offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer;
+ chain_idx = offset / hd->ioc->req_sz;
+ rc = SUCCESS;
+ }
+ spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
-#ifndef MPT_SCSI_USE_NEW_EH
- search_taskQ_for_cmd(sc, hd);
-#endif
- /* Linux handles an unsolicited DID_RESET better
- * than an unsolicited DID_ABORT.
- */
- sc->result = DID_RESET << 16;
- /* GEM Workaround. */
- if (hd->is_spi)
- mptscsih_no_negotiate(hd, sc->target);
- break;
+ *retIndex = chain_idx;
- case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
- case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
-#ifndef MPT_SCSI_USE_NEW_EH
- search_taskQ_for_cmd(sc, hd);
-#endif
- sc->result = DID_RESET << 16;
+ dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n",
+ hd->ioc->name, *retIndex, chainBuf));
- /* GEM Workaround. */
- if (hd->is_spi)
- mptscsih_no_negotiate(hd, sc->target);
- break;
+ return rc;
+} /* mptscsih_getFreeChainBuffer() */
- case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
- case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
- /*
- * YIKES! I just discovered that SCSI IO which
- * returns check condition, SenseKey=05 (ILLEGAL REQUEST)
- * and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific),
- * comes down this path!
- * Do upfront check for valid SenseData and give it
- * precedence!
- */
- sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
-#ifdef MPT_SAVE_AUTOSENSE
- clear_sense_flag(hd, pScsiReq);
-#endif
- if (scsi_state == 0) {
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
+ * SCSIIORequest_t Message Frame.
+ * @hd: Pointer to MPT_SCSI_HOST structure
+ * @SCpnt: Pointer to Scsi_Cmnd structure
+ * @pReq: Pointer to SCSIIORequest_t structure
+ *
+ * Returns ...
+ */
+static int
+mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt,
+ SCSIIORequest_t *pReq, int req_idx)
+{
+ char *psge;
+ char *chainSge;
+ struct scatterlist *sg;
+ int frm_sz;
+ int sges_left, sg_done;
+ int chain_idx = MPT_HOST_NO_CHAIN;
+ int sgeOffset;
+ int numSgeSlots, numSgeThisFrame;
+ u32 sgflags, sgdir, thisxfer = 0;
+ int chain_dma_off = 0;
+ int newIndex;
+ int ii;
+ dma_addr_t v2;
+
+ sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
+ if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
+ sgdir = MPT_TRANSFER_HOST_TO_IOC;
+ } else {
+ sgdir = MPT_TRANSFER_IOC_TO_HOST;
+ }
+
+ psge = (char *) &pReq->SGL;
+ frm_sz = hd->ioc->req_sz;
+
+ /* Map the data portion, if any.
+ * sges_left = 0 if no data transfer.
+ */
+ sges_left = SCpnt->use_sg;
+ if (SCpnt->use_sg) {
+ sges_left = pci_map_sg(hd->ioc->pcidev,
+ (struct scatterlist *) SCpnt->request_buffer,
+ SCpnt->use_sg,
+ scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ } else if (SCpnt->request_bufflen) {
+ dma_addr_t buf_dma_addr;
+ scPrivate *my_priv;
+
+ buf_dma_addr = pci_map_single(hd->ioc->pcidev,
+ SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+
+ /* We hide it here for later unmap. */
+ my_priv = (scPrivate *) &SCpnt->SCp;
+ my_priv->p1 = (void *)(ulong) buf_dma_addr;
+
+ dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
+ hd->ioc->name, SCpnt, SCpnt->request_bufflen));
+
+ mptscsih_add_sge((char *) &pReq->SGL,
+ 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
+ buf_dma_addr);
+
+ return SUCCESS;
+ }
+
+ /* Handle the SG case.
+ */
+ sg = (struct scatterlist *) SCpnt->request_buffer;
+ sg_done = 0;
+ sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
+ chainSge = NULL;
+
+ /* Prior to entering this loop - the following must be set
+ * current MF: sgeOffset (bytes)
+ * chainSge (Null if original MF is not a chain buffer)
+ * sg_done (num SGE done for this MF)
+ */
+
+nextSGEset:
+ numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
+ numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
+
+ sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
+
+ /* Get first (num - 1) SG elements
+ * Skip any SG entries with a length of 0
+ * NOTE: at finish, sg and psge pointed to NEXT data/location positions
+ */
+ for (ii=0; ii < (numSgeThisFrame-1); ii++) {
+ thisxfer = sg_dma_len(sg);
+ if (thisxfer == 0) {
+ sg ++; /* Get next SG element from the OS */
+ sg_done++;
+ continue;
+ }
+
+ v2 = sg_dma_address(sg);
+ mptscsih_add_sge(psge, sgflags | thisxfer, v2);
+
+ sg++; /* Get next SG element from the OS */
+ psge += (sizeof(u32) + sizeof(dma_addr_t));
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ sg_done++;
+ }
+
+ if (numSgeThisFrame == sges_left) {
+ /* Add last element, end of buffer and end of list flags.
+ */
+ sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
+ MPT_SGE_FLAGS_END_OF_BUFFER |
+ MPT_SGE_FLAGS_END_OF_LIST;
+
+ /* Add last SGE and set termination flags.
+ * Note: Last SGE may have a length of 0 - which should be ok.
+ */
+ thisxfer = sg_dma_len(sg);
+
+ v2 = sg_dma_address(sg);
+ mptscsih_add_sge(psge, sgflags | thisxfer, v2);
+ /*
+ sg++;
+ psge += (sizeof(u32) + sizeof(dma_addr_t));
+ */
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ sg_done++;
+
+ if (chainSge) {
+ /* The current buffer is a chain buffer,
+ * but there is not another one.
+ * Update the chain element
+ * Offset and Length fields.
+ */
+ mptscsih_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
+ } else {
+ /* The current buffer is the original MF
+ * and there is no Chain buffer.
+ */
+ pReq->ChainOffset = 0;
+ }
+ } else {
+ /* At least one chain buffer is needed.
+ * Complete the first MF
+ * - last SGE element, set the LastElement bit
+ * - set ChainOffset (words) for orig MF
+ * (OR finish previous MF chain buffer)
+ * - update MFStructPtr ChainIndex
+ * - Populate chain element
+ * Also
+ * Loop until done.
+ */
+
+ dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
+ hd->ioc->name, sg_done));
+
+ /* Set LAST_ELEMENT flag for last non-chain element
+ * in the buffer. Since psge points at the NEXT
+ * SGE element, go back one SGE element, update the flags
+ * and reset the pointer. (Note: sgflags & thisxfer are already
+ * set properly).
+ */
+ if (sg_done) {
+ u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
+ sgflags = le32_to_cpu(*ptmp);
+ sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
+ *ptmp = cpu_to_le32(sgflags);
+ }
+
+ if (chainSge) {
+ /* The current buffer is a chain buffer.
+ * chainSge points to the previous Chain Element.
+ * Update its chain element Offset and Length (must
+ * include chain element size) fields.
+ * Old chain element is now complete.
+ */
+ u8 nextChain = (u8) (sgeOffset >> 2);
+ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
+ mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
+ } else {
+ /* The original MF buffer requires a chain buffer -
+ * set the offset.
+ * Last element in this MF is a chain element.
+ */
+ pReq->ChainOffset = (u8) (sgeOffset >> 2);
+ }
+
+ sges_left -= sg_done;
+
+
+ /* NOTE: psge points to the beginning of the chain element
+ * in current buffer. Get a chain buffer.
+ */
+ if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED)
+ return FAILED;
+
+ /* Update the tracking arrays.
+ * If chainSge == NULL, update ReqToChain, else ChainToChain
+ */
+ if (chainSge) {
+ hd->ChainToChain[chain_idx] = newIndex;
+ } else {
+ hd->ReqToChain[req_idx] = newIndex;
+ }
+ chain_idx = newIndex;
+ chain_dma_off = hd->ioc->req_sz * chain_idx;
+
+ /* Populate the chainSGE for the current buffer.
+ * - Set chain buffer pointer to psge and fill
+ * out the Address and Flags fields.
+ */
+ chainSge = (char *) psge;
+ dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
+ psge, req_idx));
+
+ /* Start the SGE for the next buffer
+ */
+ psge = (char *) (hd->ChainBuffer + chain_dma_off);
+ sgeOffset = 0;
+ sg_done = 0;
+
+ dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
+ psge, chain_idx));
+
+ /* Start the SGE for the next buffer
+ */
+
+ goto nextSGEset;
+ }
+
+ return SUCCESS;
+} /* mptscsih_AddSGE() */
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_io_done - Main SCSI IO callback routine registered to
+ * Fusion MPT (base) driver
+ * @ioc: Pointer to MPT_ADAPTER structure
+ * @mf: Pointer to original MPT request frame
+ * @r: Pointer to MPT reply frame (NULL if TurboReply)
+ *
+ * This routine is called from mpt.c::mpt_interrupt() at the completion
+ * of any SCSI IO request.
+ * This routine is registered with the Fusion MPT (base) driver at driver
+ * load/init time via the mpt_register() API call.
+ *
+ * Returns 1 indicating alloc'd request frame ptr should be freed.
+ */
+static int
+mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
+{
+ Scsi_Cmnd *sc;
+ MPT_SCSI_HOST *hd;
+ SCSIIORequest_t *pScsiReq;
+ SCSIIOReply_t *pScsiReply;
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
+ unsigned long flags;
+#endif
+ u16 req_idx;
+
+ hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
+
+ if ((mf == NULL) ||
+ (mf >= MPT_INDEX_2_MFPTR(ioc, ioc->req_depth))) {
+ printk(MYIOC_s_ERR_FMT "%s req frame ptr! (=%p)!\n",
+ ioc->name, mf?"BAD":"NULL", (void *) mf);
+ return 0;
+ }
+
+ req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ sc = hd->ScsiLookup[req_idx];
+ if (sc == NULL) {
+ MPIHeader_t *hdr = (MPIHeader_t *)mf;
+
+ atomic_dec(&queue_depth);
+
+ /* writeSDP1 will use the ScsiDoneCtx
+ * There is no processing for the reply.
+ * Just return to the calling function.
+ */
+ if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST)
+ printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name);
+
+ mptscsih_freeChainBuffers(hd, req_idx);
+ return 1;
+ }
+
+ dmfprintk((MYIOC_s_INFO_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n",
+ ioc->name, mf, mr, sc, req_idx));
+
+ atomic_dec(&queue_depth);
+
+ sc->result = DID_OK << 16; /* Set default reply as OK */
+ pScsiReq = (SCSIIORequest_t *) mf;
+ pScsiReply = (SCSIIOReply_t *) mr;
+
+ if (pScsiReply == NULL) {
+ /* special context reply handling */
+
+ /* If regular Inquiry cmd - save inquiry data
+ */
+ if (pScsiReq->CDB[0] == INQUIRY && !(pScsiReq->CDB[1] & 0x3)) {
+ int dlen;
+
+ dlen = le32_to_cpu(pScsiReq->DataLength);
+ if (dlen >= SCSI_STD_INQUIRY_BYTES) {
+ mptscsih_initTarget(hd,
+ hd->port,
+ sc->target,
+ pScsiReq->LUN[1],
+ sc->buffer,
+ dlen);
+ }
+ }
+#ifdef MPT_SAVE_AUTOSENSE
+ clear_sense_flag(hd, pScsiReq);
+#endif
+ } else {
+ u32 xfer_cnt;
+ u16 status;
+ u8 scsi_state;
+
+ status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK;
+ scsi_state = pScsiReply->SCSIState;
+
+ dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n",
+ ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1],
+ mf, mr, sc));
+ dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh"
+ ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n",
+ status, scsi_state, pScsiReply->SCSIStatus,
+ le32_to_cpu(pScsiReply->IOCLogInfo)));
+
+ if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)
+ copy_sense_data(sc, hd, mf, pScsiReply);
+
+ /*
+ * Look for + dump FCP ResponseInfo[]!
+ */
+ if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID) {
+ dprintk((KERN_NOTICE " FCP_ResponseInfo=%08xh\n",
+ le32_to_cpu(pScsiReply->ResponseInfo)));
+ }
+
+ switch(status) {
+ case MPI_IOCSTATUS_BUSY: /* 0x0002 */
+ /* CHECKME!
+ * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry)
+ * But not: DID_BUS_BUSY lest one risk
+ * killing interrupt handler:-(
+ */
+ sc->result = STS_BUSY;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */
+ case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */
+ sc->result = DID_BAD_TARGET << 16;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */
+ /* Spoof to SCSI Selection Timeout! */
+ sc->result = DID_NO_CONNECT << 16;
+
+ if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF)
+ hd->sel_timeout[pScsiReq->TargetID]++;
+ break;
+
+ case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */
+#ifndef MPT_SCSI_USE_NEW_EH
+ search_taskQ_for_cmd(sc, hd);
+#endif
+ /* Linux handles an unsolicited DID_RESET better
+ * than an unsolicited DID_ABORT.
+ */
+ sc->result = DID_RESET << 16;
+
+ /* GEM Workaround. */
+ if (hd->is_spi)
+ mptscsih_no_negotiate(hd, sc->target);
+ break;
+
+ case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */
+ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */
+#ifndef MPT_SCSI_USE_NEW_EH
+ search_taskQ_for_cmd(sc, hd);
+#endif
+ sc->result = DID_RESET << 16;
+
+ /* GEM Workaround. */
+ if (hd->is_spi)
+ mptscsih_no_negotiate(hd, sc->target);
+ break;
+
+ case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */
+ case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */
+ /*
+ * YIKES! I just discovered that SCSI IO which
+ * returns check condition, SenseKey=05 (ILLEGAL REQUEST)
+ * and ASC/ASCQ=94/01 (LSI Logic RAID vendor specific),
+ * comes down this path!
+ * Do upfront check for valid SenseData and give it
+ * precedence!
+ */
+ sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
+#ifdef MPT_SAVE_AUTOSENSE
+ clear_sense_flag(hd, pScsiReq);
+#endif
+ if (scsi_state == 0) {
;
} else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
/* Have already saved the status and sense data
;
} else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) {
/*
- * If running agains circa 200003dd 909 MPT f/w,
+ * If running against circa 200003dd 909 MPT f/w,
* may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL
* (QUEUE_FULL) returned from device! --> get 0x0000?128
* and with SenseBytes set to 0.
hd->ScsiLookup[req_idx] = NULL;
- sc->host_scribble = NULL; /* CHECKME! - Do we need to clear this??? */
+#ifndef MPT_SCSI_USE_NEW_EH
+ sc->host_scribble = NULL;
+#endif
MPT_HOST_LOCK(flags);
sc->scsi_done(sc); /* Issue the command callback */
int ii;
int max = hd->ioc->req_depth;
-#ifndef MPT_SCSI_USE_NEW_EH
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
unsigned long flags;
#endif
search_taskQ_for_cmd(SCpnt, hd);
#endif
- /* Search pendingQ, if found,
+ /* Search pendingQ, if found,
* delete from Q. If found, do not decrement
* queue_depth, command never posted.
*/
* of chain buffers to be allocated.
* index = chain_idx
*
- * Calculate the number of chain buffers needed(plus 1) per I/O
+ * Calculate the number of chain buffers needed(plus 1) per I/O
* then multiply the the maximum number of simultaneous cmds
*
* num_sge = num sge in request frame + last chain buffer
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int BeenHereDoneThat = 0;
+static char *info_kbuf = NULL;
/* SCSI host fops start here... */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
tpnt->proc_dir = &proc_mpt_scsihost;
#endif
+ tpnt->proc_info = mptscsih_proc_info;
sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
if (sh != NULL) {
- mptscsih_lock(this, flags);
+ spin_lock_irqsave(&this->FreeQlock, flags);
sh->io_port = 0;
sh->n_io_port = 0;
sh->irq = 0;
} else {
numSGE = 1 + (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
(this->req_sz - 64) / (sizeof(dma_addr_t) + sizeof(u32));
- }
+ }
if (numSGE < sh->sg_tablesize) {
/* Reset this value */
*/
scsi_set_pci_device(sh, this->pcidev);
- mptscsih_unlock(this, flags);
+ spin_unlock_irqrestore(&this->FreeQlock, flags);
hd = (MPT_SCSI_HOST *) sh->hostdata;
hd->ioc = this;
done:
if (mpt_scsi_hosts > 0)
register_reboot_notifier(&mptscsih_notifier);
+ else {
+ mpt_reset_deregister(ScsiDoneCtx);
+ dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notifications\n"));
+
+ mpt_event_deregister(ScsiDoneCtx);
+ dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notifications\n"));
+
+ mpt_deregister(ScsiScanDvCtx);
+ mpt_deregister(ScsiTaskCtx);
+ mpt_deregister(ScsiDoneCtx);
+
+ if (info_kbuf != NULL)
+ kfree(info_kbuf);
+ }
return mpt_scsi_hosts;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static char *info_kbuf = NULL;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* mptscsih_release - Unregister SCSI host from linux scsi mid-layer
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_info - Return information about MPT adapter
- * @SChost: Pointer to Scsi_Host structure
- *
- * (linux Scsi_Host_Template.info routine)
- *
- * Returns pointer to buffer where information was written.
- */
-const char *
-mptscsih_info(struct Scsi_Host *SChost)
-{
- MPT_SCSI_HOST *h;
- int size = 0;
-
- if (info_kbuf == NULL)
- if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
- return info_kbuf;
-
- h = (MPT_SCSI_HOST *)SChost->hostdata;
- info_kbuf[0] = '\0';
- mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0);
- info_kbuf[size-1] = '\0';
-
- return info_kbuf;
-}
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static int max_qd = 1;
-#if 0
-static int index_log[128];
-static int index_ent = 0;
-static __inline__ void ADD_INDEX_LOG(int req_ent)
-{
- int i = index_ent++;
-
- index_log[i & (128 - 1)] = req_ent;
-}
-#else
-#define ADD_INDEX_LOG(req_ent) do { } while(0)
-#endif
-
-#ifdef DROP_TEST
-#define DROP_IOC 1 /* IOC to force failures */
-#define DROP_TARGET 3 /* Target ID to force failures */
-#define DROP_THIS_CMD 10000 /* iteration to drop command */
-static int dropCounter = 0;
-static int dropTestOK = 0; /* num did good */
-static int dropTestBad = 0; /* num did bad */
-static int dropTestNum = 0; /* total = good + bad + incomplete */
-static int numTotCmds = 0;
-static MPT_FRAME_HDR *dropMfPtr = NULL;
-static int numTMrequested = 0;
-#endif
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W.
- * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx)
- * @id: IOC id number
- * @mf: Pointer to message frame
- *
- * Handles the call to mptbase for posting request and queue depth
- * tracking.
- *
- * Returns none.
- */
-static void
-mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf)
-{
- /* Main banana... */
- atomic_inc(&queue_depth);
- if (atomic_read(&queue_depth) > max_qd) {
- max_qd = atomic_read(&queue_depth);
- dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd));
- }
-
- mpt_put_msg_frame(context, id, mf);
-
- return;
-}
-
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/**
- * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
- * @SCpnt: Pointer to Scsi_Cmnd structure
- * @done: Pointer SCSI mid-layer IO completion function
+ * mptscsih_info - Return information about MPT adapter
+ * @SChost: Pointer to Scsi_Host structure
*
- * (linux Scsi_Host_Template.queuecommand routine)
- * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
- * from a linux Scsi_Cmnd request and send it to the IOC.
+ * (linux Scsi_Host_Template.info routine)
*
- * Returns 0. (rtn value discarded by linux scsi mid-layer)
+ * Returns pointer to buffer where information was written.
*/
-int
-mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+const char *
+mptscsih_info(struct Scsi_Host *SChost)
{
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- SCSIIORequest_t *pScsiReq;
- VirtDevice *pTarget;
- MPT_DONE_Q *buffer = NULL;
- unsigned long flags;
- int target;
- int lun;
- int datadir;
- u32 datalen;
- u32 scsictl;
- u32 scsidir;
- u32 qtag;
- u32 cmd_len;
- int my_idx;
- int ii;
- int rc;
- int did_errcode;
- int issueCmd;
-
- did_errcode = 0;
- hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
- target = SCpnt->target;
- lun = SCpnt->lun;
- SCpnt->scsi_done = done;
-
- pTarget = hd->Targets[target];
-
- dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
- (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
+ MPT_SCSI_HOST *h = NULL;
+ int size = 0;
-#ifdef MPT_SAVE_AUTOSENSE
- /* 20000617 -sralston
- * GRRRRR... Shouldn't have to do this but...
- * Do explicit check for REQUEST_SENSE and cached SenseData.
- * If yes, return cached SenseData.
- */
- if (SCpnt->cmnd[0] == REQUEST_SENSE) {
- u8 *dest = NULL;
- int sz;
+ if (info_kbuf == NULL)
+ if ((info_kbuf = kmalloc(0x1000 /* 4Kb */, GFP_KERNEL)) == NULL)
+ return info_kbuf;
- if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) {
- pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; //sjr-moved-here
- if (!SCpnt->use_sg) {
- dest = SCpnt->request_buffer;
- } else {
- struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
- if (sg)
- dest = (u8 *)(ulong)sg_dma_address(sg);
- }
+ h = (MPT_SCSI_HOST *)SChost->hostdata;
+ info_kbuf[0] = '\0';
+ if (h) {
+ mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0);
+ info_kbuf[size-1] = '\0';
+ }
- if (dest) {
- sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen);
- memcpy(dest, pTarget->sense, sz);
+ return info_kbuf;
+}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- SCpnt->resid = SCpnt->request_bufflen - sz;
-#endif
- SCpnt->result = 0;
- SCpnt->scsi_done(SCpnt);
+struct info_str {
+ char *buffer;
+ int length;
+ int offset;
+ int pos;
+};
- //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
+static void copy_mem_info(struct info_str *info, char *data, int len)
+{
+ if (info->pos + len > info->length)
+ len = info->length - info->pos;
- return 0;
- }
- }
+ if (info->pos + len < info->offset) {
+ info->pos += len;
+ return;
}
-#endif
- if (hd->resetPending) {
- /* Prevent new commands from being issued
- * while reloading the FW.
- */
- did_errcode = 1;
- goto did_error;
+ if (info->pos < info->offset) {
+ data += (info->offset - info->pos);
+ len -= (info->offset - info->pos);
}
- /*
- * Put together a MPT SCSI request...
- */
- if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) {
- dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
- hd->ioc->name));
- did_errcode = 2;
- goto did_error;
+ if (len > 0) {
+ memcpy(info->buffer + info->pos, data, len);
+ info->pos += len;
}
+}
- pScsiReq = (SCSIIORequest_t *) mf;
+static int copy_info(struct info_str *info, char *fmt, ...)
+{
+ va_list args;
+ char buf[81];
+ int len;
- my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
+ va_start(args, fmt);
+ len = vsprintf(buf, fmt, args);
+ va_end(args);
- ADD_INDEX_LOG(my_idx);
+ copy_mem_info(info, buf, len);
+ return len;
+}
- /*
- * The scsi layer should be handling this stuff
- * (In 2.3.x it does -DaveM)
- */
+static int mptscsih_host_info(MPT_ADAPTER *ioc, char *pbuf, off_t offset, int len)
+{
+ struct info_str info;
- /* BUG FIX! 19991030 -sralston
- * TUR's being issued with scsictl=0x02000000 (DATA_IN)!
- * Seems we may receive a buffer (datalen>0) even when there
- * will be no data transfer! GRRRRR...
- */
- datadir = mptscsih_io_direction(SCpnt);
- if (datadir == SCSI_DATA_READ) {
- datalen = SCpnt->request_bufflen;
- scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
- } else if (datadir == SCSI_DATA_WRITE) {
- datalen = SCpnt->request_bufflen;
- scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
- } else {
- datalen = 0;
- scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
- }
+ info.buffer = pbuf;
+ info.length = len;
+ info.offset = offset;
+ info.pos = 0;
- /* Default to untagged. Once a target structure has been allocated,
- * use the Inquiry data to determine if device supports tagged.
- */
- qtag = MPI_SCSIIO_CONTROL_UNTAGGED;
- if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
- && (SCpnt->device->tagged_supported)) {
- /*
- * Some drives are too stupid to handle fairness issues
- * with tagged queueing. We throw in the odd ordered
- * tag to stop them starving themselves.
- */
- if ((jiffies - hd->qtag_tick) > (5*HZ)) {
- qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
- hd->qtag_tick = jiffies;
+ copy_info(&info, "%s: %s, ", ioc->name, ioc->prod_name);
+ copy_info(&info, "%s%08xh, ", MPT_FW_REV_MAGIC_ID_STRING, ioc->facts.FWVersion.Word);
+ copy_info(&info, "Ports=%d, ", ioc->facts.NumberOfPorts);
+ copy_info(&info, "MaxQ=%d\n", ioc->req_depth);
+
+ return ((info.pos > info.offset) ? info.pos - info.offset : 0);
+}
+
+struct mptscsih_usrcmd {
+ ulong target;
+ ulong lun;
+ ulong data;
+ ulong cmd;
+};
+
+#define UC_GET_SPEED 0x10
+
+static void mptscsih_exec_user_cmd(MPT_ADAPTER *ioc, struct mptscsih_usrcmd *uc)
+{
+ CONFIGPARMS cfg;
+ dma_addr_t cfg_dma_addr = -1;
+ ConfigPageHeader_t header;
+
+ dprintk(("exec_user_command: ioc %p cmd %ld target=%ld\n",
+ ioc, uc->cmd, uc->target));
+
+ switch (uc->cmd) {
+ case UC_GET_SPEED:
+ {
+ SCSIDevicePage0_t *pData = NULL;
+
+ if (ioc->spi_data.sdp0length == 0)
+ return;
+
+ pData = (SCSIDevicePage0_t *)pci_alloc_consistent(ioc->pcidev,
+ ioc->spi_data.sdp0length * 4, &cfg_dma_addr);
+
+ if (pData == NULL)
+ return;
+
+ header.PageVersion = ioc->spi_data.sdp0version;
+ header.PageLength = ioc->spi_data.sdp0length;
+ header.PageNumber = 0;
+ header.PageType = MPI_CONFIG_PAGETYPE_SCSI_DEVICE;
+
+ cfg.hdr = &header;
+ cfg.action = MPI_CONFIG_ACTION_PAGE_READ_CURRENT;
+ cfg.dir = 0;
+ cfg.pageAddr = (u32) uc->target; /* bus << 8 | target */
+ cfg.physAddr = cfg_dma_addr;
+
+ if (mpt_config(ioc, &cfg) == 0) {
+ u32 np = le32_to_cpu(pData->NegotiatedParameters);
+ u32 tmp = np & MPI_SCSIDEVPAGE0_NP_WIDE;
+
+ printk("Target %d: %s;",
+ (u32) uc->target,
+ tmp ? "Wide" : "Narrow");
+
+ tmp = np & MPI_SCSIDEVPAGE0_NP_NEG_SYNC_OFFSET_MASK;
+ if (tmp) {
+ u32 speed = 0;
+ printk(" Synchronous");
+ tmp = (tmp >> 16);
+ printk(" (Offset=0x%x", tmp);
+ tmp = np & MPI_SCSIDEVPAGE0_NP_NEG_SYNC_PERIOD_MASK;
+ tmp = (tmp >> 8);
+ printk(" Factor=0x%x)", tmp);
+ if (tmp <= MPT_ULTRA320)
+ speed=160;
+ else if (tmp <= MPT_ULTRA160)
+ speed=80;
+ else if (tmp <= MPT_ULTRA2)
+ speed=40;
+ else if (tmp <= MPT_ULTRA)
+ speed=20;
+ else if (tmp <= MPT_FAST)
+ speed=10;
+ else if (tmp <= MPT_SCSI)
+ speed=5;
+
+ if (np & MPI_SCSIDEVPAGE0_NP_WIDE)
+ speed*=2;
+
+ printk(" %dMB/sec\n", speed);
+
+ } else
+ printk(" Asynchronous.\n");
+ } else {
+ printk("failed\n" );
+ }
+
+ pci_free_consistent(ioc->pcidev, ioc->spi_data.sdp0length * 4,
+ pData, cfg_dma_addr);
}
- else
- qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
+ break;
}
- scsictl = scsidir | qtag;
+}
- /* Use the above information to set up the message frame
- */
- pScsiReq->TargetID = target;
- pScsiReq->Bus = hd->port;
- pScsiReq->ChainOffset = 0;
- pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
- pScsiReq->CDBLength = SCpnt->cmd_len;
- pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
- pScsiReq->Reserved = 0;
- pScsiReq->MsgFlags = mpt_msg_flags();
- pScsiReq->LUN[0] = 0;
- pScsiReq->LUN[1] = lun;
- pScsiReq->LUN[2] = 0;
- pScsiReq->LUN[3] = 0;
- pScsiReq->LUN[4] = 0;
- pScsiReq->LUN[5] = 0;
- pScsiReq->LUN[6] = 0;
- pScsiReq->LUN[7] = 0;
- pScsiReq->Control = cpu_to_le32(scsictl);
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+#define digit_to_bin(c) ((c) - '0')
+#define is_space(c) ((c) == ' ' || (c) == '\t')
- /*
- * Write SCSI CDB into the message
- */
- cmd_len = SCpnt->cmd_len;
- for (ii=0; ii < cmd_len; ii++)
- pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
- for (ii=cmd_len; ii < 16; ii++)
- pScsiReq->CDB[ii] = 0;
+static int skip_spaces(char *ptr, int len)
+{
+ int cnt, c;
- /* DataLength */
- pScsiReq->DataLength = cpu_to_le32(datalen);
+ for (cnt = len; cnt > 0 && (c = *ptr++) && is_space(c); cnt --);
- /* SenseBuffer low address */
- pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
- + (my_idx * MPT_SENSE_BUFFER_ALLOC));
+ return (len - cnt);
+}
- /* Now add the SG list
- * Always have a SGE even if null length.
- */
- rc = SUCCESS;
- if (datalen == 0) {
- /* Add a NULL SGE */
- mpt_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
- (dma_addr_t) -1);
- } else {
- /* Add a 32 or 64 bit SGE */
- rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx);
+static int get_int_arg(char *ptr, int len, ulong *pv)
+{
+ int cnt, c;
+ ulong v;
+ for (v = 0, cnt = len; cnt > 0 && (c=*ptr++) && is_digit(c); cnt --) {
+ v = (v * 10) + digit_to_bin(c);
}
+ if (pv)
+ *pv = v;
- if (rc == SUCCESS) {
- hd->ScsiLookup[my_idx] = SCpnt;
- SCpnt->host_scribble = NULL;
+ return (len - cnt);
+}
-#ifdef DROP_TEST
- numTotCmds++;
- /* If the IOC number and target match, increment
- * counter. If counter matches DROP_THIS, do not
- * issue command to FW to force a reset.
- * Save the MF pointer so we can free resources
- * when task mgmt completes.
- */
- if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) {
- dropCounter++;
- if (dropCounter == DROP_THIS_CMD) {
- dropCounter = 0;
+static int is_keyword(char *ptr, int len, char *verb)
+{
+ int verb_len = strlen(verb);
- /* If global is set, then we are already
- * doing something - so keep issuing commands.
- */
- if (dropMfPtr == NULL) {
- dropTestNum++;
- dropMfPtr = mf;
- atomic_inc(&queue_depth);
- printk(MYIOC_s_INFO_FMT
- "Dropped SCSI cmd (%p)\n",
- hd->ioc->name, SCpnt);
- printk("mf (%p) req (%4x) tot cmds (%d)\n",
- mf, my_idx, numTotCmds);
+ if (len >= strlen(verb) && !memcmp(verb, ptr, verb_len))
+ return verb_len;
+ else
+ return 0;
+}
- return 0;
- }
- }
- }
-#endif
+#define SKIP_SPACES(min_spaces) \
+ if ((arg_len = skip_spaces(ptr,len)) < (min_spaces)) \
+ return -EINVAL; \
+ ptr += arg_len; \
+ len -= arg_len;
- /* SCSI specific processing */
- issueCmd = 1;
- if (hd->is_spi) {
- int dvStatus = hd->ioc->spi_data.dvStatus[target];
+#define GET_INT_ARG(v) \
+ if (!(arg_len = get_int_arg(ptr,len, &(v)))) \
+ return -EINVAL; \
+ ptr += arg_len; \
+ len -= arg_len;
+
+static int mptscsih_user_command(MPT_ADAPTER *ioc, char *buffer, int length)
+{
+ char *ptr = buffer;
+ struct mptscsih_usrcmd cmd, *uc = &cmd;
+ ulong target;
+ int arg_len;
+ int len = length;
+
+ uc->target = uc->cmd = uc->lun = uc->data = 0;
+
+ if ((len > 0) && (ptr[len -1] == '\n'))
+ --len;
- if (dvStatus || hd->ioc->spi_data.forceDv) {
+ if ((arg_len = is_keyword(ptr, len, "getspeed")) != 0)
+ uc->cmd = UC_GET_SPEED;
+ else
+ arg_len = 0;
- /* Write SDP1 on this I/O to this target */
- if (dvStatus & MPT_SCSICFG_NEGOTIATE) {
- mptscsih_writeSDP1(hd, 0, target, hd->negoNvram);
- dvStatus &= ~MPT_SCSICFG_NEGOTIATE;
- hd->ioc->spi_data.dvStatus[target] = dvStatus;
- } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) {
- mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO);
- dvStatus &= ~MPT_SCSICFG_BLK_NEGO;
- hd->ioc->spi_data.dvStatus[target] = dvStatus;
- }
+ dprintk(("user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd));
-#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- if ((dvStatus & MPT_SCSICFG_NEED_DV) || hd->ioc->spi_data.forceDv) {
- unsigned long lflags;
- /* Schedule DV if necessary */
- spin_lock_irqsave(&dvtaskQ_lock, lflags);
- if (!dvtaskQ_active) {
- dvtaskQ_active = 1;
- spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
- MPT_INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd);
+ if (!arg_len)
+ return -EINVAL;
- SCHEDULE_TASK(&mptscsih_dvTask);
- } else {
- spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
- }
- hd->ioc->spi_data.forceDv = 0;
- }
+ ptr += arg_len;
+ len -= arg_len;
- /* Trying to do DV to this target, extend timeout.
- * Wait to issue intil flag is clear
- */
- if (dvStatus & MPT_SCSICFG_DV_PENDING) {
- mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
- issueCmd = 0;
- }
+ switch(uc->cmd) {
+ case UC_GET_SPEED:
+ SKIP_SPACES(1);
+ GET_INT_ARG(target);
+ uc->target = target;
+ break;
+ }
- /* Set the DV flags.
- */
- if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
- mptscsih_set_dvflags(hd, pScsiReq);
-#endif
- }
- }
+ dprintk(("user_command: target=%ld len=%d\n", uc->target, len));
- if (issueCmd) {
- mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
- dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
- hd->ioc->name, SCpnt, mf, my_idx));
- } else {
- ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n",
- hd->ioc->name, SCpnt, my_idx));
- /* Place this command on the pendingQ if possible */
- spin_lock_irqsave(&hd->freedoneQlock, flags);
- if (!Q_IS_EMPTY(&hd->freeQ)) {
- buffer = hd->freeQ.head;
- Q_DEL_ITEM(buffer);
+ if (len)
+ return -EINVAL;
+ else {
+ /* process this command ...
+ */
+ mptscsih_exec_user_cmd(ioc, uc);
+ }
+ /* Not yet implemented */
+ return length;
+}
- /* Save the mf pointer
- */
- buffer->argp = (void *)mf;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/**
+ * mptscsih_proc_info - Return information about MPT adapter
+ *
+ * (linux Scsi_Host_Template.info routine)
+ *
+ * buffer: if write, user data; if read, buffer for user
+ * length: if write, return length;
+ * offset: if write, 0; if read, the current offset into the buffer from
+ * the previous read.
+ * hostno: scsi host number
+ * func: if write = 1; if read = 0
+ */
+int mptscsih_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int func)
+{
+ MPT_ADAPTER *ioc = NULL;
+ MPT_SCSI_HOST *hd = NULL;
+ int size = 0;
- /* Add to the pendingQ
- */
- Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q);
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- } else {
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- SCpnt->result = (DID_BUS_BUSY << 16);
- SCpnt->scsi_done(SCpnt);
- }
+ dprintk(("Called mptscsih_proc_info: hostno=%d, func=%d\n", hostno, func));
+ dprintk(("buffer %p, start=%p (%p) offset=%ld length = %d\n",
+ buffer, start, *start, offset, length));
+
+ for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
+ if ((ioc->sh) && (ioc->sh->host_no == hostno)) {
+ hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
+ break;
}
+ }
+ if ((ioc == NULL) || (ioc->sh == NULL) || (hd == NULL))
+ return 0;
+
+ if (func) {
+ size = mptscsih_user_command(ioc, buffer, length);
} else {
- mptscsih_freeChainBuffers(hd, my_idx);
- mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
- did_errcode = 3;
- goto did_error;
+ if (start)
+ *start = buffer;
+
+ size = mptscsih_host_info(ioc, buffer, offset, length);
}
- return 0;
+ return size;
+}
-did_error:
- dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n",
- hd->ioc->name, did_errcode, SCpnt));
- /* Just wish OS to issue a retry */
- SCpnt->result = (DID_BUS_BUSY << 16);
- spin_lock_irqsave(&hd->freedoneQlock, flags);
- if (!Q_IS_EMPTY(&hd->freeQ)) {
- buffer = hd->freeQ.head;
- Q_DEL_ITEM(buffer);
- /* Set the Scsi_Cmnd pointer
- */
- buffer->argp = (void *)SCpnt;
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+ static int max_qd = 1;
+#if 0
+static int index_log[128];
+static int index_ent = 0;
+static __inline__ void ADD_INDEX_LOG(int req_ent)
+{
+ int i = index_ent++;
- /* Add to the doneQ
- */
- Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q);
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- } else {
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- SCpnt->scsi_done(SCpnt);
+ index_log[i & (128 - 1)] = req_ent;
+}
+#else
+#define ADD_INDEX_LOG(req_ent) do { } while(0)
+#endif
+
+#ifdef DROP_TEST
+#define DROP_IOC 1 /* IOC to force failures */
+#define DROP_TARGET 3 /* Target ID to force failures */
+#define DROP_THIS_CMD 10000 /* iteration to drop command */
+static int dropCounter = 0;
+static int dropTestOK = 0; /* num did good */
+static int dropTestBad = 0; /* num did bad */
+static int dropTestNum = 0; /* total = good + bad + incomplete */
+static int numTotCmds = 0;
+static MPT_FRAME_HDR *dropMfPtr = NULL;
+static int numTMrequested = 0;
+#endif
+
+/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
+/*
+ * mptscsih_put_msgframe - Wrapper routine to post message frame to F/W.
+ * @context: Call back context (ScsiDoneCtx, ScsiScanDvCtx)
+ * @id: IOC id number
+ * @mf: Pointer to message frame
+ *
+ * Handles the call to mptbase for posting request and queue depth
+ * tracking.
+ *
+ * Returns none.
+ */
+static inline void
+mptscsih_put_msgframe(int context, int id, MPT_FRAME_HDR *mf)
+{
+ /* Main banana... */
+ atomic_inc(&queue_depth);
+ if (atomic_read(&queue_depth) > max_qd) {
+ max_qd = atomic_read(&queue_depth);
+ dprintk((KERN_INFO MYNAM ": Queue depth now %d.\n", max_qd));
}
- return 0;
+ mpt_put_msg_frame(context, id, mf);
+
+ return;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the
- * SCSIIORequest_t Message Frame.
- * @hd: Pointer to MPT_SCSI_HOST structure
+/**
+ * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine.
* @SCpnt: Pointer to Scsi_Cmnd structure
- * @pReq: Pointer to SCSIIORequest_t structure
+ * @done: Pointer SCSI mid-layer IO completion function
*
- * Returns ...
+ * (linux Scsi_Host_Template.queuecommand routine)
+ * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest
+ * from a linux Scsi_Cmnd request and send it to the IOC.
+ *
+ * Returns 0. (rtn value discarded by linux scsi mid-layer)
*/
-static int
-mptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt,
- SCSIIORequest_t *pReq, int req_idx)
+int
+mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
{
- char *psge;
- char *chainSge;
- struct scatterlist *sg;
- int frm_sz;
- int sges_left, sg_done;
- int chain_idx = MPT_HOST_NO_CHAIN;
- int sgeOffset;
- int numSgeSlots, numSgeThisFrame;
- u32 sgflags, sgdir, thisxfer = 0;
- int chain_dma_off = 0;
- int newIndex;
+ MPT_SCSI_HOST *hd;
+ MPT_FRAME_HDR *mf;
+ SCSIIORequest_t *pScsiReq;
+ VirtDevice *pTarget;
+ MPT_DONE_Q *buffer = NULL;
+ unsigned long flags;
+ int target;
+ int lun;
+ int datadir;
+ u32 datalen;
+ u32 scsictl;
+ u32 scsidir;
+ u32 qtag;
+ u32 cmd_len;
+ int my_idx;
int ii;
- dma_addr_t v2;
+ int rc;
+ int did_errcode;
+ int issueCmd;
- sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK;
- if (sgdir == MPI_SCSIIO_CONTROL_WRITE) {
- sgdir = MPT_TRANSFER_HOST_TO_IOC;
- } else {
- sgdir = MPT_TRANSFER_IOC_TO_HOST;
- }
+ did_errcode = 0;
+ hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata;
+ target = SCpnt->target;
+ lun = SCpnt->lun;
+ SCpnt->scsi_done = done;
- psge = (char *) &pReq->SGL;
- frm_sz = hd->ioc->req_sz;
+ pTarget = hd->Targets[target];
- /* Map the data portion, if any.
- * sges_left = 0 if no data transfer.
+ dmfprintk((MYIOC_s_INFO_FMT "qcmd: SCpnt=%p, done()=%p\n",
+ (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
+
+#ifdef MPT_SAVE_AUTOSENSE
+ /* 20000617 -sralston
+ * GRRRRR... Shouldn't have to do this but...
+ * Do explicit check for REQUEST_SENSE and cached SenseData.
+ * If yes, return cached SenseData.
*/
- sges_left = SCpnt->use_sg;
- if (SCpnt->use_sg) {
- sges_left = pci_map_sg(hd->ioc->pcidev,
- (struct scatterlist *) SCpnt->request_buffer,
- SCpnt->use_sg,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
- } else if (SCpnt->request_bufflen) {
- dma_addr_t buf_dma_addr;
- scPrivate *my_priv;
+ if (SCpnt->cmnd[0] == REQUEST_SENSE) {
+ u8 *dest = NULL;
+ int sz;
- buf_dma_addr = pci_map_single(hd->ioc->pcidev,
- SCpnt->request_buffer,
- SCpnt->request_bufflen,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_VALID_SENSE)) {
+ pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE; //sjr-moved-here
+ if (!SCpnt->use_sg) {
+ dest = SCpnt->request_buffer;
+ } else {
+ struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+ if (sg)
+ dest = (u8 *)(ulong)sg_dma_address(sg);
+ }
+
+ if (dest) {
+ sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen);
+ memcpy(dest, pTarget->sense, sz);
- /* We hide it here for later unmap. */
- my_priv = (scPrivate *) &SCpnt->SCp;
- my_priv->p1 = (void *)(ulong) buf_dma_addr;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
+ SCpnt->resid = SCpnt->request_bufflen - sz;
+#endif
+ SCpnt->result = 0;
+ SCpnt->scsi_done(SCpnt);
- dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
- hd->ioc->name, SCpnt, SCpnt->request_bufflen));
+ //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
- mpt_add_sge((char *) &pReq->SGL,
- 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
- buf_dma_addr);
+ return 0;
+ }
+ }
+ }
+#endif
- return SUCCESS;
+ if (hd->resetPending) {
+ /* Prevent new commands from being issued
+ * while reloading the FW.
+ */
+ did_errcode = 1;
+ goto did_error;
}
- /* Handle the SG case.
+ /*
+ * Put together a MPT SCSI request...
*/
- sg = (struct scatterlist *) SCpnt->request_buffer;
- sg_done = 0;
- sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
- chainSge = NULL;
+ if ((mf = mpt_get_msg_frame(ScsiDoneCtx, hd->ioc->id)) == NULL) {
+ dprintk((MYIOC_s_WARN_FMT "QueueCmd, no msg frames!!\n",
+ hd->ioc->name));
+ did_errcode = 2;
+ goto did_error;
+ }
- /* Prior to entering this loop - the following must be set
- * current MF: sgeOffset (bytes)
- * chainSge (Null if original MF is not a chain buffer)
- * sg_done (num SGE done for this MF)
- */
+ pScsiReq = (SCSIIORequest_t *) mf;
-nextSGEset:
- numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
- numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
+ my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
+ ADD_INDEX_LOG(my_idx);
- /* Get first (num - 1) SG elements
- * Skip any SG entries with a length of 0
- * NOTE: at finish, sg and psge pointed to NEXT data/location positions
+ /*
+ * The scsi layer should be handling this stuff
+ * (In 2.3.x it does -DaveM)
*/
- for (ii=0; ii < (numSgeThisFrame-1); ii++) {
- thisxfer = sg_dma_len(sg);
- if (thisxfer == 0) {
- sg ++; /* Get next SG element from the OS */
- sg_done++;
- continue;
- }
-
- v2 = sg_dma_address(sg);
- mpt_add_sge(psge, sgflags | thisxfer, v2);
- sg++; /* Get next SG element from the OS */
- psge += (sizeof(u32) + sizeof(dma_addr_t));
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
- sg_done++;
+ /* BUG FIX! 19991030 -sralston
+ * TUR's being issued with scsictl=0x02000000 (DATA_IN)!
+ * Seems we may receive a buffer (datalen>0) even when there
+ * will be no data transfer! GRRRRR...
+ */
+ datadir = mptscsih_io_direction(SCpnt);
+ if (datadir == SCSI_DATA_READ) {
+ datalen = SCpnt->request_bufflen;
+ scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
+ } else if (datadir == SCSI_DATA_WRITE) {
+ datalen = SCpnt->request_bufflen;
+ scsidir = MPI_SCSIIO_CONTROL_WRITE; /* DATA OUT (host-->ioc-->dev) */
+ } else {
+ datalen = 0;
+ scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
}
- if (numSgeThisFrame == sges_left) {
- /* Add last element, end of buffer and end of list flags.
- */
- sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
- MPT_SGE_FLAGS_END_OF_BUFFER |
- MPT_SGE_FLAGS_END_OF_LIST;
-
- /* Add last SGE and set termination flags.
- * Note: Last SGE may have a length of 0 - which should be ok.
+ /* Default to untagged. Once a target structure has been allocated,
+ * use the Inquiry data to determine if device supports tagged.
+ */
+ qtag = MPI_SCSIIO_CONTROL_UNTAGGED;
+ if (pTarget && (pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)
+ && (SCpnt->device->tagged_supported)) {
+ /*
+ * Some drives are too stupid to handle fairness issues
+ * with tagged queueing. We throw in the odd ordered
+ * tag to stop them starving themselves.
*/
- thisxfer = sg_dma_len(sg);
+ if ((jiffies - hd->qtag_tick) > (5*HZ)) {
+ qtag = MPI_SCSIIO_CONTROL_ORDEREDQ;
+ hd->qtag_tick = jiffies;
+ }
+ else
+ qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
+ }
+ scsictl = scsidir | qtag;
- v2 = sg_dma_address(sg);
- mpt_add_sge(psge, sgflags | thisxfer, v2);
- /*
- sg++;
- psge += (sizeof(u32) + sizeof(dma_addr_t));
- */
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
- sg_done++;
+ /* Use the above information to set up the message frame
+ */
+ pScsiReq->TargetID = target;
+ pScsiReq->Bus = hd->port;
+ pScsiReq->ChainOffset = 0;
+ pScsiReq->Function = MPI_FUNCTION_SCSI_IO_REQUEST;
+ pScsiReq->CDBLength = SCpnt->cmd_len;
+ pScsiReq->SenseBufferLength = MPT_SENSE_BUFFER_SIZE;
+ pScsiReq->Reserved = 0;
+ pScsiReq->MsgFlags = mpt_msg_flags();
+ pScsiReq->LUN[0] = 0;
+ pScsiReq->LUN[1] = lun;
+ pScsiReq->LUN[2] = 0;
+ pScsiReq->LUN[3] = 0;
+ pScsiReq->LUN[4] = 0;
+ pScsiReq->LUN[5] = 0;
+ pScsiReq->LUN[6] = 0;
+ pScsiReq->LUN[7] = 0;
+ pScsiReq->Control = cpu_to_le32(scsictl);
- if (chainSge) {
- /* The current buffer is a chain buffer,
- * but there is not another one.
- * Update the chain element
- * Offset and Length fields.
- */
- mpt_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
- } else {
- /* The current buffer is the original MF
- * and there is no Chain buffer.
- */
- pReq->ChainOffset = 0;
- }
- } else {
- /* At least one chain buffer is needed.
- * Complete the first MF
- * - last SGE element, set the LastElement bit
- * - set ChainOffset (words) for orig MF
- * (OR finish previous MF chain buffer)
- * - update MFStructPtr ChainIndex
- * - Populate chain element
- * Also
- * Loop until done.
- */
+ /*
+ * Write SCSI CDB into the message
+ * Should write from cmd_len up to 16, but skip for performance reasons.
+ */
+ cmd_len = SCpnt->cmd_len;
+ for (ii=0; ii < cmd_len; ii++)
+ pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
- dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
- hd->ioc->name, sg_done));
+ /* DataLength */
+ pScsiReq->DataLength = cpu_to_le32(datalen);
- /* Set LAST_ELEMENT flag for last non-chain element
- * in the buffer. Since psge points at the NEXT
- * SGE element, go back one SGE element, update the flags
- * and reset the pointer. (Note: sgflags & thisxfer are already
- * set properly).
- */
- if (sg_done) {
- u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t)));
- sgflags = le32_to_cpu(*ptmp);
- sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT;
- *ptmp = cpu_to_le32(sgflags);
- }
+ /* SenseBuffer low address */
+ pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
+ + (my_idx * MPT_SENSE_BUFFER_ALLOC));
- if (chainSge) {
- /* The current buffer is a chain buffer.
- * chainSge points to the previous Chain Element.
- * Update its chain element Offset and Length (must
- * include chain element size) fields.
- * Old chain element is now complete.
- */
- u8 nextChain = (u8) (sgeOffset >> 2);
- sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
- mpt_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
- } else {
- /* The original MF buffer requires a chain buffer -
- * set the offset.
- * Last element in this MF is a chain element.
- */
- pReq->ChainOffset = (u8) (sgeOffset >> 2);
- }
+ /* Now add the SG list
+ * Always have a SGE even if null length.
+ */
+ rc = SUCCESS;
+ if (datalen == 0) {
+ /* Add a NULL SGE */
+ mptscsih_add_sge((char *)&pScsiReq->SGL, MPT_SGE_FLAGS_SSIMPLE_READ | 0,
+ (dma_addr_t) -1);
+ } else {
+ /* Add a 32 or 64 bit SGE */
+ rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx);
+ }
- sges_left -= sg_done;
+ if (rc == SUCCESS) {
+ hd->ScsiLookup[my_idx] = SCpnt;
+ SCpnt->host_scribble = NULL;
- /* NOTE: psge points to the beginning of the chain element
- * in current buffer. Get a chain buffer.
+#ifdef DROP_TEST
+ numTotCmds++;
+ /* If the IOC number and target match, increment
+ * counter. If counter matches DROP_THIS, do not
+ * issue command to FW to force a reset.
+ * Save the MF pointer so we can free resources
+ * when task mgmt completes.
*/
- if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED)
- return FAILED;
+ if ((hd->ioc->id == DROP_IOC) && (target == DROP_TARGET)) {
+ dropCounter++;
- /* Update the tracking arrays.
- * If chainSge == NULL, update ReqToChain, else ChainToChain
- */
- if (chainSge) {
- hd->ChainToChain[chain_idx] = newIndex;
- } else {
- hd->ReqToChain[req_idx] = newIndex;
+ if (dropCounter == DROP_THIS_CMD) {
+ dropCounter = 0;
+
+ /* If global is set, then we are already
+ * doing something - so keep issuing commands.
+ */
+ if (dropMfPtr == NULL) {
+ dropTestNum++;
+ dropMfPtr = mf;
+ atomic_inc(&queue_depth);
+ printk(MYIOC_s_INFO_FMT
+ "Dropped SCSI cmd (%p)\n",
+ hd->ioc->name, SCpnt);
+ printk("mf (%p) req (%4x) tot cmds (%d)\n",
+ mf, my_idx, numTotCmds);
+
+ return 0;
+ }
+ }
}
- chain_idx = newIndex;
- chain_dma_off = hd->ioc->req_sz * chain_idx;
+#endif
- /* Populate the chainSGE for the current buffer.
- * - Set chain buffer pointer to psge and fill
- * out the Address and Flags fields.
- */
- chainSge = (char *) psge;
- dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
- psge, req_idx));
+ /* SCSI specific processing */
+ issueCmd = 1;
+ if (hd->is_spi) {
+ int dvStatus = hd->ioc->spi_data.dvStatus[target];
- /* Start the SGE for the next buffer
- */
- psge = (char *) (hd->ChainBuffer + chain_dma_off);
- sgeOffset = 0;
- sg_done = 0;
+ if (dvStatus || hd->ioc->spi_data.forceDv) {
- dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
- psge, chain_idx));
+ /* Write SDP1 on this I/O to this target */
+ if (dvStatus & MPT_SCSICFG_NEGOTIATE) {
+ mptscsih_writeSDP1(hd, 0, target, hd->negoNvram);
+ dvStatus &= ~MPT_SCSICFG_NEGOTIATE;
+ hd->ioc->spi_data.dvStatus[target] = dvStatus;
+ } else if (dvStatus & MPT_SCSICFG_BLK_NEGO) {
+ mptscsih_writeSDP1(hd, 0, target, MPT_SCSICFG_BLK_NEGO);
+ dvStatus &= ~MPT_SCSICFG_BLK_NEGO;
+ hd->ioc->spi_data.dvStatus[target] = dvStatus;
+ }
- /* Start the SGE for the next buffer
- */
+#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
+ if ((dvStatus & MPT_SCSICFG_NEED_DV) ||
+ (hd->ioc->spi_data.forceDv & MPT_SCSICFG_NEED_DV)) {
+ unsigned long lflags;
+ /* Schedule DV if necessary */
+ spin_lock_irqsave(&dvtaskQ_lock, lflags);
+ if (!dvtaskQ_active) {
+ dvtaskQ_active = 1;
+ spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
+ MPT_INIT_WORK(&mptscsih_dvTask, mptscsih_domainValidation, (void *) hd);
- goto nextSGEset;
- }
+ SCHEDULE_TASK(&mptscsih_dvTask);
+ } else {
+ spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
+ }
+ hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
+ }
- return SUCCESS;
-}
+ /* Trying to do DV to this target, extend timeout.
+ * Wait to issue intil flag is clear
+ */
+ if (dvStatus & MPT_SCSICFG_DV_PENDING) {
+ mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
+ issueCmd = 0;
+ }
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/*
- * mptscsih_getFreeChainBuffes - Function to get a free chain
- * from the MPT_SCSI_HOST FreeChainQ.
- * @hd: Pointer to the MPT_SCSI_HOST instance
- * @req_idx: Index of the SCSI IO request frame. (output)
- *
- * return SUCCESS or FAILED
- */
-static int
-mptscsih_getFreeChainBuffer(MPT_SCSI_HOST *hd, int *retIndex)
-{
- MPT_FRAME_HDR *chainBuf = NULL;
- unsigned long flags;
- int rc = FAILED;
- int chain_idx = MPT_HOST_NO_CHAIN;
+ /* Set the DV flags.
+ */
+ if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
+ mptscsih_set_dvflags(hd, pScsiReq);
+#endif
+ }
+ }
- //spin_lock_irqsave(&hd->FreeChainQlock, flags);
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (!Q_IS_EMPTY(&hd->FreeChainQ)) {
+ if (issueCmd) {
+ mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
+ dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%d\n",
+ hd->ioc->name, SCpnt, mf, my_idx));
+ } else {
+ ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n",
+ hd->ioc->name, SCpnt, my_idx));
+ /* Place this command on the pendingQ if possible */
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->freeQ)) {
+ buffer = hd->freeQ.head;
+ Q_DEL_ITEM(buffer);
- int offset;
+ /* Save the mf pointer
+ */
+ buffer->argp = (void *)mf;
- chainBuf = hd->FreeChainQ.head;
- Q_DEL_ITEM(&chainBuf->u.frame.linkage);
- offset = (u8 *)chainBuf - (u8 *)hd->ChainBuffer;
- chain_idx = offset / hd->ioc->req_sz;
- rc = SUCCESS;
+ /* Add to the pendingQ
+ */
+ Q_ADD_TAIL(&hd->pendingQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ } else {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ SCpnt->result = (DID_BUS_BUSY << 16);
+ SCpnt->scsi_done(SCpnt);
+ }
+ }
+ } else {
+ mptscsih_freeChainBuffers(hd, my_idx);
+ mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
+ did_errcode = 3;
+ goto did_error;
}
- //spin_unlock_irqrestore(&hd->FreeChainQlock, flags);
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
+ return 0;
- *retIndex = chain_idx;
+did_error:
+ dprintk((MYIOC_s_WARN_FMT "_qcmd did_errcode=%d (sc=%p)\n",
+ hd->ioc->name, did_errcode, SCpnt));
+ /* Just wish OS to issue a retry */
+ SCpnt->result = (DID_BUS_BUSY << 16);
+ spin_lock_irqsave(&hd->freedoneQlock, flags);
+ if (!Q_IS_EMPTY(&hd->freeQ)) {
+ buffer = hd->freeQ.head;
+ Q_DEL_ITEM(buffer);
- dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n",
- hd->ioc->name, *retIndex, chainBuf));
+ /* Set the Scsi_Cmnd pointer
+ */
+ buffer->argp = (void *)SCpnt;
- return rc;
+ /* Add to the doneQ
+ */
+ Q_ADD_TAIL(&hd->doneQ.head, buffer, MPT_DONE_Q);
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ } else {
+ spin_unlock_irqrestore(&hd->freedoneQlock, flags);
+ SCpnt->scsi_done(SCpnt);
+ }
+
+ return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#ifdef MPT_DEBUG_RESET
if ((ioc_raw_state & MPI_IOC_STATE_MASK) != MPI_IOC_STATE_OPERATIONAL) {
- printk(MYIOC_s_WARN_FMT
- "TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n",
+ printk(MYIOC_s_WARN_FMT
+ "TM Handler: IOC Not operational! state 0x%x Calling HardResetHandler\n",
hd->ioc->name, ioc_raw_state);
}
#endif
hd->abortSCpnt = SCpnt;
if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
- SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP)
+ SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP)
< 0) {
/* The TM request failed and the subsequent FW-reload failed!
}
if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
- SCpnt->target, 0, 0, NO_SLEEP)
+ SCpnt->target, 0, 0, NO_SLEEP)
< 0){
/* The TM request failed and the subsequent FW-reload failed!
* Fatal error case.
/* We are now ready to execute the task management request. */
if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
- 0, 0, 0, NO_SLEEP)
+ 0, 0, 0, NO_SLEEP)
< 0){
/* The TM request failed and the subsequent FW-reload failed!
* Fatal error case.
*/
- printk(MYIOC_s_WARN_FMT
+ printk(MYIOC_s_WARN_FMT
"Error processing TaskMgmt request (sc=%p)\n",
hd->ioc->name, SCpnt);
hd->tmPending = 0;
if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0){
status = FAILED;
} else {
- /* Make sure TM pending is cleared and TM state is set to
- * NONE.
+ /* Make sure TM pending is cleared and TM state is set to
+ * NONE.
*/
hd->tmPending = 0;
hd->tmState = TM_STATE_NONE;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
- * mptscsih_tm_pending_wait - wait for pending task management request to
+ * mptscsih_tm_pending_wait - wait for pending task management request to
* complete.
* @hd: Pointer to MPT host structure.
*
* (bottom/unused portion of) MPT request frame.
*/
ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo;
- MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt);
+ MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt);
SCHEDULE_TASK(ptaskfoo);
} else {
* (bottom/unused portion of) MPT request frame.
*/
ptaskfoo = (struct mpt_work_struct *) &mptscsih_ptaskfoo;
- MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt);
+ MPT_INIT_WORK(&mptscsih_ptaskfoo, mptscsih_taskmgmt_bh, (void *) SCpnt);
SCHEDULE_TASK(ptaskfoo);
} else {
* Called once per device the bus scan. Use it to force the queue_depth
* member to 1 if a device does not support Q tags.
*/
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52)
int
mptscsih_slave_configure(Scsi_Device *device)
{
if (!device->tagged_supported ||
!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
scsi_adjust_queue_depth(device, 0, 1);
+
+ } else if ((pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)
+ && (pTarget->inq_data[0] & 0x1f) == 0x00
+ && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)) {
+ scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
+ MPT_SCSI_CMD_PER_DEV_HIGH);
} else {
- scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
- device->host->can_queue >> 1);
+ scsi_adjust_queue_depth(device, MSG_SIMPLE_TAG,
+ MPT_SCSI_CMD_PER_DEV_LOW);
}
}
}
return 0;
}
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */
+#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52) */
void
mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList)
{
for (ii=0; ii < max; ii++) {
pTarget = hd->Targets[ii];
- if (pTarget && !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
+ if (pTarget == NULL) {
+ continue;
+ }
+ if (!(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
device->queue_depth = 1;
+ } else if ((pTarget->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)
+ && (pTarget->inq_data[0] & 0x1f) == 0x00
+ && (pTarget->minSyncFactor <= MPT_ULTRA160 || !hd->is_spi)) {
+ device->queue_depth = MPT_SCSI_CMD_PER_DEV_HIGH;
+ } else {
+ device->queue_depth = MPT_SCSI_CMD_PER_DEV_LOW;
}
+ dprintk((MYIOC_s_INFO_FMT
+ "target = %d, sync factor = %#x, queue depth = %d\n",
+ hd->ioc->name, pTarget->target_id,
+ pTarget->minSyncFactor, device->queue_depth));
}
}
}
}
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44) */
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52) */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* Private routines...
*/
-/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
-/* 19991030 -sralston
- * Return absolute SCSI data direction:
- * 1 = _DATA_OUT
- * 0 = _DIR_NONE
- * -1 = _DATA_IN
- *
- * Changed: 3-20-2002 pdelaney to use the default data
- * direction and the defines set up in the
- * 2.4 kernel series
- * 1 = _DATA_OUT changed to SCSI_DATA_WRITE (1)
- * 0 = _DIR_NONE changed to SCSI_DATA_NONE (3)
- * -1 = _DATA_IN changed to SCSI_DATA_READ (2)
- * If the direction is unknown, fall through to original code.
- *
- * Mid-layer bug fix(): sg interface generates the wrong data
- * direction in some cases. Set the direction the hard way for
- * the most common commands.
- */
-static int
-mptscsih_io_direction(Scsi_Cmnd *cmd)
-{
- switch (cmd->cmnd[0]) {
- case WRITE_6:
- case WRITE_10:
- return SCSI_DATA_WRITE;
- break;
- case READ_6:
- case READ_10:
- return SCSI_DATA_READ;
- break;
- }
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,0)
- if (cmd->sc_data_direction != SCSI_DATA_UNKNOWN)
- return cmd->sc_data_direction;
-#endif
- switch (cmd->cmnd[0]) {
- /* _DATA_OUT commands */
- case WRITE_6: case WRITE_10: case WRITE_12:
- case WRITE_LONG: case WRITE_SAME: case WRITE_BUFFER:
- case WRITE_VERIFY: case WRITE_VERIFY_12:
- case COMPARE: case COPY: case COPY_VERIFY:
- case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
- case SEARCH_EQUAL_12: case SEARCH_HIGH_12: case SEARCH_LOW_12:
- case MODE_SELECT: case MODE_SELECT_10: case LOG_SELECT:
- case SEND_DIAGNOSTIC: case CHANGE_DEFINITION: case UPDATE_BLOCK:
- case SET_WINDOW: case MEDIUM_SCAN: case SEND_VOLUME_TAG:
- case REASSIGN_BLOCKS:
- case PERSISTENT_RESERVE_OUT:
- case 0xea:
- case 0xa3:
- return SCSI_DATA_WRITE;
-
- /* No data transfer commands */
- case SEEK_6: case SEEK_10:
- case RESERVE: case RELEASE:
- case TEST_UNIT_READY:
- case START_STOP:
- case ALLOW_MEDIUM_REMOVAL:
- return SCSI_DATA_NONE;
-
- /* Conditional data transfer commands */
- case FORMAT_UNIT:
- if (cmd->cmnd[1] & 0x10) /* FmtData (data out phase)? */
- return SCSI_DATA_WRITE;
- else
- return SCSI_DATA_NONE;
-
- case VERIFY:
- if (cmd->cmnd[1] & 0x02) /* VERIFY:BYTCHK (data out phase)? */
- return SCSI_DATA_WRITE;
- else
- return SCSI_DATA_NONE;
-
- case RESERVE_10:
- if (cmd->cmnd[1] & 0x03) /* RESERVE:{LongID|Extent} (data out phase)? */
- return SCSI_DATA_WRITE;
- else
- return SCSI_DATA_NONE;
-
-#if 0
- case REZERO_UNIT: /* (or REWIND) */
- case SPACE:
- case ERASE: case ERASE_10:
- case SYNCHRONIZE_CACHE:
- case LOCK_UNLOCK_CACHE:
-#endif
-
- /* Must be data _IN! */
- default:
- return SCSI_DATA_READ;
- }
-}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* Utility function to copy sense data from the scsi_cmnd buffer
#ifdef ABORT_FIX
if (sz >= SCSI_STD_SENSE_BYTES) {
- if ((sense_data[02] == ABORTED_COMMAND) &&
+ if ((sense_data[02] == ABORTED_COMMAND) &&
(sense_data[12] == 0x47) && (sense_data[13] == 0x00)){
target->numAborts++;
if ((target->raidVolume == 0) && (target->numAborts > 5)) {
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/* Search the pendingQ for a command with specific index.
- * If found, delete and return mf pointer
+ * If found, delete and return mf pointer
* If not found, return NULL
*/
static MPT_FRAME_HDR *
dtmprintk((MYIOC_s_WARN_FMT "Post-Reset handling complete.\n",
ioc->name));
+
+
+ /* 8. Set flag to force DV and re-read IOC Page 3
+ */
+ ioc->spi_data.forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
+ ddvtprintk(("Set reload IOC Pg3 Flag\n"));
+
}
return 1; /* currently means nothing really */
case MPI_EVENT_INTEGRATED_RAID: /* 0B */
#ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- /* negoNvram set to 0 if DV enabled and to USE_NVRAM if
+ /* negoNvram set to 0 if DV enabled and to USE_NVRAM if
* if DV disabled. Need to check for target mode.
*/
hd = NULL;
reason = (le32_to_cpu(pEvReply->Data[0]) & 0x00FF0000) >> 16;
if (reason == MPI_EVENT_RAID_RC_DOMAIN_VAL_NEEDED) {
- /* New or replaced disk.
+ /* New or replaced disk.
* Set DV flag and schedule DV.
*/
pSpi = &ioc->spi_data;
physDiskNum = (le32_to_cpu(pEvReply->Data[0]) & 0xFF000000) >> 24;
+ ddvtprintk(("DV requested for phys disk id %d\n", physDiskNum));
if (pSpi->pIocPg3) {
pPDisk = pSpi->pIocPg3->PhysDisk;
numPDisk =pSpi->pIocPg3->NumPhysDisks;
pPDisk++;
numPDisk--;
}
+
+ if (numPDisk == 0) {
+ /* The physical disk that needs DV was not found
+ * in the stored IOC Page 3. The driver must reload
+ * this page. DV routine will set the NEED_DV flag for
+ * all phys disks that have DV_NOT_DONE set.
+ */
+ pSpi->forceDv = MPT_SCSICFG_NEED_DV | MPT_SCSICFG_RELOAD_IOC_PG3;
+ ddvtprintk(("phys disk %d not found. Setting reload IOC Pg3 Flag\n", physDiskNum));
+ }
}
}
}
if (ioop->cdbPtr == NULL) {
return 0;
} else if ((ioop->cdbPtr[0] == CMD_TestUnitReady) ||
- (ioop->cdbPtr[0] == CMD_ReadCapacity) ||
+ (ioop->cdbPtr[0] == CMD_ReadCapacity) ||
(ioop->cdbPtr[0] == 0x43)) {
return 0;
}
}
if (vdev && data) {
- if ((!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) ||
+ if ((!(vdev->tflags & MPT_TARGET_FLAGS_VALID_INQUIRY)) ||
((dlen > 56) && (!(vdev->tflags & MPT_TARGET_FLAGS_VALID_56)))) {
/* Copy the inquiry data - if we haven't yet.
factor = MPT_ULTRA320;
/* If RAID, never disable QAS
- * else if non RAID, do not disable
+ * else if non RAID, do not disable
* QAS if bit 1 is set
* bit 1 QAS support, non-raid only
* bit 0 IU support
#endif
/* If DV disabled (negoNvram set to USE_NVARM) or if not LUN 0, return.
- * Else set the NEED_DV flag after Read Capacity Issued (disks)
- * or Mode Sense (cdroms).
+ * Else set the NEED_DV flag after Read Capacity Issued (disks)
+ * or Mode Sense (cdroms).
*
* Tapes, initTarget will set this flag on completion of Inquiry command.
* Called only if DV_NOT_DONE flag is set
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
- * If no Target, bus reset on 1st I/O. Set the flag to
+ * If no Target, bus reset on 1st I/O. Set the flag to
* prevent any future negotiations to this device.
*/
static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id)
pData->Reserved = 0;
pData->Configuration = cpu_to_le32(configuration);
- dprintk((MYIOC_s_INFO_FMT
+ dprintk((MYIOC_s_INFO_FMT
"write SDP1: id %d pgaddr 0x%x req 0x%x config 0x%x\n",
- ioc->name, id, (id | (bus<<8)),
+ ioc->name, id, (id | (bus<<8)),
requested, configuration));
mptscsih_put_msgframe(ScsiDoneCtx, ioc->id, mf);
/* Because we have reset the IOC, no TM requests can be
* pending. So let's make sure the tmPending flag is reset.
*/
- nehprintk((KERN_WARNING MYNAM
- ": %s: mptscsih_taskmgmt_timeout\n",
+ nehprintk((KERN_WARNING MYNAM
+ ": %s: mptscsih_taskmgmt_timeout\n",
hd->ioc->name));
hd->tmPending = 0;
}
if (hd->tmPending) {
spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
return;
- } else
+ } else
hd->tmPending = 1;
spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
pReq->ActionDataWord = 0; /* Reserved for this action */
//pReq->ActionDataSGE = 0;
- mpt_add_sge((char *)&pReq->ActionDataSGE,
+ mpt_add_sge((char *)&pReq->ActionDataSGE,
MPT_SGE_FLAGS_SSIMPLE_READ | 0, (dma_addr_t) -1);
ddvprintk((MYIOC_s_INFO_FMT "RAID Volume action %x id %d\n",
if (id == hostId)
id++;
- /* Write SDP1 for all SCSI devices
+ /* Write SDP1 for all SCSI devices
* Alloc memory and set up config buffer
*/
if (hd->is_spi) {
spin_unlock_irqrestore(&dvtaskQ_lock, flags);
/* For this ioc, loop through all devices and do dv to each device.
- * When complete with this ioc, search through the ioc list, and
+ * When complete with this ioc, search through the ioc list, and
* for each scsi ioc found, do dv for all devices. Exit when no
* device needs dv.
*/
if (hd == NULL)
continue;
+ if ((ioc->spi_data.forceDv & MPT_SCSICFG_RELOAD_IOC_PG3) != 0) {
+ mpt_read_ioc_pg_3(ioc);
+ if (ioc->spi_data.pIocPg3) {
+ Ioc3PhysDisk_t *pPDisk = ioc->spi_data.pIocPg3->PhysDisk;
+ int numPDisk = ioc->spi_data.pIocPg3->NumPhysDisks;
+
+ while (numPDisk) {
+ if (ioc->spi_data.dvStatus[pPDisk->PhysDiskID] & MPT_SCSICFG_DV_NOT_DONE)
+ ioc->spi_data.dvStatus[pPDisk->PhysDiskID] |= MPT_SCSICFG_NEED_DV;
+
+ pPDisk++;
+ numPDisk--;
+ }
+ }
+ ioc->spi_data.forceDv &= ~MPT_SCSICFG_RELOAD_IOC_PG3;
+ }
+
maxid = MIN (ioc->sh->max_id, MPT_MAX_SCSI_DEVICES);
for (id = 0; id < maxid; id++) {
lun = 0;
bus = 0;
- ddvtprintk((MYIOC_s_NOTE_FMT
+ ddvtprintk((MYIOC_s_NOTE_FMT
"DV started: numIOs %d bus=%d, id %d dv @ %p\n",
ioc->name, atomic_read(&queue_depth), bus, id, &dv));
/* Skip this ID? Set cfg.hdr to force config page write
*/
if ((ioc->spi_data.nvram[id] != MPT_HOST_NVRAM_INVALID) &&
- (!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) {
+ (!(ioc->spi_data.nvram[id] & MPT_NVRAM_ID_SCAN_ENABLE))) {
ddvprintk((MYIOC_s_NOTE_FMT "DV Skipped: bus, id, lun (%d, %d, %d)\n",
ioc->name, bus, id, lun));
/* Wide - narrow - wide workaround case
*/
- if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
+ if ((rc == MPT_SCANDV_ISSUE_SENSE) && dv.max.width) {
/* Send an untagged command to reset disk Qs corrupted
* when a parity error occurs on a Request Sense.
*/
- if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
+ if ((hd->ioc->facts.FWVersion.Word >= 0x01000600) ||
((hd->ioc->facts.FWVersion.Word >= 0x01010000) &&
(hd->ioc->facts.FWVersion.Word < 0x01010B00)) ) {
rc = hd->pLocal->completion;
if (rc == MPT_SCANDV_GOOD) {
if (hd->pLocal->scsiStatus == STS_BUSY) {
- retcode = 1;
+ if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
+ retcode = 1;
+ else
+ retcode = 0;
+
goto target_done;
}
} else if (rc == MPT_SCANDV_SENSE) {
* Fujitsu: PPR U320 -> Msg Reject and Ultra2 and wide
* Resetart with a request for U160.
*/
- if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
+ if ((dv.now.factor == MPT_ULTRA320) && (sdp0_nego == MPT_ULTRA2)) {
doFallback = 1;
} else {
dv.cmd = MPT_UPDATE_MAX;
}
- } else if (rc == MPT_SCANDV_ISSUE_SENSE)
+ } else if (rc == MPT_SCANDV_ISSUE_SENSE)
doFallback = 1; /* set fallback flag */
else if ((rc == MPT_SCANDV_DID_RESET) || (rc == MPT_SCANDV_SENSE))
doFallback = 1; /* set fallback flag */
mdelay (2000);
notDone++;
} else {
- ddvprintk((MYIOC_s_INFO_FMT
+ ddvprintk((MYIOC_s_INFO_FMT
"DV: Reserved Failed.", ioc->name));
goto target_done;
}
patt = -1;
continue;
}
- }
+ }
goto target_done;
}
else
if (hd->pLocal->completion == MPT_SCANDV_GOOD)
iocmd.flags &= ~MPT_ICFLAG_RESERVED;
} else {
- printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
+ printk(MYIOC_s_INFO_FMT "DV: Release failed. id %d",
ioc->name, id);
}
}
mptscsih_dv_parms(hd, &dv, (void *)pcfg1Data);
#if 0
- /* Double writes to SDP1 can cause problems,
+ /* Double writes to SDP1 can cause problems,
* skip here since unnecessary
*/
/* Save the final negotiated settings to
case MPT_SET_MIN:
ddvprintk((MYIOC_s_NOTE_FMT "Setting Min: ",
hd->ioc->name));
- /* Set page to asynchronous and narrow
+ /* Set page to asynchronous and narrow
* Do not update now, breaks fallback routine. */
width = MPT_NARROW;
offset = 0;
case MPT_FALLBACK:
ddvprintk((MYIOC_s_NOTE_FMT
"Fallback: Start: offset %d, factor %x, width %d \n",
- hd->ioc->name, dv->now.offset,
+ hd->ioc->name, dv->now.offset,
dv->now.factor, dv->now.width));
width = dv->now.width;
offset = dv->now.offset;