mptscsih.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:201k
- /*
- * linux/drivers/message/fusion/mptscsih.c
- * High performance SCSI / Fibre Channel SCSI Host device driver.
- * For use with PCI chip/adapter(s):
- * LSIFC9xx/LSI409xx Fibre Channel
- * running LSI Logic Fusion MPT (Message Passing Technology) firmware.
- *
- * Credits:
- * This driver would not exist if not for Alan Cox's development
- * of the linux i2o driver.
- *
- * A special thanks to Pamela Delaney (LSI Logic) for tons of work
- * and countless enhancements while adding support for the 1030
- * chip family. Pam has been instrumental in the development of
- * of the 2.xx.xx series fusion drivers, and her contributions are
- * far too numerous to hope to list in one place.
- *
- * A huge debt of gratitude is owed to David S. Miller (DaveM)
- * for fixing much of the stupid and broken stuff in the early
- * driver while porting to sparc64 platform. THANK YOU!
- *
- * (see mptbase.c)
- *
- * Copyright (c) 1999-2002 LSI Logic Corporation
- * Original author: Steven J. Ralston
- * (mailto:sjralston1@netscape.net)
- * (mailto:Pam.Delaney@lsil.com)
- *
- * $Id: mptscsih.c,v 1.101 2002/09/05 22:30:11 pdelaney Exp $
- */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; version 2 of the License.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- NO WARRANTY
- THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT
- LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT,
- MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is
- solely responsible for determining the appropriateness of using and
- distributing the Program and assumes all risks associated with its
- exercise of rights under this Agreement, including but not limited to
- the risks and costs of program errors, damage to or loss of data,
- programs or equipment, and unavailability or interruption of operations.
- DISCLAIMER OF LIABILITY
- NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY
- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND
- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
- USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED
- HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- #include <linux/module.h>
- #include <linux/version.h>
- #include <linux/kernel.h>
- #include <linux/init.h>
- #include <linux/errno.h>
- #include <linux/kdev_t.h>
- #include <linux/blkdev.h>
- #include <linux/blk.h> /* for io_request_lock (spinlock) decl */
- #include <linux/delay.h> /* for mdelay */
- #include <linux/interrupt.h> /* needed for in_interrupt() proto */
- #include <linux/reboot.h> /* notifier code */
- #include "../../scsi/scsi.h"
- #include "../../scsi/hosts.h"
- #include "../../scsi/sd.h"
- #include "mptbase.h"
- #include "mptscsih.h"
- #include "isense.h"
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- #define my_NAME "Fusion MPT SCSI Host driver"
- #define my_VERSION MPT_LINUX_VERSION_COMMON
- #define MYNAM "mptscsih"
- MODULE_AUTHOR(MODULEAUTHOR);
- MODULE_DESCRIPTION(my_NAME);
- MODULE_LICENSE("GPL");
- /* Set string for command line args from insmod */
- #ifdef MODULE
- char *mptscsih = 0;
- MODULE_PARM(mptscsih, "s");
- #endif
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- typedef struct _BIG_SENSE_BUF {
- u8 data[MPT_SENSE_BUFFER_ALLOC];
- } BIG_SENSE_BUF;
- #define MPT_SCANDV_GOOD (0x00000000) /* must be 0 */
- #define MPT_SCANDV_DID_RESET (0x00000001)
- #define MPT_SCANDV_SENSE (0x00000002)
- #define MPT_SCANDV_SOME_ERROR (0x00000004)
- #define MPT_SCANDV_SELECTION_TIMEOUT (0x00000008)
- #define MPT_SCANDV_MAX_RETRIES (10)
- #define MPT_ICFLAG_BUF_CAP 0x01 /* ReadBuffer Read Capacity format */
- #define MPT_ICFLAG_ECHO 0x02 /* ReadBuffer Echo buffer format */
- #define MPT_ICFLAG_PHYS_DISK 0x04 /* Any SCSI IO but do Phys Disk Format */
- #define MPT_ICFLAG_TAGGED_CMD 0x08 /* Do tagged IO */
- #define MPT_ICFLAG_DID_RESET 0x20 /* Bus Reset occured with this command */
- #define MPT_ICFLAG_RESERVED 0x40 /* Reserved has been issued */
- typedef struct _internal_cmd {
- char *data; /* data pointer */
- dma_addr_t data_dma; /* data dma address */
- int size; /* transfer size */
- u8 cmd; /* SCSI Op Code */
- u8 bus; /* bus number */
- u8 id; /* SCSI ID (virtual) */
- u8 lun;
- u8 flags; /* Bit Field - See above */
- u8 physDiskNum; /* Phys disk number, -1 else */
- u8 rsvd2;
- u8 rsvd;
- } INTERNAL_CMD;
- typedef struct _negoparms {
- u8 width;
- u8 offset;
- u8 factor;
- u8 flags;
- } NEGOPARMS;
- typedef struct _dv_parameters {
- NEGOPARMS max;
- NEGOPARMS now;
- u8 cmd;
- u8 id;
- u16 pad1;
- } DVPARAMETERS;
- /*
- * Other private/forward protos...
- */
- 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);
- #ifndef MPT_SCSI_USE_NEW_EH
- static void search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd);
- #else
- static int mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd);
- #endif
- static u32 SCPNT_TO_LOOKUP_IDX(Scsi_Cmnd *sc);
- static MPT_FRAME_HDR *mptscsih_search_pendingQ(MPT_SCSI_HOST *hd, int scpnt_idx);
- static void post_pendingQ_commands(MPT_SCSI_HOST *hd);
- static int mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
- static int mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag);
- static int mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
- static int mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);
- static VirtDevice *mptscsih_initTarget(MPT_SCSI_HOST *hd, int bus_id, int target_id, u8 lun, char *data, int dlen);
- void mptscsih_setTargetNegoParms(MPT_SCSI_HOST *hd, VirtDevice *target, char byte56);
- static void clear_sense_flag(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
- static void mptscsih_set_dvflags(MPT_SCSI_HOST *hd, SCSIIORequest_t *pReq);
- static void mptscsih_setDevicePage1Flags (u8 width, u8 factor, u8 offset, int *requestedPtr, int *configurationPtr, u8 flags);
- static void mptscsih_no_negotiate(MPT_SCSI_HOST *hd, int target_id);
- static int mptscsih_writeSDP1(MPT_SCSI_HOST *hd, int portnum, int target, int flags);
- static int mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
- static void mptscsih_timer_expired(unsigned long data);
- static void mptscsih_taskmgmt_timeout(unsigned long data);
- static int mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
- static int mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, int portnum);
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- static int mptscsih_do_raid(MPT_SCSI_HOST *hd, u8 action, INTERNAL_CMD *io);
- static void mptscsih_domainValidation(void *hd);
- static int mptscsih_is_phys_disk(MPT_ADAPTER *ioc, int id);
- static void mptscsih_qas_check(MPT_SCSI_HOST *hd);
- static void mptscsih_doDv(MPT_SCSI_HOST *hd, int portnum, int target);
- static void mptscsih_dv_parms(MPT_SCSI_HOST *hd, DVPARAMETERS *dv,void *pPage);
- static void mptscsih_fillbuf(char *buffer, int size, int index, int width);
- #endif
- static int mptscsih_setup(char *str);
- static int mptscsih_halt(struct notifier_block *nb, ulong event, void *buf);
- /*
- * Reboot Notification
- */
- static struct notifier_block mptscsih_notifier = {
- mptscsih_halt, NULL, 0
- };
- /*
- * Private data...
- */
- static int mpt_scsi_hosts = 0;
- static atomic_t queue_depth;
- static int ScsiDoneCtx = -1;
- static int ScsiTaskCtx = -1;
- static int ScsiScanDvCtx = -1; /* Used only for bus scan and dv */
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,28)
- static struct proc_dir_entry proc_mpt_scsihost =
- {
- .low_ino = PROC_SCSI_MPT,
- .namelen = 8,
- .name = "mptscsih",
- .mode = S_IFDIR | S_IRUGO | S_IXUGO,
- .nlink = 2,
- };
- #endif
- #define SNS_LEN(scp) sizeof((scp)->sense_buffer)
- #ifndef MPT_SCSI_USE_NEW_EH
- /*
- * Stuff to handle single-threading SCSI TaskMgmt
- * (abort/reset) requests...
- */
- static spinlock_t mytaskQ_lock = SPIN_LOCK_UNLOCKED;
- static int mytaskQ_bh_active = 0;
- static struct tq_struct mptscsih_ptaskfoo;
- static atomic_t mpt_taskQdepth;
- #endif
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- /*
- * Domain Validation task structure
- */
- static spinlock_t dvtaskQ_lock = SPIN_LOCK_UNLOCKED;
- static int dvtaskQ_active = 0;
- static int dvtaskQ_release = 0;
- static struct tq_struct mptscsih_dvTask;
- #endif
- /*
- * Wait Queue setup
- */
- static DECLARE_WAIT_QUEUE_HEAD (scandv_waitq);
- static int scandv_wait_done = 1;
- /* Driver default setup
- */
- static struct mptscsih_driver_setup
- driver_setup = MPTSCSIH_DRIVER_SETUP;
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * 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;
- #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;
- }
- 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;
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- if (hd->is_spi) {
- u32 qtag = le32_to_cpu(pScsiReq->Control);
- if (qtag & MPI_SCSIIO_CONTROL_UNTAGGED)
- hd->ioc->spi_data.iocntr[sc->target]--;
- }
- #endif
- 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);
- }
- }
- clear_sense_flag(hd, pScsiReq);
- } 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=%pn",
- ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1],
- mf, mr, sc));
- dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh"
- ", SCSIStatus=%02xh, IOCLogInfo=%08xhn",
- 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=%08xhn",
- 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;
- 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;
- clear_sense_flag(hd, pScsiReq);
- if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- /* Have already saved the status and sense data
- */
- ;
- } else if (pScsiReply->SCSIState & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) {
- /* What to do?
- */
- sc->result = DID_SOFT_ERROR << 16;
- }
- else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
- /* Not real sure here either... */
- sc->result = DID_RESET << 16;
- }
- /* Give report and update residual count.
- */
- xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
- dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}n",
- sc->underflow));
- dprintk((KERN_NOTICE " ActBytesXferd=%02xhn", xfer_cnt));
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- sc->resid = sc->request_bufflen - xfer_cnt;
- dprintk((KERN_NOTICE " SET sc->resid=%02xhn", sc->resid));
- #endif
- /* Report Queue Full
- */
- if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
- mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
- /* If regular Inquiry cmd and some data was transferred,
- * save inquiry data
- */
- if ( pScsiReq->CDB[0] == INQUIRY
- && !(pScsiReq->CDB[1] & 0x3)
- && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
- ) {
- mptscsih_initTarget(hd,
- hd->port,
- sc->target,
- pScsiReq->LUN[1],
- sc->buffer,
- xfer_cnt);
- }
- break;
- case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */
- case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
- sc->result = (DID_OK << 16) | pScsiReply->SCSIStatus;
- clear_sense_flag(hd, pScsiReq);
- if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) {
- /*
- * If running agains 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.
- */
- if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL)
- mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
- #ifndef MPT_SCSI_USE_NEW_EH
- /* ADDED 20011120 -sralston
- * Scsi mid-layer (old_eh) doesn't seem to like it
- * when RAID returns SCSIStatus=02 (CHECK CONDITION),
- * SenseKey=01 (RECOVERED ERROR), ASC/ASCQ=95/01.
- * Seems to be * treating this as a IO error:-(
- *
- * So just lie about it altogether here.
- *
- * NOTE: It still gets reported to syslog via
- * mpt_ScsiHost_ErrorReport from copy_sense_data
- * call far above.
- */
- if ( pScsiReply->SCSIStatus == STS_CHECK_CONDITION
- && SD_Sense_Key(sc->sense_buffer) == SK_RECOVERED_ERROR
- ) {
- sc->result = 0;
- }
- #endif
- }
- else if (pScsiReply->SCSIState &
- (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
- ) {
- /*
- * What to do?
- */
- sc->result = DID_SOFT_ERROR << 16;
- }
- else if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
- /* Not real sure here either... */
- sc->result = DID_RESET << 16;
- }
- else if (pScsiReply->SCSIState & MPI_SCSI_STATE_QUEUE_TAG_REJECTED) {
- /* Device Inq. data indicates that it supports
- * QTags, but rejects QTag messages.
- * This command completed OK.
- *
- * Not real sure here either so do nothing... */
- }
- if (sc->result == MPI_SCSI_STATUS_TASK_SET_FULL)
- mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
- /* Add handling of:
- * Reservation Conflict, Busy,
- * Command Terminated, CHECK
- */
- /* If regular Inquiry cmd - save inquiry data
- */
- xfer_cnt = le32_to_cpu(pScsiReply->TransferCount);
- if ( sc->result == (DID_OK << 16)
- && pScsiReq->CDB[0] == INQUIRY
- && !(pScsiReq->CDB[1] & 0x3)
- && xfer_cnt >= SCSI_STD_INQUIRY_BYTES
- ) {
- mptscsih_initTarget(hd,
- hd->port,
- sc->target,
- pScsiReq->LUN[1],
- sc->buffer,
- xfer_cnt);
- }
- break;
- case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: /* 0x0047 */
- if (pScsiReply->SCSIState & MPI_SCSI_STATE_TERMINATED) {
- /* Not real sure here either... */
- sc->result = DID_RESET << 16;
- } else
- sc->result = DID_SOFT_ERROR << 16;
- break;
- case MPI_IOCSTATUS_INVALID_FUNCTION: /* 0x0001 */
- case MPI_IOCSTATUS_INVALID_SGL: /* 0x0003 */
- case MPI_IOCSTATUS_INTERNAL_ERROR: /* 0x0004 */
- case MPI_IOCSTATUS_RESERVED: /* 0x0005 */
- case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */
- case MPI_IOCSTATUS_INVALID_FIELD: /* 0x0007 */
- case MPI_IOCSTATUS_INVALID_STATE: /* 0x0008 */
- case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
- case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: /* 0x0046 */
- case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: /* 0x004A */
- default:
- /*
- * What to do?
- */
- sc->result = DID_SOFT_ERROR << 16;
- break;
- } /* switch(status) */
- dprintk((KERN_NOTICE " sc->result set to %08xhn", sc->result));
- } /* end of address reply case */
- /* Unmap the DMA buffers, if any. */
- if (sc->use_sg) {
- pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
- sc->use_sg, scsi_to_pci_dma_dir(sc->sc_data_direction));
- } else if (sc->request_bufflen) {
- scPrivate *my_priv;
- my_priv = (scPrivate *) &sc->SCp;
- pci_unmap_single(ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
- sc->request_bufflen,
- scsi_to_pci_dma_dir(sc->sc_data_direction));
- }
- hd->ScsiLookup[req_idx] = NULL;
- sc->host_scribble = NULL; /* CHECKME! - Do we need to clear this??? */
- MPT_HOST_LOCK(flags);
- sc->scsi_done(sc); /* Issue the command callback */
- MPT_HOST_UNLOCK(flags);
- /* Free Chain buffers */
- mptscsih_freeChainBuffers(hd, req_idx);
- return 1;
- }
- #ifndef MPT_SCSI_USE_NEW_EH /* { */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * search_taskQ - Search SCSI task mgmt request queue for specific
- * request type.
- * @remove: (Boolean) Should request be removed if found?
- * @sc: Pointer to Scsi_Cmnd structure
- * @task_type: Task type to search for
- *
- * Returns pointer to MPT request frame if found, or %NULL if request
- * was not found.
- */
- static MPT_FRAME_HDR *
- search_taskQ(int remove, Scsi_Cmnd *sc, MPT_SCSI_HOST *hd, u8 task_type)
- {
- MPT_FRAME_HDR *mf = NULL;
- unsigned long flags;
- int count = 0;
- int list_sz;
- dprintk((KERN_INFO MYNAM ": search_taskQ(%d,sc=%p,%d) calledn",
- remove, sc, task_type));
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- list_sz = hd->taskQcnt;
- if (! Q_IS_EMPTY(&hd->taskQ)) {
- mf = hd->taskQ.head;
- do {
- count++;
- if (mf->u.frame.linkage.argp1 == sc &&
- mf->u.frame.linkage.arg1 == task_type) {
- if (remove) {
- Q_DEL_ITEM(&mf->u.frame.linkage);
- hd->taskQcnt--;
- atomic_dec(&mpt_taskQdepth);
- /* Don't save mf into nextmf because
- * exit after command has been deleted.
- */
- /* Place the MF back on the FreeQ */
- Q_ADD_TAIL(&hd->ioc->FreeQ,
- &mf->u.frame.linkage,
- MPT_FRAME_HDR);
- #ifdef MFCNT
- hd->ioc->mfcnt--;
- #endif
- }
- break;
- }
- } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ);
- if (mf == (MPT_FRAME_HDR*)&hd->taskQ) {
- mf = NULL;
- }
- }
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- if (list_sz) {
- dprintk((KERN_INFO " Results=%p (%sFOUND%s)!n",
- mf,
- mf ? "" : "NOT_",
- (mf && remove) ? "+REMOVED" : "" ));
- dprintk((KERN_INFO " (searched thru %d of %d items on taskQ)n",
- count,
- list_sz ));
- }
- return mf;
- }
- /*
- * clean_taskQ - Clean the SCSI task mgmt request for
- * this SCSI host instance.
- * @hd: MPT_SCSI_HOST pointer
- *
- * Returns: None.
- */
- static void
- clean_taskQ(MPT_SCSI_HOST *hd)
- {
- MPT_FRAME_HDR *mf = NULL;
- MPT_FRAME_HDR *nextmf = NULL;
- MPT_ADAPTER *ioc = hd->ioc;
- unsigned long flags;
- dprintk((KERN_INFO MYNAM ": clean_taskQ calledn"));
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- if (! Q_IS_EMPTY(&hd->taskQ)) {
- mf = hd->taskQ.head;
- do {
- Q_DEL_ITEM(&mf->u.frame.linkage);
- hd->taskQcnt--;
- atomic_dec(&mpt_taskQdepth);
- nextmf = mf->u.frame.linkage.forw;
- /* Place the MF back on the FreeQ */
- Q_ADD_TAIL(&ioc->FreeQ, &mf->u.frame.linkage,
- MPT_FRAME_HDR);
- #ifdef MFCNT
- hd->ioc->mfcnt--;
- #endif
- } while ((mf = nextmf) != (MPT_FRAME_HDR*)&hd->taskQ);
- }
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- return;
- }
- /*
- * search_taskQ_for_cmd - Search the SCSI task mgmt request queue for
- * the specified command. If found, delete
- * @hd: MPT_SCSI_HOST pointer
- *
- * Returns: None.
- */
- static void
- search_taskQ_for_cmd(Scsi_Cmnd *sc, MPT_SCSI_HOST *hd)
- {
- MPT_FRAME_HDR *mf = NULL;
- unsigned long flags;
- int count = 0;
- dprintk((KERN_INFO MYNAM ": search_taskQ_for_cmd(sc=%p) calledn", sc));
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (! Q_IS_EMPTY(&hd->taskQ)) {
- mf = hd->taskQ.head;
- do {
- count++;
- if (mf->u.frame.linkage.argp1 == sc) {
- Q_DEL_ITEM(&mf->u.frame.linkage);
- hd->taskQcnt--;
- atomic_dec(&mpt_taskQdepth);
- dprintk((KERN_INFO MYNAM
- ": Cmd %p found! Deleting.n", sc));
- /* Don't save mf into nextmf because
- * exit after command has been deleted.
- */
- /* Place the MF back on the FreeQ */
- Q_ADD_TAIL(&hd->ioc->FreeQ,
- &mf->u.frame.linkage,
- MPT_FRAME_HDR);
- #ifdef MFCNT
- hd->ioc->mfcnt--;
- #endif
- break;
- }
- } while ((mf = mf->u.frame.linkage.forw) != (MPT_FRAME_HDR*)&hd->taskQ);
- }
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- return;
- }
- #endif /* } MPT_SCSI_USE_NEW_EH */
- /*
- * Flush all commands on the doneQ.
- * Lock Q when deleting/adding members
- * Lock io_request_lock for OS callback.
- */
- static void
- flush_doneQ(MPT_SCSI_HOST *hd)
- {
- MPT_DONE_Q *buffer;
- Scsi_Cmnd *SCpnt;
- unsigned long flags;
- /* Flush the doneQ.
- */
- dprintk((KERN_INFO MYNAM ": flush_doneQ calledn"));
- while (1) {
- spin_lock_irqsave(&hd->freedoneQlock, flags);
- if (Q_IS_EMPTY(&hd->doneQ)) {
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- break;
- }
- buffer = hd->doneQ.head;
- /* Delete from Q
- */
- Q_DEL_ITEM(buffer);
- /* Set the Scsi_Cmnd pointer
- */
- SCpnt = (Scsi_Cmnd *) buffer->argp;
- buffer->argp = NULL;
- /* Add to the freeQ
- */
- Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- /* Do the OS callback.
- */
- MPT_HOST_LOCK(flags);
- SCpnt->scsi_done(SCpnt);
- MPT_HOST_UNLOCK(flags);
- }
- return;
- }
- /*
- * Search the doneQ for a specific command. If found, delete from Q.
- * Calling function will finish processing.
- */
- static void
- search_doneQ_for_cmd(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt)
- {
- unsigned long flags;
- MPT_DONE_Q *buffer;
- spin_lock_irqsave(&hd->freedoneQlock, flags);
- if (!Q_IS_EMPTY(&hd->doneQ)) {
- buffer = hd->doneQ.head;
- do {
- Scsi_Cmnd *sc = (Scsi_Cmnd *) buffer->argp;
- if (SCpnt == sc) {
- Q_DEL_ITEM(buffer);
- SCpnt->result = sc->result;
- /* Set the Scsi_Cmnd pointer
- */
- buffer->argp = NULL;
- /* Add to the freeQ
- */
- Q_ADD_TAIL(&hd->freeQ.head, buffer, MPT_DONE_Q);
- break;
- }
- } while ((buffer = buffer->forw) != (MPT_DONE_Q *) &hd->doneQ);
- }
- spin_unlock_irqrestore(&hd->freedoneQlock, flags);
- return;
- }
- /*
- * mptscsih_flush_running_cmds - For each command found, search
- * Scsi_Host instance taskQ and reply to OS.
- * Called only if recovering from a FW reload.
- * @hd: Pointer to a SCSI HOST structure
- *
- * Returns: None.
- *
- * Must be called while new I/Os are being queued.
- */
- static void
- mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
- {
- Scsi_Cmnd *SCpnt = NULL;
- MPT_FRAME_HDR *mf = NULL;
- int ii;
- int max = hd->ioc->req_depth;
- #ifndef MPT_SCSI_USE_NEW_EH
- unsigned long flags;
- #endif
- dprintk((KERN_INFO MYNAM ": flush_ScsiLookup calledn"));
- for (ii= 0; ii < max; ii++) {
- if ((SCpnt = hd->ScsiLookup[ii]) != NULL) {
- /* Command found.
- */
- #ifndef MPT_SCSI_USE_NEW_EH
- /* Search taskQ, if found, delete.
- */
- search_taskQ_for_cmd(SCpnt, hd);
- #endif
- /* Search pendingQ, if found,
- * delete from Q. If found, do not decrement
- * queue_depth, command never posted.
- */
- if (mptscsih_search_pendingQ(hd, ii) == NULL)
- atomic_dec(&queue_depth);
- /* Null ScsiLookup index
- */
- hd->ScsiLookup[ii] = NULL;
- mf = MPT_INDEX_2_MFPTR(hd->ioc, ii);
- dmfprintk(( "flush: ScsiDone (mf=%p,sc=%p)n",
- mf, SCpnt));
- /* Set status, free OS resources (SG DMA buffers)
- * Do OS callback
- * Free driver resources (chain, msg buffers)
- */
- if (SCpnt->use_sg) {
- pci_unmap_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) {
- scPrivate *my_priv;
-
- my_priv = (scPrivate *) &SCpnt->SCp;
- pci_unmap_single(hd->ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
- SCpnt->request_bufflen,
- scsi_to_pci_dma_dir(SCpnt->sc_data_direction));
- }
- SCpnt->result = DID_RESET << 16;
- SCpnt->host_scribble = NULL;
- MPT_HOST_LOCK(flags);
- SCpnt->scsi_done(SCpnt); /* Issue the command callback */
- MPT_HOST_UNLOCK(flags);
- /* Free Chain buffers */
- mptscsih_freeChainBuffers(hd, ii);
- /* Free Message frames */
- mpt_free_msg_frame(ScsiDoneCtx, hd->ioc->id, mf);
- }
- }
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- /* Clear untagged counting array */
- for (ii= 0; ii < MPT_MAX_SCSI_DEVICES; ii++)
- hd->ioc->spi_data.iocntr[ii] = 0;
- #endif
- return;
- }
- #ifdef DROP_TEST
- /* mptscsih_flush_drop_test - Free resources and do callback if
- * DROP_TEST enabled.
- *
- * @hd: Pointer to a SCSI HOST structure
- *
- * Returns: None.
- *
- * Must be called while new I/Os are being queued.
- */
- static void
- mptscsih_flush_drop_test (MPT_SCSI_HOST *hd)
- {
- Scsi_Cmnd *sc;
- unsigned long flags;
- u16 req_idx;
- /* Free resources for the drop test MF
- * and chain buffers.
- */
- if (dropMfPtr) {
- req_idx = le16_to_cpu(dropMfPtr->u.frame.hwhdr.msgctxu.fld.req_idx);
- sc = hd->ScsiLookup[req_idx];
- if (sc == NULL) {
- printk(MYIOC_s_ERR_FMT "Drop Test: NULL ScsiCmd ptr!n",
- ioc->name);
- } else {
- /* unmap OS resources, set status, do callback
- * free driver resources
- */
- if (sc->use_sg) {
- pci_unmap_sg(ioc->pcidev, (struct scatterlist *) sc->request_buffer,
- sc->use_sg, scsi_to_pci_dma_dir(sc->sc_data_direction));
- } else if (sc->request_bufflen) {
- scPrivate *my_priv;
- my_priv = (scPrivate *) &sc->SCp;
- pci_unmap_single(ioc->pcidev, (dma_addr_t)(ulong)my_priv->p1,
- sc->request_bufflen,
- scsi_to_pci_dma_dir(sc->sc_data_direction));
- }
- sc->host_scribble = NULL;
- sc->result = DID_RESET << 16;
- hd->ScsiLookup[req_idx] = NULL;
- atomic_dec(&queue_depth);
- MPT_HOST_LOCK(flags);
- sc->scsi_done(sc); /* Issue callback */
- MPT_HOST_UNLOCK(flags);
- }
- mptscsih_freeChainBuffers(hd, req_idx);
- mpt_free_msg_frame(ScsiDoneCtx, ioc->id, dropMfPtr);
- printk(MYIOC_s_INFO_FMT "Free'd Dropped cmd (%p)n",
- hd->ioc->name, sc);
- printk(MYIOC_s_INFO_FMT "mf (%p) reqidx (%4x)n",
- hd->ioc->name, dropMfPtr, req_idx);
- printk(MYIOC_s_INFO_FMT "Num Tot (%d) Good (%d) Bad (%d) n",
- hd->ioc->name, dropTestNum,
- dropTestOK, dropTestBad);
- }
- dropMfPtr = NULL;
- return;
- }
- #endif
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_initChainBuffers - Allocate memory for and initialize
- * chain buffers, chain buffer control arrays and spinlock.
- * @hd: Pointer to MPT_SCSI_HOST structure
- * @init: If set, initialize the spin lock.
- */
- static int
- mptscsih_initChainBuffers (MPT_SCSI_HOST *hd, int init)
- {
- MPT_FRAME_HDR *chain;
- u8 *mem;
- unsigned long flags;
- int sz, ii, numChain;
- /* Chain buffer allocations
- * Allocate and initialize tracker structures
- */
- if (hd->ioc->req_sz <= 64)
- numChain = MPT_SG_REQ_64_SCALE * hd->ioc->req_depth;
- else if (hd->ioc->req_sz <= 96)
- numChain = MPT_SG_REQ_96_SCALE * hd->ioc->req_depth;
- else
- numChain = MPT_SG_REQ_128_SCALE * hd->ioc->req_depth;
- sz = numChain * sizeof(int);
- if (hd->ReqToChain == NULL) {
- mem = kmalloc(sz, GFP_ATOMIC);
- if (mem == NULL)
- return -1;
- hd->ReqToChain = (int *) mem;
- } else {
- mem = (u8 *) hd->ReqToChain;
- }
- memset(mem, 0xFF, sz);
- if (hd->ChainToChain == NULL) {
- mem = kmalloc(sz, GFP_ATOMIC);
- if (mem == NULL)
- return -1;
- hd->ChainToChain = (int *) mem;
- } else {
- mem = (u8 *) hd->ChainToChain;
- }
- memset(mem, 0xFF, sz);
- if (hd->ChainBuffer == NULL) {
- /* Allocate free chain buffer pool
- */
- sz = numChain * hd->ioc->req_sz;
- mem = pci_alloc_consistent(hd->ioc->pcidev, sz, &hd->ChainBufferDMA);
- if (mem == NULL)
- return -1;
- hd->ChainBuffer = (u8*)mem;
- } else {
- mem = (u8 *) hd->ChainBuffer;
- }
- memset(mem, 0, sz);
- dprintk((KERN_INFO " ChainBuffer @ %p(%p), sz=%dn",
- hd->ChainBuffer, (void *)(ulong)hd->ChainBufferDMA, sz));
- /* Initialize the free chain Q.
- */
- if (init) {
- spin_lock_init(&hd->FreeChainQlock);
- }
- spin_lock_irqsave (&hd->FreeChainQlock, flags);
- Q_INIT(&hd->FreeChainQ, MPT_FRAME_HDR);
- /* Post the chain buffers to the FreeChainQ.
- */
- mem = (u8 *)hd->ChainBuffer;
- for (ii=0; ii < numChain; ii++) {
- chain = (MPT_FRAME_HDR *) mem;
- Q_ADD_TAIL(&hd->FreeChainQ.head, &chain->u.frame.linkage, MPT_FRAME_HDR);
- mem += hd->ioc->req_sz;
- }
- spin_unlock_irqrestore(&hd->FreeChainQlock, flags);
- return 0;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * Hack! It might be nice to report if a device is returning QUEUE_FULL
- * but maybe not each and every time...
- */
- static long last_queue_full = 0;
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_report_queue_full - Report QUEUE_FULL status returned
- * from a SCSI target device.
- * @sc: Pointer to Scsi_Cmnd structure
- * @pScsiReply: Pointer to SCSIIOReply_t
- * @pScsiReq: Pointer to original SCSI request
- *
- * This routine periodically reports QUEUE_FULL status returned from a
- * SCSI target device. It reports this to the console via kernel
- * printk() API call, not more than once every 10 seconds.
- */
- static void
- mptscsih_report_queue_full(Scsi_Cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
- {
- long time = jiffies;
- if (time - last_queue_full > 10 * HZ) {
- char *ioc_str = "ioc?";
- if (sc->host != NULL && sc->host->hostdata != NULL)
- ioc_str = ((MPT_SCSI_HOST *)sc->host->hostdata)->ioc->name;
- printk(MYIOC_s_WARN_FMT "Device (%d:%d:%d) reported QUEUE_FULL!n",
- ioc_str, 0, sc->target, sc->lun);
- last_queue_full = time;
- }
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static int BeenHereDoneThat = 0;
- /* SCSI host fops start here... */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_detect - Register MPT adapter(s) as SCSI host(s) with
- * linux scsi mid-layer.
- * @tpnt: Pointer to Scsi_Host_Template structure
- *
- * (linux Scsi_Host_Template.detect routine)
- *
- * Returns number of SCSI host adapters that were successfully
- * registered with the linux scsi mid-layer via the scsi_register()
- * API call.
- */
- int
- mptscsih_detect(Scsi_Host_Template *tpnt)
- {
- struct Scsi_Host *sh = NULL;
- MPT_SCSI_HOST *hd = NULL;
- MPT_ADAPTER *this;
- MPT_DONE_Q *freedoneQ;
- unsigned long flags;
- int sz, ii;
- int numSGE = 0;
- int scale;
- u8 *mem;
- if (! BeenHereDoneThat++) {
- show_mptmod_ver(my_NAME, my_VERSION);
- ScsiDoneCtx = mpt_register(mptscsih_io_done, MPTSCSIH_DRIVER);
- ScsiTaskCtx = mpt_register(mptscsih_taskmgmt_complete, MPTSCSIH_DRIVER);
- ScsiScanDvCtx = mpt_register(mptscsih_scandv_complete, MPTSCSIH_DRIVER);
- #ifndef MPT_SCSI_USE_NEW_EH
- spin_lock_init(&mytaskQ_lock);
- #endif
- if (mpt_event_register(ScsiDoneCtx, mptscsih_event_process) == 0) {
- dprintk((KERN_INFO MYNAM ": Registered for IOC event notificationsn"));
- } else {
- /* FIXME! */
- }
- if (mpt_reset_register(ScsiDoneCtx, mptscsih_ioc_reset) == 0) {
- dprintk((KERN_INFO MYNAM ": Registered for IOC reset notificationsn"));
- } else {
- /* FIXME! */
- }
- }
- dprintk((KERN_INFO MYNAM ": mpt_scsih_detect()n"));
- #ifdef MODULE
- /* Evaluate the command line arguments, if any */
- if (mptscsih)
- mptscsih_setup(mptscsih);
- #endif
- #ifndef MPT_SCSI_USE_NEW_EH
- atomic_set(&mpt_taskQdepth, 0);
- #endif
- this = mpt_adapter_find_first();
- while (this != NULL) {
- int portnum;
- for (portnum=0; portnum < this->facts.NumberOfPorts; portnum++) {
- /* 20010215 -sralston
- * Added sanity check on SCSI Initiator-mode enabled
- * for this MPT adapter.
- */
- if (!(this->pfacts[portnum].ProtocolFlags & MPI_PORTFACTS_PROTOCOL_INITIATOR)) {
- printk(MYIOC_s_WARN_FMT "Skipping because SCSI Initiator mode is NOT enabled!n",
- this->name);
- continue;
- }
- /* 20010202 -sralston
- * Added sanity check on readiness of the MPT adapter.
- */
- if (this->last_state != MPI_IOC_STATE_OPERATIONAL) {
- printk(MYIOC_s_WARN_FMT "Skipping because it's not operational!n",
- this->name);
- continue;
- }
- #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
- tpnt->proc_dir = &proc_mpt_scsihost;
- #endif
- sh = scsi_register(tpnt, sizeof(MPT_SCSI_HOST));
- if (sh != NULL) {
- save_flags(flags);
- cli();
- sh->io_port = 0;
- sh->n_io_port = 0;
- sh->irq = 0;
- /* Yikes! This is important!
- * Otherwise, by default, linux
- * only scans target IDs 0-7!
- * pfactsN->MaxDevices unreliable
- * (not supported in early
- * versions of the FW).
- * max_id = 1 + actual max id,
- * max_lun = 1 + actual last lun,
- * see hosts.h :o(
- */
- if ((int)this->chip_type > (int)FC929)
- sh->max_id = MPT_MAX_SCSI_DEVICES;
- else {
- /* For FC, increase the queue depth
- * from MPT_SCSI_CAN_QUEUE (31)
- * to MPT_FC_CAN_QUEUE (63).
- */
- sh->can_queue = MPT_FC_CAN_QUEUE;
- sh->max_id = MPT_MAX_FC_DEVICES<256 ? MPT_MAX_FC_DEVICES : 255;
- }
- sh->max_lun = MPT_LAST_LUN + 1;
- sh->this_id = this->pfacts[portnum].PortSCSIID;
- /* OS entry to allow host drivers to force
- * a queue depth on a per device basis.
- */
- sh->select_queue_depths = mptscsih_select_queue_depths;
- /* Verify that we won't exceed the maximum
- * number of chain buffers
- * We can optimize: ZZ = req_sz/sizeof(SGE)
- * For 32bit SGE's:
- * numSGE = 1 + (ZZ-1)*(maxChain -1) + ZZ
- * + (req_sz - 64)/sizeof(SGE)
- * A slightly different algorithm is required for
- * 64bit SGEs.
- */
- scale = this->req_sz/(sizeof(dma_addr_t) + sizeof(u32));
- if (sizeof(dma_addr_t) == sizeof(u64)) {
- numSGE = (scale - 1) * (this->facts.MaxChainDepth-1) + scale +
- (this->req_sz - 60) / (sizeof(dma_addr_t) + sizeof(u32));
- } 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 */
- dprintk((MYIOC_s_INFO_FMT
- "Resetting sg_tablesize to %d from %dn",
- this->name, numSGE, sh->sg_tablesize));
- sh->sg_tablesize = numSGE;
- }
- /* Set the pci device pointer in Scsi_Host structure.
- */
- scsi_set_pci_device(sh, this->pcidev);
- restore_flags(flags);
- hd = (MPT_SCSI_HOST *) sh->hostdata;
- hd->ioc = this;
- if ((int)this->chip_type > (int)FC929)
- hd->is_spi = 1;
- if (DmpService &&
- (this->chip_type == FC919 || this->chip_type == FC929))
- hd->is_multipath = 1;
- hd->port = 0; /* FIXME! */
- /* SCSI needs Scsi_Cmnd lookup table!
- * (with size equal to req_depth*PtrSz!)
- */
- sz = hd->ioc->req_depth * sizeof(void *);
- mem = kmalloc(sz, GFP_ATOMIC);
- if (mem == NULL)
- goto done;
- memset(mem, 0, sz);
- hd->ScsiLookup = (struct scsi_cmnd **) mem;
- dprintk((MYIOC_s_INFO_FMT "ScsiLookup @ %p, sz=%dn",
- this->name, hd->ScsiLookup, sz));
- if (mptscsih_initChainBuffers(hd, 1) < 0)
- goto done;
- /* Allocate memory for free and doneQ's
- */
- sz = sh->can_queue * sizeof(MPT_DONE_Q);
- mem = kmalloc(sz, GFP_ATOMIC);
- if (mem == NULL)
- goto done;
- memset(mem, 0xFF, sz);
- hd->memQ = mem;
- /* Initialize the free, done and pending Qs.
- */
- Q_INIT(&hd->freeQ, MPT_DONE_Q);
- Q_INIT(&hd->doneQ, MPT_DONE_Q);
- Q_INIT(&hd->pendingQ, MPT_DONE_Q);
- spin_lock_init(&hd->freedoneQlock);
- mem = hd->memQ;
- for (ii=0; ii < sh->can_queue; ii++) {
- freedoneQ = (MPT_DONE_Q *) mem;
- Q_ADD_TAIL(&hd->freeQ.head, freedoneQ, MPT_DONE_Q);
- mem += sizeof(MPT_DONE_Q);
- }
- /* Initialize this Scsi_Host
- * internal task Q.
- */
- Q_INIT(&hd->taskQ, MPT_FRAME_HDR);
- hd->taskQcnt = 0;
- /* Allocate memory for the device structures.
- * A non-Null pointer at an offset
- * indicates a device exists.
- * max_id = 1 + maximum id (hosts.h)
- */
- sz = sh->max_id * sizeof(void *);
- mem = kmalloc(sz, GFP_ATOMIC);
- if (mem == NULL)
- goto done;
- memset(mem, 0, sz);
- hd->Targets = (VirtDevice **) mem;
- dprintk((KERN_INFO " Targets @ %p, sz=%dn", hd->Targets, sz));
- /* Clear the TM flags
- */
- hd->tmPending = 0;
- #ifdef MPT_SCSI_USE_NEW_EH
- hd->tmState = TM_STATE_NONE;
- #endif
- hd->resetPending = 0;
- hd->abortSCpnt = NULL;
- hd->tmPtr = NULL;
- hd->numTMrequests = 0;
- /* Clear the pointer used to store
- * single-threaded commands, i.e., those
- * issued during a bus scan, dv and
- * configuration pages.
- */
- hd->cmdPtr = NULL;
- /* Attach the SCSI Host to the IOC structure
- */
- this->sh = sh;
- /* Initialize this SCSI Hosts' timers
- * To use, set the timer expires field
- * and add_timer
- */
- init_timer(&hd->timer);
- hd->timer.data = (unsigned long) hd;
- hd->timer.function = mptscsih_timer_expired;
- init_timer(&hd->TMtimer);
- hd->TMtimer.data = (unsigned long) hd;
- hd->TMtimer.function = mptscsih_taskmgmt_timeout;
- hd->qtag_tick = jiffies;
- /* Moved Earlier Pam D */
- /* this->sh = sh; */
- if (hd->is_spi) {
- /* Update with the driver setup
- * values.
- */
- if (hd->ioc->spi_data.maxBusWidth > driver_setup.max_width)
- hd->ioc->spi_data.maxBusWidth = driver_setup.max_width;
- if (hd->ioc->spi_data.minSyncFactor < driver_setup.min_sync_fac)
- hd->ioc->spi_data.minSyncFactor = driver_setup.min_sync_fac;
- if (hd->ioc->spi_data.minSyncFactor == MPT_ASYNC)
- hd->ioc->spi_data.maxSyncOffset = 0;
- hd->negoNvram = 0;
- #ifdef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
- #endif
- if (driver_setup.dv == 0)
- hd->negoNvram = MPT_SCSICFG_USE_NVRAM;
- hd->ioc->spi_data.forceDv = 0;
- for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
- hd->ioc->spi_data.dvStatus[ii] = MPT_SCSICFG_NEGOTIATE;
-
- if (hd->negoNvram == 0) {
- for (ii=0; ii < MPT_MAX_SCSI_DEVICES; ii++)
- hd->ioc->spi_data.dvStatus[ii] |= MPT_SCSICFG_DV_NOT_DONE;
- }
- ddvprintk((MYIOC_s_INFO_FMT
- "dv %x width %x factor %x n",
- hd->ioc->name, driver_setup.dv,
- driver_setup.max_width,
- driver_setup.min_sync_fac));
- }
- mpt_scsi_hosts++;
- }
- } /* for each adapter port */
- this = mpt_adapter_find_next(this);
- }
- done:
- if (mpt_scsi_hosts > 0)
- register_reboot_notifier(&mptscsih_notifier);
- return mpt_scsi_hosts;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- static char *info_kbuf = NULL;
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_release - Unregister SCSI host from linux scsi mid-layer
- * @host: Pointer to Scsi_Host structure
- *
- * (linux Scsi_Host_Template.release routine)
- * This routine releases all resources associated with the SCSI host
- * adapter.
- *
- * Returns 0 for success.
- */
- int
- mptscsih_release(struct Scsi_Host *host)
- {
- MPT_SCSI_HOST *hd;
- int count;
- unsigned long flags;
- hd = (MPT_SCSI_HOST *) host->hostdata;
- #ifndef MPT_SCSI_USE_NEW_EH
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- spin_lock_irqsave(&dvtaskQ_lock, flags);
- dvtaskQ_release = 1;
- spin_unlock_irqrestore(&dvtaskQ_lock, flags);
- #endif
- count = 10 * HZ;
- spin_lock_irqsave(&mytaskQ_lock, flags);
- if (mytaskQ_bh_active) {
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- dprintk((KERN_INFO MYNAM ": Info: Zapping TaskMgmt thread!n"));
- clean_taskQ(hd);
- while(mytaskQ_bh_active && --count) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
- }
- } else {
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- }
- if (!count)
- printk(KERN_ERR MYNAM ": ERROR - TaskMgmt thread still active!n");
- #endif
- #ifndef MPTSCSIH_DISABLE_DOMAIN_VALIDATION
- /* Check DV thread active */
- count = 10 * HZ;
- spin_lock_irqsave(&dvtaskQ_lock, flags);
- if (dvtaskQ_active) {
- spin_unlock_irqrestore(&dvtaskQ_lock, flags);
- while(dvtaskQ_active && --count) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(1);
- }
- } else {
- spin_unlock_irqrestore(&dvtaskQ_lock, flags);
- }
- if (!count)
- printk(KERN_ERR MYNAM ": ERROR - DV thread still active!n");
- #if defined(MPT_DEBUG_DV) || defined(MPT_DEBUG_DV_TINY)
- else
- printk(KERN_ERR MYNAM ": DV thread orig %d, count %dn", 10 * HZ, count);
- #endif
- #endif
- unregister_reboot_notifier(&mptscsih_notifier);
- if (hd != NULL) {
- int sz1, sz2, sz3, sztarget=0;
- int szr2chain = 0;
- int szc2chain = 0;
- int szchain = 0;
- int szQ = 0;
- int scale;
- /* Synchronize disk caches
- */
- (void) mptscsih_synchronize_cache(hd, 0);
- sz1 = sz2 = sz3 = 0;
- if (hd->ioc->req_sz <= 64)
- scale = MPT_SG_REQ_64_SCALE;
- else if (hd->ioc->req_sz <= 96)
- scale = MPT_SG_REQ_96_SCALE;
- else
- scale = MPT_SG_REQ_128_SCALE;
- if (hd->ScsiLookup != NULL) {
- sz1 = hd->ioc->req_depth * sizeof(void *);
- kfree(hd->ScsiLookup);
- hd->ScsiLookup = NULL;
- }
- if (hd->ReqToChain != NULL) {
- szr2chain = scale * hd->ioc->req_depth * sizeof(int);
- kfree(hd->ReqToChain);
- hd->ReqToChain = NULL;
- }
- if (hd->ChainToChain != NULL) {
- szc2chain = scale * hd->ioc->req_depth * sizeof(int);
- kfree(hd->ChainToChain);
- hd->ChainToChain = NULL;
- }
- if (hd->ChainBuffer != NULL) {
- sz2 = scale * hd->ioc->req_depth * hd->ioc->req_sz;
- szchain = szr2chain + szc2chain + sz2;
- pci_free_consistent(hd->ioc->pcidev, sz2,
- hd->ChainBuffer, hd->ChainBufferDMA);
- hd->ChainBuffer = NULL;
- }
- if (hd->memQ != NULL) {
- szQ = host->can_queue * sizeof(MPT_DONE_Q);
- kfree(hd->memQ);
- hd->memQ = NULL;
- }
- if (hd->Targets != NULL) {
- int max, ii;
- /*
- * Free any target structures that were allocated.
- */
- if (hd->is_spi) {
- max = MPT_MAX_SCSI_DEVICES;
- } else {
- max = MPT_MAX_FC_DEVICES;
- }
- for (ii=0; ii < max; ii++) {
- if (hd->Targets[ii]) {
- kfree(hd->Targets[ii]);
- hd->Targets[ii] = NULL;
- sztarget += sizeof(VirtDevice);
- }
- }
- /*
- * Free pointer array.
- */
- sz3 = max * sizeof(void *);
- kfree(hd->Targets);
- hd->Targets = NULL;
- }
- dprintk((MYIOC_s_INFO_FMT "Free'd ScsiLookup (%d), chain (%d) and Target (%d+%d) memoryn",
- hd->ioc->name, sz1, szchain, sz3, sztarget));
- dprintk(("Free'd done and free Q (%d) memoryn", szQ));
- }
- /* NULL the Scsi_Host pointer
- */
- hd->ioc->sh = NULL;
- scsi_unregister(host);
- if (mpt_scsi_hosts) {
- if (--mpt_scsi_hosts == 0) {
- mpt_reset_deregister(ScsiDoneCtx);
- dprintk((KERN_INFO MYNAM ": Deregistered for IOC reset notificationsn"));
- mpt_event_deregister(ScsiDoneCtx);
- dprintk((KERN_INFO MYNAM ": Deregistered for IOC event notificationsn"));
- mpt_deregister(ScsiScanDvCtx);
- mpt_deregister(ScsiTaskCtx);
- mpt_deregister(ScsiDoneCtx);
- if (info_kbuf != NULL)
- kfree(info_kbuf);
- }
- }
- return 0;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_halt - Process the reboot notification
- * @nb: Pointer to a struct notifier_block (ignored)
- * @event: event (SYS_HALT, SYS_RESTART, SYS_POWER_OFF)
- * @buf: Pointer to a data buffer (ignored)
- *
- * This routine called if a system shutdown or reboot is to occur.
- *
- * Return NOTIFY_DONE if this is something other than a reboot message.
- * NOTIFY_OK if this is a reboot message.
- */
- static int
- mptscsih_halt(struct notifier_block *nb, ulong event, void *buf)
- {
- MPT_ADAPTER *ioc = NULL;
- MPT_SCSI_HOST *hd = NULL;
- /* Ignore all messages other than reboot message
- */
- if ((event != SYS_RESTART) && (event != SYS_HALT)
- && (event != SYS_POWER_OFF))
- return (NOTIFY_DONE);
- for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
- /* 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] = '';
- mpt_print_ioc_summary(h->ioc, info_kbuf, &size, 0, 0);
- info_kbuf[size-1] = '';
- 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
- *
- * (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)
- */
- int
- mptscsih_qcmd(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
- {
- 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()=%pn",
- (hd && hd->ioc) ? hd->ioc->name : "ioc?", SCpnt, done));
- /* 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 (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);
- #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)
- SCpnt->resid = SCpnt->request_bufflen - sz;
- #endif
- SCpnt->result = 0;
- SCpnt->scsi_done(SCpnt);
- //sjr-moved-up//pTarget->tflags &= ~MPT_TARGET_FLAGS_VALID_SENSE;
- return 0;
- }
- }
- }
- if (hd->resetPending) {
- /* Prevent new commands from being issued
- * while reloading the FW.
- */
- did_errcode = 1;
- goto did_error;
- }
- /*
- * 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;
- }
- pScsiReq = (SCSIIORequest_t *) mf;
- my_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx);
- ADD_INDEX_LOG(my_idx);
- /*
- * The scsi layer should be handling this stuff
- * (In 2.3.x it does -DaveM)
- */
- /* 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;
- }
- /* 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;
- }
- else
- qtag = MPI_SCSIIO_CONTROL_SIMPLEQ;
- }
- 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);
- /*
- * 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;
- /* 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.
- */
- 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);
- }
- 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++;
- 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;
- }
- }
- }
- #endif
- /* 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;
- }
- #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);
- mptscsih_dvTask.sync = 0;
- mptscsih_dvTask.routine = mptscsih_domainValidation;
- mptscsih_dvTask.data = (void *) hd;
- SCHEDULE_TASK(&mptscsih_dvTask);
- } else {
- spin_unlock_irqrestore(&dvtaskQ_lock, lflags);
- }
- hd->ioc->spi_data.forceDv = 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;
- }
- if (qtag == MPI_SCSIIO_CONTROL_UNTAGGED)
- hd->ioc->spi_data.iocntr[target]++;
- /* Set the DV flags.
- */
- if (dvStatus & MPT_SCSICFG_DV_NOT_DONE)
- mptscsih_set_dvflags(hd, pScsiReq);
- #endif
- }
- }
- if (issueCmd) {
- mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
- dmfprintk((MYIOC_s_INFO_FMT "Issued SCSI cmd (%p) mf=%p idx=%dn",
- hd->ioc->name, SCpnt, mf, my_idx));
- } else {
- ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %dn",
- 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);
- /* Save the mf pointer
- */
- buffer->argp = (void *)mf;
- /* 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 0;
- 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;
- /* 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;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * 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=%dn",
- hd->ioc->name, SCpnt, SCpnt->request_bufflen));
- mpt_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);
- 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++;
- }
- 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);
- mpt_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.
- */
- 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.
- */
- dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %dn",
- 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));
- 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);
- }
- 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_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;
- //spin_lock_irqsave(&hd->FreeChainQlock, flags);
- 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->FreeChainQlock, flags);
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- *retIndex = chain_idx;
- dsgprintk((MYIOC_s_INFO_FMT "getFreeChainBuffer (index %d), got buf=%pn",
- hd->ioc->name, *retIndex, chainBuf));
- return rc;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_freeChainBuffers - Function to free chain buffers associated
- * with a SCSI IO request
- * @hd: Pointer to the MPT_SCSI_HOST instance
- * @req_idx: Index of the SCSI IO request frame.
- *
- * Called if SG chain buffer allocation fails and mptscsih callbacks.
- * No return.
- */
- static void
- mptscsih_freeChainBuffers(MPT_SCSI_HOST *hd, int req_idx)
- {
- MPT_FRAME_HDR *chain = NULL;
- unsigned long flags;
- int chain_idx;
- int next;
- /* Get the first chain index and reset
- * tracker state.
- */
- chain_idx = hd->ReqToChain[req_idx];
- hd->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
- while (chain_idx != MPT_HOST_NO_CHAIN) {
- /* Save the next chain buffer index */
- next = hd->ChainToChain[chain_idx];
- /* Free this chain buffer and reset
- * tracker
- */
- hd->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
- chain = (MPT_FRAME_HDR *) (hd->ChainBuffer
- + (chain_idx * hd->ioc->req_sz));
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- Q_ADD_TAIL(&hd->FreeChainQ.head,
- &chain->u.frame.linkage, MPT_FRAME_HDR);
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- dmfprintk((MYIOC_s_INFO_FMT "FreeChainBuffers (index %d)n",
- hd->ioc->name, chain_idx));
- /* handle next */
- chain_idx = next;
- }
- return;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * Reset Handling
- */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_TMHandler - Generic handler for SCSI Task Management.
- * Fall through to mpt_HardResetHandler if: not operational, too many
- * failed TM requests or handshake failure.
- *
- * @ioc: Pointer to MPT_ADAPTER structure
- * @type: Task Management type
- * @target: Logical Target ID for reset (if appropriate)
- * @lun: Logical Unit for reset (if appropriate)
- * @ctx2abort: Context for the task to be aborted (if appropriate)
- * @sleepFlag: If set, use udelay instead of schedule in handshake code.
- *
- * Remark: Currently invoked from a non-interrupt thread (_bh).
- *
- * Remark: With old EH code, at most 1 SCSI TaskMgmt function per IOC
- * will be active.
- *
- * Returns 0 for SUCCESS or -1 if FAILED.
- */
- static int
- mptscsih_TMHandler(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
- {
- MPT_ADAPTER *ioc = NULL;
- int rc = -1;
- int doTask = 1;
- u32 ioc_raw_state;
- unsigned long flags;
- /* If FW is being reloaded currently, return success to
- * the calling function.
- */
- if (hd == NULL)
- return 0;
- ioc = hd->ioc;
- dtmprintk((MYIOC_s_INFO_FMT "TMHandler Entered!n", ioc->name));
- if (ioc == NULL) {
- printk(KERN_ERR MYNAM " TMHandler" " NULL ioc!n");
- return 0;
- }
- // SJR - CHECKME - Can we avoid this here?
- // (mpt_HardResetHandler has this check...)
- spin_lock_irqsave(&ioc->diagLock, flags);
- if ((ioc->diagPending) || (ioc->alt_ioc && ioc->alt_ioc->diagPending)) {
- spin_unlock_irqrestore(&ioc->diagLock, flags);
- return 0;
- }
- spin_unlock_irqrestore(&ioc->diagLock, flags);
- /* Do not do a Task Management if there are
- * too many failed TMs on this adapter.
- */
- if (hd->numTMrequests > MPT_HOST_TOO_MANY_TM)
- doTask = 0;
- /* Is operational?
- */
- ioc_raw_state = mpt_GetIocState(hd->ioc, 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 HardResetHandlern",
- hd->ioc->name, ioc_raw_state);
- }
- #endif
- if (doTask && ((ioc_raw_state & MPI_IOC_STATE_MASK) == MPI_IOC_STATE_OPERATIONAL)
- && !(ioc_raw_state & MPI_DOORBELL_ACTIVE)) {
- /* Isse the Task Mgmt request.
- */
- rc = mptscsih_IssueTaskMgmt(hd, type, target, lun, ctx2abort, sleepFlag);
- if (rc) {
- #ifdef MPT_SCSI_USE_NEW_EH
- hd->tmState = TM_STATE_ERROR;
- #endif
- printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt failed!n", hd->ioc->name);
- } else {
- printk(MYIOC_s_INFO_FMT "Issue of TaskMgmt Successful!n", hd->ioc->name);
- }
- }
- #ifdef DROP_TEST
- numTMrequested++;
- if (numTMrequested > 5) {
- rc = 0; /* set to 1 to force a hard reset */
- numTMrequested = 0;
- }
- #endif
- if (rc) {
- dtmprintk((MYIOC_s_INFO_FMT "Falling through to HardReset! n",
- hd->ioc->name));
- rc = mpt_HardResetHandler(hd->ioc, sleepFlag);
- }
- dtmprintk((MYIOC_s_INFO_FMT "TMHandler rc = %d!n", hd->ioc->name, rc));
- #ifndef MPT_SCSI_USE_NEW_EH
- dtmprintk((MYIOC_s_INFO_FMT "TMHandler: _bh_handler state (%d) taskQ count (%d)n",
- ioc->name, mytaskQ_bh_active, hd->taskQcnt));
- #endif
- return rc;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_IssueTaskMgmt - Generic send Task Management function.
- * @hd: Pointer to MPT_SCSI_HOST structure
- * @type: Task Management type
- * @target: Logical Target ID for reset (if appropriate)
- * @lun: Logical Unit for reset (if appropriate)
- * @ctx2abort: Context for the task to be aborted (if appropriate)
- * @sleepFlag: If set, use udelay instead of schedule in handshake code.
- *
- * Remark: _HardResetHandler can be invoked from an interrupt thread (timer)
- * or a non-interrupt thread. In the former, must not call schedule().
- *
- * Not all fields are meaningfull for all task types.
- *
- * Returns 0 for SUCCESS, -999 for "no msg frames",
- * else other non-zero value returned.
- */
- static int
- mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 target, u8 lun, int ctx2abort, int sleepFlag)
- {
- MPT_FRAME_HDR *mf;
- SCSITaskMgmt_t *pScsiTm;
- int ii;
- int retval;
- /* Return Fail to calling function if no message frames available.
- */
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
- dtmprintk((MYIOC_s_WARN_FMT "IssueTaskMgmt, no msg frames!!n",
- hd->ioc->name));
- //return FAILED;
- return -999;
- }
- dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt request @ %pn",
- hd->ioc->name, mf));
- /* Format the Request
- */
- pScsiTm = (SCSITaskMgmt_t *) mf;
- pScsiTm->TargetID = target;
- pScsiTm->Bus = hd->port;
- pScsiTm->ChainOffset = 0;
- pScsiTm->Function = MPI_FUNCTION_SCSI_TASK_MGMT;
- pScsiTm->Reserved = 0;
- pScsiTm->TaskType = type;
- pScsiTm->Reserved1 = 0;
- pScsiTm->MsgFlags = (type == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS)
- ? MPI_SCSITASKMGMT_MSGFLAGS_LIPRESET_RESET_OPTION : 0;
- for (ii= 0; ii < 8; ii++) {
- pScsiTm->LUN[ii] = 0;
- }
- pScsiTm->LUN[1] = lun;
- for (ii=0; ii < 7; ii++)
- pScsiTm->Reserved2[ii] = 0;
- pScsiTm->TaskMsgContext = ctx2abort;
- dtmprintk((MYIOC_s_INFO_FMT "IssueTaskMgmt, ctx2abort (0x%08x), type (%d)n",
- hd->ioc->name, ctx2abort, type));
- /* MPI v0.10 requires SCSITaskMgmt requests be sent via Doorbell/handshake
- mpt_put_msg_frame(hd->ioc->id, mf);
- * Save the MF pointer in case the request times out.
- */
- hd->tmPtr = mf;
- hd->numTMrequests++;
- hd->TMtimer.expires = jiffies + HZ*20; /* 20 seconds */
- add_timer(&hd->TMtimer);
- if ((retval = mpt_send_handshake_request(ScsiTaskCtx, hd->ioc->id,
- sizeof(SCSITaskMgmt_t), (u32*)pScsiTm, sleepFlag))
- != 0) {
- dtmprintk((MYIOC_s_WARN_FMT "_send_handshake FAILED!"
- " (hd %p, ioc %p, mf %p) n", hd->ioc->name, hd, hd->ioc, mf));
- hd->numTMrequests--;
- hd->tmPtr = NULL;
- del_timer(&hd->TMtimer);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- }
- return retval;
- }
- #ifdef MPT_SCSI_USE_NEW_EH /* { */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_abort - Abort linux Scsi_Cmnd routine, new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
- *
- * (linux Scsi_Host_Template.eh_abort_handler routine)
- *
- * Returns SUCCESS or FAILED.
- */
- int
- mptscsih_abort(Scsi_Cmnd * SCpnt)
- {
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- unsigned long flags;
- u32 ctx2abort;
- int scpnt_idx;
- /* If we can't locate our host adapter structure, return FAILED status.
- */
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- nehprintk((KERN_WARNING MYNAM ": mptscsih_abort: "
- "Can't locate host! (sc=%p)n",
- SCpnt));
- return FAILED;
- }
- printk(KERN_WARNING MYNAM ": %s: >> Attempting task abort! (sc=%p)n",
- hd->ioc->name, SCpnt);
- printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %dn",
- hd->ioc->name, atomic_read(&queue_depth));
- /* Find this command
- */
- if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
- /* Cmd not found in ScsiLookup. If found in
- * doneQ, delete from Q. Do OS callback.
- */
- search_doneQ_for_cmd(hd, SCpnt);
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
- "Command not in the active list! (sc=%p)n",
- hd->ioc->name, SCpnt));
- return SUCCESS;
- }
- /* Wait a fixed amount of time for the TM pending flag to be cleared.
- * If we time out, then we return a FAILED status to the caller. This
- * call to mptscsih_tm_pending_wait() will set the pending flag if we are
- * successful.
- */
- if (mptscsih_tm_pending_wait(hd) == FAILED){
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
- "Timed out waiting for previous TM to complete! "
- "(sc = %p)n",
- hd->ioc->name, SCpnt));
- return FAILED;
- }
- /* If this command is pended, then timeout/hang occurred
- * during DV. Post command and flush pending Q
- * and then following up with the reset request.
- */
- if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
- mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
- post_pendingQ_commands(hd);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
- "Found command in pending queue! (sc=%p)n",
- hd->ioc->name, SCpnt));
- }
- /* Most important! Set TaskMsgContext to SCpnt's MsgContext!
- * (the IO to be ABORT'd)
- *
- * NOTE: Since we do not byteswap MsgContext, we do not
- * swap it here either. It is an opaque cookie to
- * the controller, so it does not matter. -DaveM
- */
- mf = MPT_INDEX_2_MFPTR(hd->ioc, scpnt_idx);
- ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
- hd->abortSCpnt = SCpnt;
- if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
- SCpnt->target, SCpnt->lun, ctx2abort, CAN_SLEEP)
- < 0
- || hd->tmState == TM_STATE_ERROR) {
- /* The TM request failed and the subsequent FW-reload failed!
- * Fatal error case.
- */
- printk(MYIOC_s_WARN_FMT "Error issuing abort task! (sc=%p)n",
- hd->ioc->name, SCpnt);
- /* If command not found, do not do callback,
- * just return failed. CHECKME
- */
- if (hd->ScsiLookup[scpnt_idx] != NULL) {
- SCpnt->result = STS_BUSY;
- SCpnt->scsi_done(SCpnt);
- }
- /* We must clear our pending flag before clearing our state.
- */
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- return FAILED;
- }
- /* Our task management request will either complete or time out. So we
- * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
- * we encountered an error executing the task management request.
- */
- while (hd->tmPending == 1){
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- }
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (hd->tmState == TM_STATE_ERROR){
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
- "TM timeout error! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return FAILED;
- }
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_abort: "
- "Abort was successful! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return SUCCESS;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_dev_reset - Perform a SCSI TARGET_RESET! new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- *
- * (linux Scsi_Host_Template.eh_dev_reset_handler routine)
- *
- * Returns SUCCESS or FAILED.
- */
- int
- mptscsih_dev_reset(Scsi_Cmnd * SCpnt)
- {
- MPT_SCSI_HOST *hd;
- unsigned long flags;
- /* If we can't locate our host adapter structure, return FAILED status.
- */
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
- nehprintk((KERN_WARNING MYNAM ": mptscsih_dev_reset: "
- "Can't locate host! (sc=%p)n",
- SCpnt));
- return FAILED;
- }
- printk(KERN_WARNING MYNAM ": %s: >> Attempting target reset! (sc=%p)n",
- hd->ioc->name, SCpnt);
- printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %dn",
- hd->ioc->name, atomic_read(&queue_depth));
- /* Wait a fixed amount of time for the TM pending flag to be cleared.
- * If we time out, then we return a FAILED status to the caller. This
- * call to mptscsih_tm_pending_wait() will set the pending flag if we are
- * successful.
- */
- if (mptscsih_tm_pending_wait(hd) == FAILED) {
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
- "Timed out waiting for previous TM to complete! "
- "(sc = %p)n",
- hd->ioc->name, SCpnt));
- return FAILED;
- }
- if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_TARGET_RESET,
- SCpnt->target, 0, 0, CAN_SLEEP)
- < 0){
- /* The TM request failed and the subsequent FW-reload failed!
- * Fatal error case.
- */
- printk(MYIOC_s_WARN_FMT "Error processing TaskMgmt request (sc=%p)n",
- hd->ioc->name, SCpnt);
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- return FAILED;
- }
- /* Our task management request will either complete or time out. So we
- * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
- * we encountered an error executing the task management request.
- */
- while (hd->tmPending == 1){
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- }
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (hd->tmState == TM_STATE_ERROR){
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
- "TM timeout error! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return FAILED;
- }
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_dev_reset: "
- "Device reset was successful! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return SUCCESS;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_bus_reset - Perform a SCSI BUS_RESET! new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- *
- * (linux Scsi_Host_Template.eh_bus_reset_handler routine)
- *
- * Returns SUCCESS or FAILED.
- */
- int
- mptscsih_bus_reset(Scsi_Cmnd * SCpnt)
- {
- MPT_SCSI_HOST *hd;
- unsigned long flags;
- /* If we can't locate our host adapter structure, return FAILED status.
- */
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
- nehprintk((KERN_WARNING MYNAM ": mptscsih_bus_reset: "
- "Can't locate host! (sc=%p)n",
- SCpnt ) );
- return FAILED;
- }
- printk(KERN_WARNING MYNAM ": %s: >> Attempting bus reset! (sc=%p)n",
- hd->ioc->name, SCpnt);
- printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %dn",
- hd->ioc->name, atomic_read(&queue_depth));
- /* Wait a fixed amount of time for the TM pending flag to be cleared.
- * If we time out, then we return a FAILED status to the caller. This
- * call to mptscsih_tm_pending_wait() will set the pending flag if we are
- * successful.
- */
- if (mptscsih_tm_pending_wait(hd) == FAILED) {
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
- "Timed out waiting for previous TM to complete! "
- "(sc = %p)n",
- hd->ioc->name, SCpnt ) );
- return FAILED;
- }
- /* We are now ready to execute the task management request. */
- if (mptscsih_TMHandler(hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS,
- 0, 0, 0, CAN_SLEEP)
- < 0){
- /* The TM request failed and the subsequent FW-reload failed!
- * Fatal error case.
- */
- printk(MYIOC_s_WARN_FMT
- "Error processing TaskMgmt request (sc=%p)n",
- hd->ioc->name, SCpnt);
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- return FAILED;
- }
- /* Our task management request will either complete or time out. So we
- * spin until tmPending is != 1. If tmState is set to TM_STATE_ERROR,
- * we encountered an error executing the task management request.
- */
- while (hd->tmPending == 1){
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- }
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (hd->tmState == TM_STATE_ERROR){
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
- "TM timeout error! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return FAILED;
- }
- hd->tmState = TM_STATE_NONE;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- nehprintk((KERN_WARNING MYNAM ": %s: mptscsih_bus_reset: "
- "Bus reset was successful! (sc=%p)n",
- hd->ioc->name,
- SCpnt));
- return SUCCESS;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_host_reset - Perform a SCSI host adapter RESET!
- * new_eh variant
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- *
- * (linux Scsi_Host_Template.eh_host_reset_handler routine)
- *
- * Returns SUCCESS or FAILED.
- */
- int
- mptscsih_host_reset(Scsi_Cmnd *SCpnt)
- {
- MPT_SCSI_HOST * hd;
- int status = SUCCESS;
- /* If we can't locate the host to reset, then we failed. */
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL){
- nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: "
- "Can't locate host! (sc=%p)n",
- SCpnt ) );
- return FAILED;
- }
- printk(KERN_WARNING MYNAM ": %s: >> Attempting host reset! (sc=%p)n",
- hd->ioc->name, SCpnt);
- printk(KERN_WARNING MYNAM ": %s: IOs outstanding = %dn",
- hd->ioc->name, atomic_read(&queue_depth));
- /* If our attempts to reset the host failed, then return a failed
- * status. The host will be taken off line by the SCSI mid-layer.
- */
- if (mpt_HardResetHandler(hd->ioc, CAN_SLEEP) < 0){
- status = FAILED;
- } else {
- /* Make sure TM pending is cleared and TM state is set to
- * NONE.
- */
- hd->tmPending = 0;
- hd->tmState = TM_STATE_NONE;
- }
- nehprintk( ( KERN_WARNING MYNAM ": mptscsih_host_reset: "
- "Status = %sn",
- (status == SUCCESS) ? "SUCCESS" : "FAILED" ) );
- return status;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_tm_pending_wait - wait for pending task management request to
- * complete.
- * @hd: Pointer to MPT host structure.
- *
- * Returns {SUCCESS,FAILED}.
- */
- static int
- mptscsih_tm_pending_wait(MPT_SCSI_HOST * hd)
- {
- unsigned long flags;
- int loop_count = 60 * 4; /* Wait 60 seconds */
- int status = FAILED;
- do {
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- if (hd->tmState == TM_STATE_NONE) {
- hd->tmState = TM_STATE_IN_PROGRESS;
- hd->tmPending = 1;
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- status = SUCCESS;
- break;
- }
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- } while (--loop_count);
- return status;
- }
- #else /* MPT_SCSI old EH stuff... */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_old_abort - Abort linux Scsi_Cmnd routine
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO to be aborted
- *
- * (linux Scsi_Host_Template.abort routine)
- *
- * Returns SCSI_ABORT_{SUCCESS,BUSY,PENDING}.
- */
- int
- mptscsih_old_abort(Scsi_Cmnd *SCpnt)
- {
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- struct tq_struct *ptaskfoo;
- unsigned long flags;
- int scpnt_idx;
- printk(KERN_WARNING MYNAM ": OldAbort scheduling ABORT SCSI IO (sc=%p)n", (void *) SCpnt);
- printk(KERN_WARNING " IOs outstanding = %dn", atomic_read(&queue_depth));
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
- printk(KERN_WARNING " WARNING - OldAbort, NULL hostdata ptr!!n");
- SCpnt->result = DID_ERROR << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_ABORT_NOT_RUNNING;
- }
- if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
- /* Cmd not found in ScsiLookup.
- * If found in doneQ, delete from Q.
- * Do OS callback.
- */
- search_doneQ_for_cmd(hd, SCpnt);
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_ABORT_SUCCESS;
- } else {
- /* If this command is pended, then timeout/hang occurred
- * during DV. Force bus reset by posting command to F/W
- * and then following up with the reset request.
- */
- if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
- mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
- post_pendingQ_commands(hd);
- }
- }
- /*
- * Check to see if there's already an ABORT queued for this guy.
- */
- mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
- if (mf != NULL) {
- dtmprintk((MYIOC_s_INFO_FMT "OldAbort:Abort Task PENDING cmd (%p) taskQ depth (%d)n",
- hd->ioc->name, SCpnt, hd->taskQcnt));
- return SCSI_ABORT_PENDING;
- }
- // SJR - CHECKME - Can we avoid this here?
- // (mpt_HardResetHandler has this check...)
- /* If IOC is reloading FW, return PENDING.
- */
- spin_lock_irqsave(&hd->ioc->diagLock, flags);
- if (hd->ioc->diagPending) {
- spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
- return SCSI_ABORT_PENDING;
- }
- spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
- /* If there are no message frames what should we do?
- */
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
- printk((KERN_WARNING " WARNING - OldAbort, no msg frames!!n"));
- /* We are out of message frames!
- * Call the reset handler to do a FW reload.
- */
- printk((KERN_WARNING " Reloading Firmware!!n"));
- if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
- printk((KERN_WARNING " Firmware Reload FAILED!!n"));
- }
- return SCSI_ABORT_PENDING;
- }
- /*
- * Add ourselves to (end of) taskQ .
- * Check to see if our _bh is running. If NOT, schedule it.
- */
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
- hd->taskQcnt++;
- atomic_inc(&mpt_taskQdepth);
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- spin_lock_irqsave(&mytaskQ_lock, flags);
- /* Save the original SCpnt mf pointer
- */
- SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx);
- /* For the time being, force bus reset on any abort
- * requests for the 1030 FW.
- */
- if (hd->is_spi)
- mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
- else
- mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
- mf->u.frame.linkage.argp1 = SCpnt;
- mf->u.frame.linkage.argp2 = (void *) hd;
- dtmprintk((MYIOC_s_INFO_FMT "OldAbort:_bh_handler state (%d) taskQ count (%d)n",
- hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt));
- if (! mytaskQ_bh_active) {
- mytaskQ_bh_active = 1;
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- /*
- * Oh how cute, no alloc/free/mgmt needed if we use
- * (bottom/unused portion of) MPT request frame.
- */
- ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
- ptaskfoo->sync = 0;
- ptaskfoo->routine = mptscsih_taskmgmt_bh;
- ptaskfoo->data = SCpnt;
- SCHEDULE_TASK(ptaskfoo);
- } else {
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- }
- return SCSI_ABORT_PENDING;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_old_reset - Perform a SCSI BUS_RESET!
- * @SCpnt: Pointer to Scsi_Cmnd structure, IO which reset is due to
- * @reset_flags: (not used?)
- *
- * (linux Scsi_Host_Template.reset routine)
- *
- * Returns SCSI_RESET_{SUCCESS,PUNT,PENDING}.
- */
- int
- mptscsih_old_reset(Scsi_Cmnd *SCpnt, unsigned int reset_flags)
- {
- MPT_SCSI_HOST *hd;
- MPT_FRAME_HDR *mf;
- struct tq_struct *ptaskfoo;
- unsigned long flags;
- int scpnt_idx;
- printk(KERN_WARNING MYNAM ": OldReset scheduling BUS_RESET (sc=%p)n", (void *) SCpnt);
- printk(KERN_WARNING " IOs outstanding = %dn", atomic_read(&queue_depth));
- if ((hd = (MPT_SCSI_HOST *) SCpnt->host->hostdata) == NULL) {
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_RESET_SUCCESS;
- }
- if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(SCpnt)) < 0) {
- /* Cmd not found in ScsiLookup.
- * If found in doneQ, delete from Q.
- * Do OS callback.
- */
- search_doneQ_for_cmd(hd, SCpnt);
- SCpnt->result = DID_RESET << 16;
- SCpnt->scsi_done(SCpnt);
- return SCSI_RESET_SUCCESS;
- } else {
- /* If this command is pended, then timeout/hang occurred
- * during DV. Force bus reset by posting command to F/W
- * and then following up with the reset request.
- */
- if ((mf = mptscsih_search_pendingQ(hd, scpnt_idx)) != NULL) {
- mptscsih_put_msgframe(ScsiDoneCtx, hd->ioc->id, mf);
- post_pendingQ_commands(hd);
- }
- }
- /*
- * Check to see if there's an ABORT_TASK queued for this guy.
- * If so, delete.
- */
- search_taskQ(1, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK);
- /*
- * Check to see if there's already a BUS_RESET queued for this guy.
- */
- mf = search_taskQ(0, SCpnt, hd, MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS);
- if (mf != NULL) {
- dtmprintk((MYIOC_s_INFO_FMT "OldReset:Reset Task PENDING cmd (%p) taskQ depth (%d)n",
- hd->ioc->name, SCpnt, hd->taskQcnt));
- return SCSI_RESET_PENDING;
- }
- // SJR - CHECKME - Can we avoid this here?
- // (mpt_HardResetHandler has this check...)
- /* If IOC is reloading FW, return PENDING.
- */
- spin_lock_irqsave(&hd->ioc->diagLock, flags);
- if (hd->ioc->diagPending) {
- spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
- return SCSI_RESET_PENDING;
- }
- spin_unlock_irqrestore(&hd->ioc->diagLock, flags);
- if ((mf = mpt_get_msg_frame(ScsiTaskCtx, hd->ioc->id)) == NULL) {
- /* We are out of message frames!
- * Call the reset handler to do a FW reload.
- */
- printk((KERN_WARNING " Reloading Firmware!!n"));
- if (mpt_HardResetHandler(hd->ioc, NO_SLEEP) < 0) {
- printk((KERN_WARNING " Firmware Reload FAILED!!n"));
- }
- return SCSI_RESET_PENDING;
- }
- /*
- * Add ourselves to (end of) taskQ.
- * Check to see if our _bh is running. If NOT, schedule it.
- */
- spin_lock_irqsave(&hd->ioc->FreeQlock, flags);
- Q_ADD_TAIL(&hd->taskQ, &mf->u.frame.linkage, MPT_FRAME_HDR);
- hd->taskQcnt++;
- atomic_inc(&mpt_taskQdepth);
- spin_unlock_irqrestore(&hd->ioc->FreeQlock, flags);
- dtmprintk((MYIOC_s_INFO_FMT "OldReset: _bh_handler state (%d) taskQ count (%d)n",
- hd->ioc->name, mytaskQ_bh_active, hd->taskQcnt));
- spin_lock_irqsave(&mytaskQ_lock, flags);
- /* Save the original SCpnt mf pointer
- */
- SCpnt->host_scribble = (u8 *) MPT_INDEX_2_MFPTR (hd->ioc, scpnt_idx);
- mf->u.frame.linkage.arg1 = MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS;
- mf->u.frame.linkage.argp1 = SCpnt;
- mf->u.frame.linkage.argp2 = (void *) hd;
- if (! mytaskQ_bh_active) {
- mytaskQ_bh_active = 1;
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- /*
- * Oh how cute, no alloc/free/mgmt needed if we use
- * (bottom/unused portion of) MPT request frame.
- */
- ptaskfoo = (struct tq_struct *) &mptscsih_ptaskfoo;
- ptaskfoo->sync = 0;
- ptaskfoo->routine = mptscsih_taskmgmt_bh;
- ptaskfoo->data = SCpnt;
- SCHEDULE_TASK(ptaskfoo);
- } else {
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- }
- return SCSI_RESET_PENDING;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * mptscsih_taskmgmt_bh - SCSI task mgmt bottom half handler
- * @sc: (unused)
- *
- * This routine (thread) is active whenever there are any outstanding
- * SCSI task management requests for a SCSI host adapter.
- * IMPORTANT! This routine is scheduled therefore should never be
- * running in ISR context. i.e., it's safe to sleep here.
- */
- void
- mptscsih_taskmgmt_bh(void *sc)
- {
- MPT_ADAPTER *ioc;
- Scsi_Cmnd *SCpnt;
- MPT_FRAME_HDR *mf = NULL;
- MPT_SCSI_HOST *hd;
- u32 ctx2abort = 0;
- unsigned long flags;
- int scpnt_idx;
- int did;
- u8 task_type;
- spin_lock_irqsave(&mytaskQ_lock, flags);
- mytaskQ_bh_active = 1;
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- do {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(HZ/4);
- did = 0;
- for (ioc = mpt_adapter_find_first(); ioc != NULL; ioc = mpt_adapter_find_next(ioc)) {
- if (ioc->sh) {
- hd = (MPT_SCSI_HOST *) ioc->sh->hostdata;
- if (hd == NULL) {
- printk(KERN_ERR MYNAM
- ": ERROR - TaskMgmt NULL SCSI Host!"
- "(ioc=%p, sh=%p hd=%p)n",
- (void *) ioc, (void *) ioc->sh, (void *) hd);
- continue;
- }
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- if (Q_IS_EMPTY(&hd->taskQ)) {
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- continue;
- }
- /* If we ever find a non-empty queue,
- * keep the handler alive
- */
- did++;
- /* tmPending is SMP lock-protected */
- if (hd->tmPending || hd->tmPtr) {
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- continue;
- }
- hd->tmPending = 1;
- /* Process this request
- */
- mf = hd->taskQ.head;
- Q_DEL_ITEM(&mf->u.frame.linkage);
- hd->taskQcnt--;
- atomic_dec(&mpt_taskQdepth);
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- SCpnt = (Scsi_Cmnd*)mf->u.frame.linkage.argp1;
- if (SCpnt == NULL) {
- printk(KERN_ERR MYNAM ": ERROR - TaskMgmt has NULL SCpnt! (mf=%p:sc=%p)n",
- (void *) mf, (void *) SCpnt);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- continue;
- }
- /* Get the ScsiLookup index pointer
- * from the SC pointer.
- */
- if (!SCpnt->host_scribble || ((MPT_SCSI_HOST *)SCpnt->host->hostdata != hd)) {
- /* The command associated with the
- * abort/reset request must have
- * completed and this is a stale
- * request. We are done.
- * Free the current MF and continue.
- */
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- continue;
- }
- scpnt_idx = MFPTR_2_MPT_INDEX(hd->ioc, SCpnt->host_scribble);
- if (scpnt_idx != SCPNT_TO_LOOKUP_IDX(SCpnt)) {
- /* Error! this should never happen!!
- */
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- continue;
- }
- task_type = mf->u.frame.linkage.arg1;
- ctx2abort = 0;
- if (task_type == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
- MPT_FRAME_HDR *SCpntMf;
- /*
- * Most important! Set TaskMsgContext to SCpnt's MsgContext!
- * (the IO to be ABORT'd)
- *
- * NOTE: Since we do not byteswap MsgContext, we do not
- * swap it here either. It is an opaque cookie to
- * the controller, so it does not matter. -DaveM
- */
- SCpntMf = (MPT_FRAME_HDR *) SCpnt->host_scribble;
- ctx2abort = SCpntMf->u.frame.hwhdr.msgctxu.MsgContext;
- hd->abortSCpnt = SCpnt;
- printk(KERN_WARNING MYNAM ": Attempting ABORT SCSI IO! (mf=%p:sc=%p)n",
- (void *) mf, (void *) SCpnt);
- }
- /* The TM handler will allocate a new mf,
- * so free the current mf.
- */
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- mf = NULL;
- if (mptscsih_TMHandler(hd, task_type, SCpnt->target, SCpnt->lun, ctx2abort, NO_SLEEP) < 0) {
- /* The TM request failed and the subsequent FW-reload failed!
- * Fatal error case.
- */
- printk(KERN_WARNING MYNAM
- ": WARNING[1] - IOC error processing TaskMgmt request (sc=%p)n", (void *) SCpnt);
- if (hd->ScsiLookup[scpnt_idx] != NULL) {
- atomic_dec(&queue_depth);
- SCpnt->result = DID_SOFT_ERROR << 16;
- MPT_HOST_LOCK(flags);
- SCpnt->scsi_done(SCpnt);
- MPT_HOST_UNLOCK(flags);
- mpt_free_msg_frame(ScsiTaskCtx, hd->ioc->id, mf);
- }
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- hd->abortSCpnt = NULL;
- }
- }
- }
- if (atomic_read(&mpt_taskQdepth) > 0)
- did++;
- } while ( did );
- spin_lock_irqsave(&mytaskQ_lock, flags);
- mytaskQ_bh_active = 0;
- spin_unlock_irqrestore(&mytaskQ_lock, flags);
- return;
- }
- #endif /* } */
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /**
- * mptscsih_taskmgmt_complete - Registered with Fusion MPT base driver
- * @ioc: Pointer to MPT_ADAPTER structure
- * @mf: Pointer to SCSI task mgmt request frame
- * @mr: Pointer to SCSI task mgmt reply frame
- *
- * This routine is called from mptbase.c::mpt_interrupt() at the completion
- * of any SCSI task management request.
- * This routine is registered with the 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_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr)
- {
- SCSITaskMgmtReply_t *pScsiTmReply;
- SCSITaskMgmt_t *pScsiTmReq;
- MPT_SCSI_HOST *hd = NULL;
- unsigned long flags;
- u8 tmType = 0;
- dtmprintk((MYIOC_s_INFO_FMT "SCSI TaskMgmt completed (mf=%p,r=%p)n",
- ioc->name, mf, mr));
- if (ioc->sh) {
- /* Depending on the thread, a timer is activated for
- * the TM request. Delete this timer on completion of TM.
- * Decrement count of outstanding TM requests.
- */
- hd = (MPT_SCSI_HOST *)ioc->sh->hostdata;
- if (hd->tmPtr) {
- del_timer(&hd->TMtimer);
- }
- dtmprintk((MYIOC_s_INFO_FMT "taskQcnt (%d)n",
- ioc->name, hd->taskQcnt));
- } else {
- dtmprintk((MYIOC_s_WARN_FMT "TaskMgmt Complete: NULL Scsi Host Ptrn",
- ioc->name));
- return 1;
- }
- if (mr == NULL) {
- dtmprintk((MYIOC_s_WARN_FMT "ERROR! TaskMgmt Reply: NULL Request %pn",
- ioc->name, mf));
- return 1;
- } else {
- pScsiTmReply = (SCSITaskMgmtReply_t*)mr;
- pScsiTmReq = (SCSITaskMgmt_t*)mf;
- /* Figure out if this was ABORT_TASK, TARGET_RESET, or BUS_RESET! */
- tmType = pScsiTmReq->TaskType;
- dtmprintk((KERN_INFO " TaskType = %d, TerminationCount=%dn",
- tmType, le32_to_cpu(pScsiTmReply->TerminationCount)));
- /* Error? (anything non-zero?) */
- if (*(u32 *)&pScsiTmReply->Reserved2[0]) {
- u16 iocstatus;
- iocstatus = le16_to_cpu(pScsiTmReply->IOCStatus) & MPI_IOCSTATUS_MASK;
- dtmprintk((KERN_INFO " SCSI TaskMgmt (%d) - Oops!n", tmType));
- dtmprintk((KERN_INFO " IOCStatus = %04xhn", iocstatus));
- dtmprintk((KERN_INFO " IOCLogInfo = %08xhn",
- le32_to_cpu(pScsiTmReply->IOCLogInfo)));
- /* clear flags and continue.
- */
- if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK)
- hd->abortSCpnt = NULL;
- #ifdef DROP_TEST
- if (dropMfPtr)
- dropTestBad++;
- #endif
- /* If an internal command is present
- * or the TM failed - reload the FW.
- * FC FW may respond FAILED to an ABORT
- */
- if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
- if ((hd->cmdPtr) ||
- (iocstatus == MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED)) {
- if (mpt_HardResetHandler(ioc, NO_SLEEP) < 0) {
- printk((KERN_WARNING
- " Firmware Reload FAILED!!n"));
- }
- }
- }
- #ifdef MPT_SCSI_USE_NEW_EH
- hd->tmState = TM_STATE_ERROR;
- #endif
- } else {
- dtmprintk((KERN_INFO " SCSI TaskMgmt SUCCESS!n"));
- #ifndef MPT_SCSI_USE_NEW_EH
- if (tmType == MPI_SCSITASKMGMT_TASKTYPE_RESET_BUS) {
- /* clean taskQ - remove tasks associated with
- * completed commands.
- */
- clean_taskQ(hd);
- } else if (tmType == MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK) {
- /* If taskQ contains another request
- * for this SCpnt, delete this request.
- */
- search_taskQ_for_cmd(hd->abortSCpnt, hd);
- }
- #endif
- hd->numTMrequests--;
- hd->abortSCpnt = NULL;
- flush_doneQ(hd);
- #ifdef DROP_TEST
- if (dropMfPtr)
- dropTestOK++;
- #endif
- }
- }
- #ifdef DROP_TEST
- mptscsih_flush_drop_test(hd);
- #endif
- #ifndef MPT_SCSI_USE_NEW_EH
- /*
- * Signal to _bh thread that we finished.
- * This IOC can now process another TM command.
- */
- dtmprintk((MYIOC_s_INFO_FMT "taskmgmt_complete: (=%p) done! Num Failed(%d) Task Count (%d)n",
- ioc->name, mf, hd->numTMrequests, hd->taskQcnt));
- #endif
- hd->tmPtr = NULL;
- spin_lock_irqsave(&ioc->FreeQlock, flags);
- hd->tmPending = 0;
- spin_unlock_irqrestore(&ioc->FreeQlock, flags);
- return 1;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * This is anyones guess quite frankly.
- */
- int
- mptscsih_bios_param(Disk * disk, kdev_t dev, int *ip)
- {
- int size;
- size = disk->capacity;
- ip[0] = 64; /* heads */
- ip[1] = 32; /* sectors */
- if ((ip[2] = size >> 11) > 1024) { /* cylinders, test for big disk */
- ip[0] = 255; /* heads */
- ip[1] = 63; /* sectors */
- ip[2] = size / (255 * 63); /* cylinders */
- }
- return 0;
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * OS entry point to adjust the queue_depths on a per-device basis.
- * 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.
- */
- void
- mptscsih_select_queue_depths(struct Scsi_Host *sh, Scsi_Device *sdList)
- {
- struct scsi_device *device;
- VirtDevice *pTarget;
- MPT_SCSI_HOST *hd;
- int ii, max;
- for (device = sdList; device != NULL; device = device->next) {
- if (device->host != sh)
- continue;
- hd = (MPT_SCSI_HOST *) sh->hostdata;
- if (hd == NULL)
- continue;
- if (hd->Targets != NULL) {
- if (hd->is_spi)
- max = MPT_MAX_SCSI_DEVICES;
- else
- max = MPT_MAX_FC_DEVICES;
- for (ii=0; ii < max; ii++) {
- pTarget = hd->Targets[ii];
- if (pTarget && !(pTarget->tflags & MPT_TARGET_FLAGS_Q_YES)) {
- device->queue_depth = 1;
- }
- }
- }
- }
- }
- /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
- /*
- * 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: