Commit 17bb4650 authored by Pam Delaney's avatar Pam Delaney Committed by Christoph Hellwig

[PATCH] Fusion-MPT Update (2.03.01.01)

This upgrades the Fusion-MPT driver from 2.03.00.02 to 2.03.01.01.

Bug Fixes:
 o Added back missing queuecommand entry point define ?!
 o Added to code to break marriage of two controllers during unload
  (could cause a panic)
 o SCSI driver will de-register with base driver if no SCSI-capable
   adapters found

Minor Changes:
 o Removed errant spaces at ends of lines  (most of the changes)
 o Moved code around (and in-lined) some functions for performance reasons.
 o Modified /proc functionality to facilitate testing with 2.5
 o Added a call to synchronize_irq on unload (HP request)
 o Modified load of base to close a potential hole
 o Added code to set the FW IO coalescing depth (IBM request)
 o Changed return when mptctl driver registration fails (Kernel.org request)
 o SCSI driver detect routine calls a generic spinlock for all kernels
   (Kernel.org request)
 o Controller RAID page dynamic instead of static

Currently running a multi-disk stress test w/ 2.5.53,  this patch and driver
built-in. Verified basic reset handling is working properly.
parent 2c3808d6
......@@ -246,30 +246,12 @@ static __inline__ int __get_order(unsigned long size)
#endif
/*
* We use our new error handling code if the kernel version is 2.5.1 or newer.
* We use our new error handling code if the kernel version is 2.4.18 or newer.
*/
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,1)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,18)
#define MPT_SCSI_USE_NEW_EH
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28)
#define mptscsih_lock(iocp, flags) \
spin_lock_irqsave(&iocp->FreeQlock, flags)
#else
#define mptscsih_lock(iocp, flags) \
({ save_flags(flags); \
cli(); \
})
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28)
#define mptscsih_unlock(iocp, flags) \
spin_unlock_irqrestore(&iocp->FreeQlock, flags)
#else
#define mptscsih_unlock(iocp, flags) restore_flags(flags);
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,41)
#define mpt_work_struct work_struct
#define MPT_INIT_WORK(_task, _func, _data) INIT_WORK(_task, _func, _data)
......@@ -282,6 +264,13 @@ static __inline__ int __get_order(unsigned long size)
})
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,28)
#define mptscsih_sync_irq(_irq) synchronize_irq(_irq)
#else
#define mptscsih_sync_irq(_irq) synchronize_irq()
#endif
/*}-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
#endif /* _LINUX_COMPAT_H */
......
......@@ -49,7 +49,7 @@
* (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 $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -208,6 +208,7 @@ static int GetIoUnitPage2(MPT_ADAPTER *ioc);
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);
......@@ -576,9 +577,11 @@ mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
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) {
......@@ -599,7 +602,7 @@ mpt_base_reply(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *reply)
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;
......@@ -1470,6 +1473,12 @@ mpt_adapter_install(struct pci_dev *pdev)
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);
......@@ -1482,6 +1491,8 @@ mpt_adapter_install(struct pci_dev *pdev)
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;
......@@ -1498,12 +1509,6 @@ mpt_adapter_install(struct pci_dev *pdev)
#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.
*/
......@@ -1638,6 +1643,9 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
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");
......@@ -1714,6 +1722,10 @@ mpt_do_ioc_recovery(MPT_ADAPTER *ioc, u32 reason, int sleepFlag)
*/
if (ioc->facts.MsgVersion >= 0x0102)
mpt_findImVolumes(ioc);
/* Check, and possibly reset, the coalescing value
*/
mpt_read_ioc_pg_1(ioc);
}
GetIoUnitPage2(ioc);
......@@ -1919,6 +1931,11 @@ mpt_adapter_dispose(MPT_ADAPTER *this)
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) {
......@@ -3254,11 +3271,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, 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);
}
......@@ -3304,11 +3317,7 @@ mpt_diag_reset(MPT_ADAPTER *ioc, int ignore, int sleepFlag)
/* 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
......@@ -3631,6 +3640,9 @@ PrimeIocFifos(MPT_ADAPTER *ioc)
}
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);
......@@ -4446,6 +4458,11 @@ mpt_readScsiDevicePageHeaders(MPT_ADAPTER *ioc, int portnum)
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;
}
......@@ -4464,17 +4481,13 @@ static int
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;
......@@ -4541,6 +4554,35 @@ mpt_findImVolumes(MPT_ADAPTER *ioc)
/* No physical disks. Done.
*/
} else {
mpt_read_ioc_pg_3(ioc);
}
done_and_free:
if (pIoc2) {
pci_free_consistent(ioc->pcidev, iocpage2sz, pIoc2, ioc2_dma);
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
*/
......@@ -4555,17 +4597,17 @@ mpt_findImVolumes(MPT_ADAPTER *ioc)
cfg.dir = 0;
cfg.timeout = 0;
if (mpt_config(ioc, &cfg) != 0)
goto done_and_free;
return 0;
if (header.PageLength == 0)
goto done_and_free;
return 0;
/* Read Header good, alloc memory
*/
iocpage3sz = header.PageLength * 4;
pIoc3 = pci_alloc_consistent(ioc->pcidev, iocpage3sz, &ioc3_dma);
if (!pIoc3)
goto done_and_free;
return 0;
/* Read the Page and save the data
* into malloc'd memory.
......@@ -4579,22 +4621,101 @@ mpt_findImVolumes(MPT_ADAPTER *ioc)
ioc->spi_data.pIocPg3 = (IOCPage3_t *) mem;
}
}
}
done_and_free:
if (pIoc2) {
pci_free_consistent(ioc->pcidev, iocpage2sz, pIoc2, ioc2_dma);
pIoc2 = NULL;
}
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;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -4690,7 +4811,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
*/
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;
}
......@@ -4698,7 +4819,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
/* 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;
}
......@@ -4731,7 +4852,7 @@ mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *pCfg)
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
......@@ -4778,7 +4899,7 @@ mpt_timer_expired(unsigned long data)
{
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)
......@@ -4788,7 +4909,7 @@ mpt_timer_expired(unsigned long data)
* 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;
}
......@@ -5096,8 +5217,7 @@ procmpt_version_read(char *buf, char **start, off_t offset, int request, int *eo
*/
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);
......@@ -5774,6 +5894,7 @@ EXPORT_SYMBOL(mpt_lan_index);
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);
......@@ -5843,6 +5964,7 @@ static void
fusion_exit(void)
{
MPT_ADAPTER *this;
struct pci_dev *pdev = NULL;
dprintk((KERN_INFO MYNAM ": fusion_exit() called!\n"));
......@@ -5861,9 +5983,14 @@ fusion_exit(void)
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);
}
......
......@@ -13,7 +13,7 @@
* (mailto:sjralston1@netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
* $Id: mptbase.h,v 1.136 2002/10/21 13:51:54 pdelaney Exp $
* $Id: mptbase.h,v 1.141 2002/12/03 21:26:32 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -80,8 +80,8 @@
#define COPYRIGHT "Copyright (c) 1999-2002 " MODULEAUTHOR
#endif
#define MPT_LINUX_VERSION_COMMON "2.03.00.02"
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.03.00.02"
#define MPT_LINUX_VERSION_COMMON "2.03.01.01"
#define MPT_LINUX_PACKAGE_NAME "@(#)mptlinux-2.03.01.01"
#define WHAT_MAGIC_STRING "@" "(" "#" ")"
#define show_mptmod_ver(s,ver) \
......@@ -134,6 +134,8 @@
#define CAN_SLEEP 1
#define NO_SLEEP 0
#define MPT_COALESCING_TIMEOUT 0x10
/*
* SCSI transfer rate defines.
*/
......@@ -524,7 +526,7 @@ typedef struct _mpt_ioctl_events {
#define MPT_SCSICFG_DV_PENDING 0x04 /* DV on this physical id pending */
#define MPT_SCSICFG_DV_NOT_DONE 0x08 /* DV has not been performed */
#define MPT_SCSICFG_BLK_NEGO 0x10 /* WriteSDP1 with WDTR and SDTR disabled */
#define MPT_SCSICFG_RELOAD_IOC_PG3 0x20 /* IOC Pg 3 data is obsolete */
/* Args passed to writeSDP1: */
#define MPT_SCSICFG_USE_NVRAM 0x01 /* WriteSDP1 using NVRAM */
#define MPT_SCSICFG_ALL_IDS 0x02 /* WriteSDP1 to all IDS */
......@@ -756,6 +758,12 @@ typedef struct _mpt_sge {
#define nehprintk(x)
#endif
#if defined(MPT_DEBUG_CONFIG) || defined(MPT_DEBUG)
#define dcprintk(x) printk x
#else
#define dcprintk(x)
#endif
#define MPT_INDEX_2_MFPTR(ioc,idx) \
(MPT_FRAME_HDR*)( (u8*)(ioc)->req_frames + (ioc)->req_sz * (idx) )
......@@ -1009,6 +1017,7 @@ extern int mpt_HardResetHandler(MPT_ADAPTER *ioc, int sleepFlag);
extern int mpt_config(MPT_ADAPTER *ioc, CONFIGPARMS *cfg);
extern void *mpt_alloc_fw_memory(MPT_ADAPTER *ioc, int size, int *frags, int *alloc_sz);
extern void mpt_free_fw_memory(MPT_ADAPTER *ioc, fw_image_t **alt_img);
extern int mpt_read_ioc_pg_3(MPT_ADAPTER *ioc);
/*
* Public data decl's...
......
......@@ -34,7 +34,7 @@
* (mailto:sjralston1@netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
* $Id: mptctl.c,v 1.61 2002/10/17 20:15:57 pdelaney Exp $
* $Id: mptctl.c,v 1.63 2002/12/03 21:26:33 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -2911,9 +2911,9 @@ int __init mptctl_init(void)
#endif /*} sparc */
/* Register this device */
if (misc_register(&mptctl_miscdev) == -1) {
err = misc_register(&mptctl_miscdev);
if (err < 0) {
printk(KERN_ERR MYNAM ": Can't register misc device [minor=%d].\n", MPT_MINOR);
err = -EBUSY;
goto out_fail;
}
printk(KERN_INFO MYNAM ": Registered with Fusion MPT base driver\n");
......
......@@ -20,7 +20,7 @@
* (mailto:sjralston1@netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
* $Id: mptctl.h,v 1.12 2002/10/17 20:15:58 pdelaney Exp $
* $Id: mptctl.h,v 1.13 2002/12/03 21:26:33 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......
......@@ -26,7 +26,7 @@
* (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 $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -159,11 +159,9 @@ typedef struct _dv_parameters {
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);
......@@ -272,6 +270,436 @@ static int scandv_wait_done = 1;
static struct mptscsih_driver_setup
driver_setup = MPTSCSIH_DRIVER_SETUP;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* Private inline 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 inline 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;
/* Must be data _IN! */
default:
return SCSI_DATA_READ;
}
} /* mptscsih_io_direction() */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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;
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);
} else {
SGESimple32_t *pSge = (SGESimple32_t *) pAddr;
pSge->FlagsLength = cpu_to_le32(flagslength);
pSge->Address = cpu_to_le32(dma_addr);
}
} /* mptscsih_add_sge() */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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;
pChain->Length = cpu_to_le16(length);
pChain->Flags = MPI_SGE_FLAGS_CHAIN_ELEMENT | mpt_addr_size();
pChain->NextChainOffset = next;
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() */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* 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;
spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
if (!Q_IS_EMPTY(&hd->FreeChainQ)) {
int offset;
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);
*retIndex = chain_idx;
dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n",
hd->ioc->name, *retIndex, chainBuf));
return rc;
} /* mptscsih_getFreeChainBuffer() */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* 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
......@@ -294,7 +722,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
MPT_SCSI_HOST *hd;
SCSIIORequest_t *pScsiReq;
SCSIIOReply_t *pScsiReply;
#ifndef MPT_SCSI_USE_NEW_EH
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
unsigned long flags;
#endif
u16 req_idx;
......@@ -305,7 +733,6 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
(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;
}
......@@ -506,7 +933,7 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
;
} 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.
......@@ -625,7 +1052,9 @@ mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
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 */
......@@ -894,7 +1323,7 @@ mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
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
......@@ -1175,6 +1604,7 @@ mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIOReque
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
static int BeenHereDoneThat = 0;
static char *info_kbuf = NULL;
/* SCSI host fops start here... */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
......@@ -1263,9 +1693,10 @@ mptscsih_detect(Scsi_Host_Template *tpnt)
#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;
......@@ -1340,7 +1771,7 @@ mptscsih_detect(Scsi_Host_Template *tpnt)
*/
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;
......@@ -1503,12 +1934,25 @@ mptscsih_detect(Scsi_Host_Template *tpnt)
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
......@@ -1708,724 +2152,742 @@ mptscsih_halt(struct notifier_block *nb, ulong event, void *buf)
/* Flush the cache of this adapter
*/
if (ioc->sh) {
hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
if (hd) {
mptscsih_synchronize_cache(hd, 0);
}
}
}
unregister_reboot_notifier(&mptscsih_notifier);
return NOTIFY_OK;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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));
hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
if (hd) {
mptscsih_synchronize_cache(hd, 0);
}
}
}
mpt_put_msg_frame(context, id, mf);
return;
unregister_reboot_notifier(&mptscsih_notifier);
return NOTIFY_OK;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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) */
info.buffer = pbuf;
info.length = len;
info.offset = offset;
info.pos = 0;
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 {
datalen = 0;
scsidir = MPI_SCSIIO_CONTROL_NODATATRANSFER;
printk("failed\n" );
}
/* 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;
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;
if (dvStatus || hd->ioc->spi_data.forceDv) {
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;
/* 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;
}
uc->target = uc->cmd = uc->lun = uc->data = 0;
#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 ((len > 0) && (ptr[len -1] == '\n'))
--len;
SCHEDULE_TASK(&mptscsih_dvTask);
} else {
spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
}
hd->ioc->spi_data.forceDv = 0;
}
if ((arg_len = is_keyword(ptr, len, "getspeed")) != 0)
uc->cmd = UC_GET_SPEED;
else
arg_len = 0;
/* 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;
dprintk(("user_command: arg_len=%d, cmd=%ld\n", arg_len, uc->cmd));
if (!arg_len)
return -EINVAL;
ptr += arg_len;
len -= arg_len;
switch(uc->cmd) {
case UC_GET_SPEED:
SKIP_SPACES(1);
GET_INT_ARG(target);
uc->target = target;
break;
}
/* Set the DV flags.
dprintk(("user_command: target=%ld len=%d\n", uc->target, len));
if (len)
return -EINVAL;
else {
/* process this command ...
*/
if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
mptscsih_set_dvflags(hd, pScsiReq);
#endif
}
mptscsih_exec_user_cmd(ioc, uc);
}
/* Not yet implemented */
return length;
}
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);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/**
* 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;
/* Save the mf pointer
*/
buffer->argp = (void *)mf;
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));
/* 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);
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
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.
*/
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);
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);
}
/* We hide it here for later unmap. */
my_priv = (scPrivate *) &SCpnt->SCp;
my_priv->p1 = (void *)(ulong) buf_dma_addr;
if (dest) {
sz = MIN (SCSI_STD_SENSE_BYTES, SCpnt->request_bufflen);
memcpy(dest, pTarget->sense, sz);
dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n",
hd->ioc->name, SCpnt, SCpnt->request_bufflen));
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
SCpnt->resid = SCpnt->request_bufflen - sz;
#endif
SCpnt->result = 0;
SCpnt->scsi_done(SCpnt);
mpt_add_sge((char *) &pReq->SGL,
0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen,
buf_dma_addr);
//sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
return SUCCESS;
return 0;
}
}
}
#endif
/* Handle the SG case.
if (hd->resetPending) {
/* Prevent new commands from being issued
* while reloading the FW.
*/
sg = (struct scatterlist *) SCpnt->request_buffer;
sg_done = 0;
sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION);
chainSge = NULL;
did_errcode = 1;
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)
/*
* 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;
}
nextSGEset:
numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) );
numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots;
pScsiReq = (SCSIIORequest_t *) mf;
sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir;
my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_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
*/
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;
}
ADD_INDEX_LOG(my_idx);
v2 = sg_dma_address(sg);
mpt_add_sge(psge, sgflags | thisxfer, v2);
/*
* The scsi layer should be handling this stuff
* (In 2.3.x it does -DaveM)
*/
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.
/* Default to untagged. Once a target structure has been allocated,
* use the Inquiry data to determine if device supports tagged.
*/
sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT |
MPT_SGE_FLAGS_END_OF_BUFFER |
MPT_SGE_FLAGS_END_OF_LIST;
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;
}
else
qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
}
scsictl = scsidir | qtag;
/* Add last SGE and set termination flags.
* Note: Last SGE may have a length of 0 - which should be ok.
/* Use the above information to set up the message frame
*/
thisxfer = sg_dma_len(sg);
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);
v2 = sg_dma_address(sg);
mpt_add_sge(psge, sgflags | thisxfer, v2);
/*
sg++;
psge += (sizeof(u32) + sizeof(dma_addr_t));
* Write SCSI CDB into the message
* Should write from cmd_len up to 16, but skip for performance reasons.
*/
sgeOffset += (sizeof(u32) + sizeof(dma_addr_t));
sg_done++;
cmd_len = SCpnt->cmd_len;
for (ii=0; ii < cmd_len; ii++)
pScsiReq->CDB[ii] = SCpnt->cmnd[ii];
if (chainSge) {
/* The current buffer is a chain buffer,
* but there is not another one.
* Update the chain element
* Offset and Length fields.
/* DataLength */
pScsiReq->DataLength = cpu_to_le32(datalen);
/* SenseBuffer low address */
pScsiReq->SenseBufferLowAddr = cpu_to_le32(hd->ioc->sense_buf_low_dma
+ (my_idx * MPT_SENSE_BUFFER_ALLOC));
/* Now add the SG list
* Always have a SGE even if null length.
*/
mpt_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off);
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 {
/* The current buffer is the original MF
* and there is no Chain buffer.
*/
pReq->ChainOffset = 0;
/* Add a 32 or 64 bit SGE */
rc = mptscsih_AddSGE(hd, SCpnt, pScsiReq, my_idx);
}
} 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.
if (rc == SUCCESS) {
hd->ScsiLookup[my_idx] = SCpnt;
SCpnt->host_scribble = NULL;
#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++;
dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n",
hd->ioc->name, sg_done));
if (dropCounter == DROP_THIS_CMD) {
dropCounter = 0;
/* 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 global is set, then we are already
* doing something - so keep issuing commands.
*/
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 (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;
}
}
}
#endif
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);
/* SCSI specific processing */
issueCmd = 1;
if (hd->is_spi) {
int dvStatus = hd->ioc->spi_data.dvStatus[target];
if (dvStatus || hd->ioc->spi_data.forceDv) {
/* 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;
}
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;
#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);
/* Update the tracking arrays.
* If chainSge == NULL, update ReqToChain, else ChainToChain
*/
if (chainSge) {
hd->ChainToChain[chain_idx] = newIndex;
SCHEDULE_TASK(&mptscsih_dvTask);
} else {
hd->ReqToChain[req_idx] = newIndex;
spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
}
hd->ioc->spi_data.forceDv &= ~MPT_SCSICFG_NEED_DV;
}
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.
/* Trying to do DV to this target, extend timeout.
* Wait to issue intil flag is clear
*/
chainSge = (char *) psge;
dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)",
psge, req_idx));
if (dvStatus & MPT_SCSICFG_DV_PENDING) {
mod_timer(&SCpnt->eh_timeout, jiffies + 40 * HZ);
issueCmd = 0;
}
/* Start the SGE for the next buffer
/* Set the DV flags.
*/
psge = (char *) (hd->ChainBuffer + chain_dma_off);
sgeOffset = 0;
sg_done = 0;
if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
mptscsih_set_dvflags(hd, pScsiReq);
#endif
}
}
dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n",
psge, chain_idx));
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);
/* Start the SGE for the next buffer
/* Save the mf pointer
*/
buffer->argp = (void *)mf;
goto nextSGEset;
/* 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;
}
return SUCCESS;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
* 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;
return 0;
//spin_lock_irqsave(&hd->FreeChainQlock, flags);
spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
if (!Q_IS_EMPTY(&hd->FreeChainQ)) {
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);
int offset;
/* Set the Scsi_Cmnd pointer
*/
buffer->argp = (void *)SCpnt;
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 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);
}
//spin_unlock_irqrestore(&hd->FreeChainQlock, flags);
spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
*retIndex = chain_idx;
dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%p\n",
hd->ioc->name, *retIndex, chainBuf));
return rc;
return 0;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
......@@ -3599,7 +4061,7 @@ mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
* 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)
{
......@@ -3614,15 +4076,21 @@ 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);
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)
{
......@@ -3648,113 +4116,32 @@ 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
......@@ -4126,6 +4513,13 @@ mptscsih_ioc_reset(MPT_ADAPTER *ioc, int reset_phase)
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 */
......@@ -4193,6 +4587,7 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
*/
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;
......@@ -4207,6 +4602,16 @@ mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply)
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));
}
}
}
}
......@@ -6128,6 +6533,23 @@ mptscsih_domainValidation(void *arg)
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++) {
......@@ -6535,7 +6957,11 @@ mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int id)
rc = hd->pLocal->completion;
if (rc == MPT_SCANDV_GOOD) {
if (hd->pLocal->scsiStatus == STS_BUSY) {
if ((iocmd.flags & MPT_ICFLAG_TAGGED_CMD) == 0)
retcode = 1;
else
retcode = 0;
goto target_done;
}
} else if (rc == MPT_SCANDV_SENSE) {
......
......@@ -20,7 +20,7 @@
* (mailto:netscape.net)
* (mailto:Pam.Delaney@lsil.com)
*
* $Id: mptscsih.h,v 1.20 2002/10/17 20:16:00 pdelaney Exp $
* $Id: mptscsih.h,v 1.21 2002/12/03 21:26:35 pdelaney Exp $
*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -73,8 +73,15 @@
* Try to keep these at 2^N-1
*/
#define MPT_FC_CAN_QUEUE 63
//#define MPT_SCSI_CAN_QUEUE 31
#define MPT_SCSI_CAN_QUEUE MPT_FC_CAN_QUEUE
#if defined MPT_SCSI_USE_NEW_EH
#define MPT_SCSI_CAN_QUEUE 127
#else
#define MPT_SCSI_CAN_QUEUE 63
#endif
#define MPT_SCSI_CMD_PER_DEV_HIGH 31
#define MPT_SCSI_CMD_PER_DEV_LOW 7
#define MPT_SCSI_CMD_PER_LUN 7
#define MPT_SCSI_MAX_SECTORS 8192
......@@ -206,11 +213,16 @@ struct mptscsih_driver_setup
#define x_scsi_dev_reset mptscsih_dev_reset
#define x_scsi_host_reset mptscsih_host_reset
#define x_scsi_bios_param mptscsih_bios_param
#define x_scsi_slave_configure mptscsih_slave_configure
#define x_scsi_taskmgmt_bh mptscsih_taskmgmt_bh
#define x_scsi_old_abort mptscsih_old_abort
#define x_scsi_old_reset mptscsih_old_reset
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52)
#define x_scsi_slave_configure mptscsih_slave_configure
#else
#define x_scsi_select_queue_depths mptscsih_select_queue_depths
#endif
#define x_scsi_proc_info mptscsih_proc_info
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
......@@ -237,8 +249,14 @@ extern int x_scsi_bios_param(Disk *, struct block_device *, int *);
#else
extern int x_scsi_bios_param(Disk *, kdev_t, int *);
#endif
extern int x_scsi_slave_configure(Scsi_Device *);
extern void x_scsi_taskmgmt_bh(void *);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52)
extern int x_scsi_slave_configure(Scsi_Device *);
#else
extern void x_scsi_select_queue_depths(struct Scsi_Host *, Scsi_Device *);
#endif
extern int x_scsi_proc_info(char *, char **, off_t, int, int, int);
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
#define PROC_SCSI_DECL
......@@ -248,14 +266,19 @@ extern void x_scsi_taskmgmt_bh(void *);
#ifdef MPT_SCSI_USE_NEW_EH
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,44)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,52)
#define MPT_SCSIHOST { \
PROC_SCSI_DECL \
.proc_info = x_scsi_proc_info, \
.name = "MPT SCSI Host", \
.detect = x_scsi_detect, \
.release = x_scsi_release, \
.info = x_scsi_info, \
.command = NULL, \
.queuecommand = x_scsi_queuecommand, \
.slave_configure = x_scsi_slave_configure, \
.eh_strategy_handler = NULL, \
.eh_abort_handler = x_scsi_abort, \
.eh_device_reset_handler = x_scsi_dev_reset, \
.eh_bus_reset_handler = x_scsi_bus_reset, \
......@@ -275,6 +298,7 @@ extern void x_scsi_taskmgmt_bh(void *);
#define MPT_SCSIHOST { \
.next = NULL, \
PROC_SCSI_DECL \
.proc_info = x_scsi_proc_info, \
.name = "MPT SCSI Host", \
.detect = x_scsi_detect, \
.release = x_scsi_release, \
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment