cregit-Linux how code gets into the kernel

Release 4.14 drivers/message/fusion/mptscsih.c

/*
 *  linux/drivers/message/fusion/mptscsih.c
 *      For use with LSI PCI chip/adapter(s)
 *      running LSI Fusion MPT (Message Passing Technology) firmware.
 *
 *  Copyright (c) 1999-2008 LSI Corporation
 *  (mailto:DL-MPTFusionLinux@lsi.com)
 *
 */
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
    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/kernel.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/kdev_t.h>
#include <linux/blkdev.h>
#include <linux/delay.h>	/* for mdelay */
#include <linux/interrupt.h>	/* needed for in_interrupt() proto */
#include <linux/reboot.h>	/* notifier code */
#include <linux/workqueue.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
#include <scsi/scsi_dbg.h>

#include "mptbase.h"
#include "mptscsih.h"
#include "lsi/mpi_log_sas.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");

MODULE_VERSION(my_VERSION);

/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
 *  Other private/forward protos...
 */
struct scsi_cmnd	*mptscsih_get_scsi_lookup(MPT_ADAPTER *ioc, int i);
static struct scsi_cmnd * mptscsih_getclear_scsi_lookup(MPT_ADAPTER *ioc, int i);
static void	mptscsih_set_scsi_lookup(MPT_ADAPTER *ioc, int i, struct scsi_cmnd *scmd);
static int	SCPNT_TO_LOOKUP_IDX(MPT_ADAPTER *ioc, struct scsi_cmnd *scmd);
int		mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static void	mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq);
int		mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);

static int	mptscsih_AddSGE(MPT_ADAPTER *ioc, struct scsi_cmnd *SCpnt,
				 SCSIIORequest_t *pReq, int req_idx);
static void	mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx);
static void	mptscsih_copy_sense_data(struct scsi_cmnd *sc, MPT_SCSI_HOST *hd, MPT_FRAME_HDR *mf, SCSIIOReply_t *pScsiReply);

int	mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id,
		u64 lun, int ctx2abort, ulong timeout);

int		mptscsih_ioc_reset(MPT_ADAPTER *ioc, int post_reset);
int		mptscsih_event_process(MPT_ADAPTER *ioc, EventNotificationReply_t *pEvReply);

void
mptscsih_taskmgmt_response_code(MPT_ADAPTER *ioc, u8 response_code);
static int	mptscsih_get_completion_code(MPT_ADAPTER *ioc,
		MPT_FRAME_HDR *req, MPT_FRAME_HDR *reply);
int		mptscsih_scandv_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *r);
static int	mptscsih_do_cmd(MPT_SCSI_HOST *hd, INTERNAL_CMD *iocmd);
static void	mptscsih_synchronize_cache(MPT_SCSI_HOST *hd, VirtDevice *vdevice);

static int
mptscsih_taskmgmt_reply(MPT_ADAPTER *ioc, u8 type,
				SCSITaskMgmtReply_t *pScsiTmReply);
void 		mptscsih_remove(struct pci_dev *);
void 		mptscsih_shutdown(struct pci_dev *);
#ifdef CONFIG_PM
int 		mptscsih_suspend(struct pci_dev *pdev, pm_message_t state);
int 		mptscsih_resume(struct pci_dev *pdev);
#endif


#define SNS_LEN(scp)	SCSI_SENSE_BUFFERSIZE


/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*
 *      mptscsih_getFreeChainBuffer - Function to get a free chain
 *      from the MPT_SCSI_HOST FreeChainQ.
 *      @ioc: Pointer to MPT_ADAPTER structure
 *      @req_idx: Index of the SCSI IO request frame. (output)
 *
 *      return SUCCESS or FAILED
 */

static inline int mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex) { MPT_FRAME_HDR *chainBuf; unsigned long flags; int rc; int chain_idx; dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "getFreeChainBuffer called\n", ioc->name)); spin_lock_irqsave(&ioc->FreeQlock, flags); if (!list_empty(&ioc->FreeChainQ)) { int offset; chainBuf = list_entry(ioc->FreeChainQ.next, MPT_FRAME_HDR, u.frame.linkage.list); list_del(&chainBuf->u.frame.linkage.list); offset = (u8 *)chainBuf - (u8 *)ioc->ChainBuffer; chain_idx = offset / ioc->req_sz; rc = SUCCESS; dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "getFreeChainBuffer chainBuf=%p ChainBuffer=%p offset=%d chain_idx=%d\n", ioc->name, chainBuf, ioc->ChainBuffer, offset, chain_idx)); } else { rc = FAILED; chain_idx = MPT_HOST_NO_CHAIN; dfailprintk(ioc, printk(MYIOC_s_ERR_FMT "getFreeChainBuffer failed\n", ioc->name)); } spin_unlock_irqrestore(&ioc->FreeQlock, flags); *retIndex = chain_idx; return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Pam Delaney9748.02%220.00%
James Bottomley4019.80%220.00%
Christoph Hellwig2411.88%220.00%
Sathya Prakash M R2411.88%110.00%
Linus Torvalds157.43%110.00%
Alan Cox10.50%110.00%
Eric Moore10.50%110.00%
Total202100.00%10100.00%

/* mptscsih_getFreeChainBuffer() */ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mptscsih_AddSGE - Add a SGE (plus chain buffers) to the * SCSIIORequest_t Message Frame. * @ioc: Pointer to MPT_ADAPTER structure * @SCpnt: Pointer to scsi_cmnd structure * @pReq: Pointer to SCSIIORequest_t structure * * Returns ... */
static int mptscsih_AddSGE(MPT_ADAPTER *ioc, struct 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; u32 RequestNB; 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 = ioc->req_sz; /* Map the data portion, if any. * sges_left = 0 if no data transfer. */ sges_left = scsi_dma_map(SCpnt); if (sges_left < 0) return FAILED; /* Handle the SG case. */ sg = scsi_sglist(SCpnt); 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) / ioc->SGE_size); numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | 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) { /* Get next SG element from the OS */ sg = sg_next(sg); sg_done++; continue; } v2 = sg_dma_address(sg); ioc->add_sge(psge, sgflags | thisxfer, v2); /* Get next SG element from the OS */ sg = sg_next(sg); psge += ioc->SGE_size; sgeOffset += ioc->SGE_size; 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); ioc->add_sge(psge, sgflags | thisxfer, v2); sgeOffset += ioc->SGE_size; 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. */ ioc->add_chain((char *)chainSge, 0, sgeOffset, ioc->ChainBufferDMA + chain_dma_off); } else { /* The current buffer is the original MF * and there is no Chain buffer. */ pReq->ChainOffset = 0; RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03; dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Single Buffer RequestNB=%x, sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); ioc->RequestNB[req_idx] = RequestNB; } } 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(ioc, printk(MYIOC_s_DEBUG_FMT "SG: Chain Required! sg done %d\n", 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 - ioc->SGE_size); 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 += ioc->SGE_size; ioc->add_chain((char *)chainSge, nextChain, sgeOffset, ioc->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); RequestNB = (((sgeOffset - 1) >> ioc->NBShiftFactor) + 1) & 0x03; dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Chain Buffer Needed, RequestNB=%x sgeOffset=%d\n", ioc->name, RequestNB, sgeOffset)); ioc->RequestNB[req_idx] = RequestNB; } sges_left -= sg_done; /* NOTE: psge points to the beginning of the chain element * in current buffer. Get a chain buffer. */ if ((mptscsih_getFreeChainBuffer(ioc, &newIndex)) == FAILED) { dfailprintk(ioc, printk(MYIOC_s_DEBUG_FMT "getFreeChainBuffer FAILED SCSI cmd=%02x (%p)\n", ioc->name, pReq->CDB[0], SCpnt)); return FAILED; } /* Update the tracking arrays. * If chainSge == NULL, update ReqToChain, else ChainToChain */ if (chainSge) { ioc->ChainToChain[chain_idx] = newIndex; } else { ioc->ReqToChain[req_idx] = newIndex; } chain_idx = newIndex; chain_dma_off = 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(ioc, printk(MYIOC_s_DEBUG_FMT " Current buff @ %p (index 0x%x)", ioc->name, psge, req_idx)); /* Start the SGE for the next buffer */ psge = (char *) (ioc->ChainBuffer + chain_dma_off); sgeOffset = 0; sg_done = 0; dsgprintk(ioc, printk(MYIOC_s_DEBUG_FMT " Chain buff @ %p (index 0x%x)\n", ioc->name, psge, chain_idx)); /* Start the SGE for the next buffer */ goto nextSGEset; } return SUCCESS; }

Contributors

PersonTokensPropCommitsCommitProp
Pam Delaney50264.44%215.38%
James Bottomley10112.97%17.69%
Sathya Prakash M R496.29%17.69%
Linus Torvalds425.39%17.69%
Kashyap Desai324.11%215.38%
Christoph Hellwig202.57%215.38%
Eric Moore101.28%17.69%
Jens Axboe101.28%17.69%
Andi Kleen81.03%17.69%
FUJITA Tomonori50.64%17.69%
Total779100.00%13100.00%

/* mptscsih_AddSGE() */
static void mptscsih_issue_sep_command(MPT_ADAPTER *ioc, VirtTarget *vtarget, U32 SlotStatus) { MPT_FRAME_HDR *mf; SEPRequest_t *SEPMsg; if (ioc->bus_type != SAS) return; /* Not supported for hidden raid components */ if (vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) return; if ((mf = mpt_get_msg_frame(ioc->InternalCtx, ioc)) == NULL) { dfailprintk(ioc, printk(MYIOC_s_WARN_FMT "%s: no msg frames!!\n", ioc->name,__func__)); return; } SEPMsg = (SEPRequest_t *)mf; SEPMsg->Function = MPI_FUNCTION_SCSI_ENCLOSURE_PROCESSOR; SEPMsg->Bus = vtarget->channel; SEPMsg->TargetID = vtarget->id; SEPMsg->Action = MPI_SEP_REQ_ACTION_WRITE_STATUS; SEPMsg->SlotStatus = SlotStatus; devtverboseprintk(ioc, printk(MYIOC_s_DEBUG_FMT "Sending SEP cmd=%x channel=%d id=%d\n", ioc->name, SlotStatus, SEPMsg->Bus, SEPMsg->TargetID)); mpt_put_msg_frame(ioc->DoneCtx, ioc, mf); }

Contributors

PersonTokensPropCommitsCommitProp
Eric Moore14188.68%360.00%
Sathya Prakash M R1710.69%120.00%
Harvey Harrison10.63%120.00%
Total159100.00%5100.00%

#ifdef CONFIG_FUSION_LOGGING /** * mptscsih_info_scsiio - debug print info on reply frame * @ioc: Pointer to MPT_ADAPTER structure * @sc: original scsi cmnd pointer * @pScsiReply: Pointer to MPT reply frame * * MPT_DEBUG_REPLY needs to be enabled to obtain this info * * Refer to lsi/mpi.h. **/
static void mptscsih_info_scsiio(MPT_ADAPTER *ioc, struct scsi_cmnd *sc, SCSIIOReply_t * pScsiReply) { char *desc = NULL; char *desc1 = NULL; u16 ioc_status; u8 skey, asc, ascq; ioc_status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; switch (ioc_status) { case MPI_IOCSTATUS_SUCCESS: desc = "success"; break; case MPI_IOCSTATUS_SCSI_INVALID_BUS: desc = "invalid bus"; break; case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: desc = "invalid target_id"; break; case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: desc = "device not there"; break; case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: desc = "data overrun"; break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: desc = "data underrun"; break; case MPI_IOCSTATUS_SCSI_IO_DATA_ERROR: desc = "I/O data error"; break; case MPI_IOCSTATUS_SCSI_PROTOCOL_ERROR: desc = "protocol error"; break; case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: desc = "task terminated"; break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: desc = "residual mismatch"; break; case MPI_IOCSTATUS_SCSI_TASK_MGMT_FAILED: desc = "task management failed"; break; case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: desc = "IOC terminated"; break; case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: desc = "ext terminated"; break; default: desc = ""; break; } switch (pScsiReply->SCSIStatus) { case MPI_SCSI_STATUS_SUCCESS: desc1 = "success"; break; case MPI_SCSI_STATUS_CHECK_CONDITION: desc1 = "check condition"; break; case MPI_SCSI_STATUS_CONDITION_MET: desc1 = "condition met"; break; case MPI_SCSI_STATUS_BUSY: desc1 = "busy"; break; case MPI_SCSI_STATUS_INTERMEDIATE: desc1 = "intermediate"; break; case MPI_SCSI_STATUS_INTERMEDIATE_CONDMET: desc1 = "intermediate condmet"; break; case MPI_SCSI_STATUS_RESERVATION_CONFLICT: desc1 = "reservation conflict"; break; case MPI_SCSI_STATUS_COMMAND_TERMINATED: desc1 = "command terminated"; break; case MPI_SCSI_STATUS_TASK_SET_FULL: desc1 = "task set full"; break; case MPI_SCSI_STATUS_ACA_ACTIVE: desc1 = "aca active"; break; case MPI_SCSI_STATUS_FCPEXT_DEVICE_LOGGED_OUT: desc1 = "fcpext device logged out"; break; case MPI_SCSI_STATUS_FCPEXT_NO_LINK: desc1 = "fcpext no link"; break; case MPI_SCSI_STATUS_FCPEXT_UNASSIGNED: desc1 = "fcpext unassigned"; break; default: desc1 = ""; break; } scsi_print_command(sc); printk(MYIOC_s_DEBUG_FMT "\tfw_channel = %d, fw_id = %d, lun = %llu\n", ioc->name, pScsiReply->Bus, pScsiReply->TargetID, sc->device->lun); printk(MYIOC_s_DEBUG_FMT "\trequest_len = %d, underflow = %d, " "resid = %d\n", ioc->name, scsi_bufflen(sc), sc->underflow, scsi_get_resid(sc)); printk(MYIOC_s_DEBUG_FMT "\ttag = %d, transfer_count = %d, " "sc->result = %08X\n", ioc->name, le16_to_cpu(pScsiReply->TaskTag), le32_to_cpu(pScsiReply->TransferCount), sc->result); printk(MYIOC_s_DEBUG_FMT "\tiocstatus = %s (0x%04x), " "scsi_status = %s (0x%02x), scsi_state = (0x%02x)\n", ioc->name, desc, ioc_status, desc1, pScsiReply->SCSIStatus, pScsiReply->SCSIState); if (pScsiReply->SCSIState & MPI_SCSI_STATE_AUTOSENSE_VALID) { skey = sc->sense_buffer[2] & 0x0F; asc = sc->sense_buffer[12]; ascq = sc->sense_buffer[13]; printk(MYIOC_s_DEBUG_FMT "\t[sense_key,asc,ascq]: " "[0x%02x,0x%02x,0x%02x]\n", ioc->name, skey, asc, ascq); } /* * Look for + dump FCP ResponseInfo[]! */ if (pScsiReply->SCSIState & MPI_SCSI_STATE_RESPONSE_INFO_VALID && pScsiReply->ResponseInfo) printk(MYIOC_s_DEBUG_FMT "response_info = %08xh\n", ioc->name, le32_to_cpu(pScsiReply->ResponseInfo)); }

Contributors

PersonTokensPropCommitsCommitProp
Sathya Prakash M R30563.54%120.00%
Eric Moore16835.00%240.00%
Kashyap Desai61.25%120.00%
Hannes Reinecke10.21%120.00%
Total480100.00%5100.00%

#endif /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * 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. */
int mptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr) { struct scsi_cmnd *sc; MPT_SCSI_HOST *hd; SCSIIORequest_t *pScsiReq; SCSIIOReply_t *pScsiReply; u16 req_idx, req_idx_MR; VirtDevice *vdevice; VirtTarget *vtarget; hd = shost_priv(ioc->sh); req_idx = le16_to_cpu(mf->u.frame.hwhdr.msgctxu.fld.req_idx); req_idx_MR = (mr != NULL) ? le16_to_cpu(mr->u.frame.hwhdr.msgctxu.fld.req_idx) : req_idx; /* Special case, where already freed message frame is received from * Firmware. It happens with Resetting IOC. * Return immediately. Do not care */ if ((req_idx != req_idx_MR) || (le32_to_cpu(mf->u.frame.linkage.arg1) == 0xdeadbeaf)) return 0; sc = mptscsih_getclear_scsi_lookup(ioc, req_idx); if (sc == NULL) { MPIHeader_t *hdr = (MPIHeader_t *)mf; /* Remark: writeSDP1 will use the ScsiDoneCtx * If a SCSI I/O cmd, device disabled by OS and * completion done. Cannot touch sc struct. Just free mem. */ if (hdr->Function == MPI_FUNCTION_SCSI_IO_REQUEST) printk(MYIOC_s_ERR_FMT "NULL ScsiCmd ptr!\n", ioc->name); mptscsih_freeChainBuffers(ioc, req_idx); return 1; } if ((unsigned char *)mf != sc->host_scribble) { mptscsih_freeChainBuffers(ioc, req_idx); return 1; } if (ioc->bus_type == SAS) { VirtDevice *vdevice = sc->device->hostdata; if (!vdevice || !vdevice->vtarget || vdevice->vtarget->deleted) { sc->result = DID_NO_CONNECT << 16; goto out; } } sc->host_scribble = NULL; sc->result = DID_OK << 16; /* Set default reply as OK */ pScsiReq = (SCSIIORequest_t *) mf; pScsiReply = (SCSIIOReply_t *) mr; if((ioc->facts.MsgVersion >= MPI_VERSION_01_05) && pScsiReply){ dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d,task-tag=%d)\n", ioc->name, mf, mr, sc, req_idx, pScsiReply->TaskTag)); }else{ dmfprintk(ioc, printk(MYIOC_s_DEBUG_FMT "ScsiDone (mf=%p,mr=%p,sc=%p,idx=%d)\n", ioc->name, mf, mr, sc, req_idx)); } if (pScsiReply == NULL) { /* special context reply handling */ ; } else { u32 xfer_cnt; u16 status; u8 scsi_state, scsi_status; u32 log_info; status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; scsi_state = pScsiReply->SCSIState; scsi_status = pScsiReply->SCSIStatus; xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); scsi_set_resid(sc, scsi_bufflen(sc) - xfer_cnt); log_info = le32_to_cpu(pScsiReply->IOCLogInfo); /* * if we get a data underrun indication, yet no data was * transferred and the SCSI status indicates that the * command was never started, change the data underrun * to success */ if (status == MPI_IOCSTATUS_SCSI_DATA_UNDERRUN && xfer_cnt == 0 && (scsi_status == MPI_SCSI_STATUS_BUSY || scsi_status == MPI_SCSI_STATUS_RESERVATION_CONFLICT || scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)) { status = MPI_IOCSTATUS_SUCCESS; } if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) mptscsih_copy_sense_data(sc, hd, mf, pScsiReply); /* * Look for + dump FCP ResponseInfo[]! */ if (scsi_state & MPI_SCSI_STATE_RESPONSE_INFO_VALID && pScsiReply->ResponseInfo) { printk(MYIOC_s_NOTE_FMT "[%d:%d:%d:%llu] " "FCP_ResponseInfo=%08xh\n", ioc->name, sc->device->host->host_no, sc->device->channel, sc->device->id, sc->device->lun, le32_to_cpu(pScsiReply->ResponseInfo)); } switch(status) { case MPI_IOCSTATUS_BUSY: /* 0x0002 */ case MPI_IOCSTATUS_INSUFFICIENT_RESOURCES: /* 0x0006 */ /* CHECKME! * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) * But not: DID_BUS_BUSY lest one risk * killing interrupt handler:-( */ sc->result = SAM_STAT_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! */ if (ioc->bus_type != FC) sc->result = DID_NO_CONNECT << 16; /* else fibre, just stall until rescan event */ else sc->result = DID_REQUEUE << 16; if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) hd->sel_timeout[pScsiReq->TargetID]++; vdevice = sc->device->hostdata; if (!vdevice) break; vtarget = vdevice->vtarget; if (vtarget->tflags & MPT_TARGET_FLAGS_LED_ON) { mptscsih_issue_sep_command(ioc, vtarget, MPI_SEP_REQ_SLOTSTATUS_UNCONFIGURED); vtarget->tflags &= ~MPT_TARGET_FLAGS_LED_ON; } break; case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */ if ( ioc->bus_type == SAS ) { u16 ioc_status = le16_to_cpu(pScsiReply->IOCStatus); if ((ioc_status & MPI_IOCSTATUS_FLAG_LOG_INFO_AVAILABLE) && ((log_info & SAS_LOGINFO_MASK) == SAS_LOGINFO_NEXUS_LOSS)) { VirtDevice *vdevice = sc->device->hostdata; /* flag the device as being in * device removal delay so we can * notify the midlayer to hold off * on timeout eh */ if (vdevice && vdevice-> vtarget && vdevice->vtarget-> raidVolume) printk(KERN_INFO "Skipping Raid Volume" "for inDMD\n"); else if (vdevice && vdevice->vtarget) vdevice->vtarget-> inDMD = 1; sc->result = (DID_TRANSPORT_DISRUPTED << 16); break; } } else if (ioc->bus_type == FC) { /* * The FC IOC may kill a request for variety of * reasons, some of which may be recovered by a * retry, some which are unlikely to be * recovered. Return DID_ERROR instead of * DID_RESET to permit retry of the command, * just not an infinite number of them */ sc->result = DID_ERROR << 16; break; } /* * Allow non-SAS & non-NEXUS_LOSS to drop into below code */ case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */ /* Linux handles an unsolicited DID_RESET better * than an unsolicited DID_ABORT. */ sc->result = DID_RESET << 16; break; case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */ if (ioc->bus_type == FC) sc->result = DID_ERROR << 16; else sc->result = DID_RESET << 16; break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ scsi_set_resid(sc, scsi_bufflen(sc) - xfer_cnt); if((xfer_cnt==0)||(sc->underflow > xfer_cnt)) sc->result=DID_SOFT_ERROR << 16; else /* Sufficient data transfer occurred */ sc->result = (DID_OK << 16) | scsi_status; dreplyprintk(ioc, printk(MYIOC_s_DEBUG_FMT "RESIDUAL_MISMATCH: result=%x on channel=%d id=%d\n", ioc->name, sc->result, sc->device->channel, sc->device->id)); break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ /* * Do upfront check for valid SenseData and give it * precedence! */ sc->result = (DID_OK <<