📄 mptscsih.c
字号:
} } 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=%d\n", hd->ioc->name, SCpnt, mf, my_idx)); } else { ddvtprintk((MYIOC_s_INFO_FMT "Pending cmd=%p idx %d\n", hd->ioc->name, SCpnt, my_idx)); /* Place this command on the pendingQ if possible */ spin_lock_irqsave(&hd->freedoneQlock, flags); if (!Q_IS_EMPTY(&hd->freeQ)) { buffer = hd->freeQ.head; Q_DEL_ITEM(buffer); /* 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 intmptscsih_AddSGE(MPT_SCSI_HOST *hd, Scsi_Cmnd *SCpnt, SCSIIORequest_t *pReq, int req_idx){ char *psge; char *chainSge; struct scatterlist *sg; int frm_sz; int sges_left, sg_done; int chain_idx = MPT_HOST_NO_CHAIN; int sgeOffset; int numSgeSlots, numSgeThisFrame; u32 sgflags, sgdir, thisxfer = 0; int chain_dma_off = 0; int newIndex; int ii; dma_addr_t v2; sgdir = le32_to_cpu(pReq->Control) & MPI_SCSIIO_CONTROL_DATADIRECTION_MASK; if (sgdir == MPI_SCSIIO_CONTROL_WRITE) { sgdir = MPT_TRANSFER_HOST_TO_IOC; } else { sgdir = MPT_TRANSFER_IOC_TO_HOST; } psge = (char *) &pReq->SGL; frm_sz = hd->ioc->req_sz; /* Map the data portion, if any. * sges_left = 0 if no data transfer. */ sges_left = SCpnt->use_sg; if (SCpnt->use_sg) { sges_left = pci_map_sg(hd->ioc->pcidev, (struct scatterlist *) SCpnt->request_buffer, SCpnt->use_sg, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); } else if (SCpnt->request_bufflen) { dma_addr_t buf_dma_addr; scPrivate *my_priv; buf_dma_addr = pci_map_single(hd->ioc->pcidev, SCpnt->request_buffer, SCpnt->request_bufflen, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); /* We hide it here for later unmap. */ my_priv = (scPrivate *) &SCpnt->SCp; my_priv->p1 = (void *)(ulong) buf_dma_addr; dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", hd->ioc->name, SCpnt, SCpnt->request_bufflen)); 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 %d\n", hd->ioc->name, sg_done)); /* Set LAST_ELEMENT flag for last non-chain element * in the buffer. Since psge points at the NEXT * SGE element, go back one SGE element, update the flags * and reset the pointer. (Note: sgflags & thisxfer are already * set properly). */ if (sg_done) { u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); sgflags = le32_to_cpu(*ptmp); sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; *ptmp = cpu_to_le32(sgflags); } if (chainSge) { /* The current buffer is a chain buffer. * chainSge points to the previous Chain Element. * Update its chain element Offset and Length (must * include chain element size) fields. * Old chain element is now complete. */ u8 nextChain = (u8) (sgeOffset >> 2); sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); mpt_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); } else { /* The original MF buffer requires a chain buffer - * set
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -