📄 mptscsih.c
字号:
/* * 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. */ if ( (sges_left = SCpnt->use_sg) ) { if ( (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))) == 0 ) return FAILED; } else if (SCpnt->request_bufflen) { dma_addr_t buf_dma_addr; scPrivate *my_priv; buf_dma_addr = pci_map_single(hd->ioc->pcidev, SCpnt->request_buffer, SCpnt->request_bufflen, scsi_to_pci_dma_dir(SCpnt->sc_data_direction)); /* We hide it here for later unmap. */ my_priv = (scPrivate *) &SCpnt->SCp; my_priv->p1 = (void *)(ulong) buf_dma_addr; dsgprintk((MYIOC_s_INFO_FMT "SG: non-SG for %p, len=%d\n", hd->ioc->name, SCpnt, SCpnt->request_bufflen)); mptscsih_add_sge((char *) &pReq->SGL, 0xD1000000|MPT_SGE_FLAGS_ADDRESSING|sgdir|SCpnt->request_bufflen, buf_dma_addr); return SUCCESS; } /* Handle the SG case. */ sg = (struct scatterlist *) SCpnt->request_buffer; sg_done = 0; sgeOffset = sizeof(SCSIIORequest_t) - sizeof(SGE_IO_UNION); chainSge = NULL; /* Prior to entering this loop - the following must be set * current MF: sgeOffset (bytes) * chainSge (Null if original MF is not a chain buffer) * sg_done (num SGE done for this MF) */nextSGEset: numSgeSlots = ((frm_sz - sgeOffset) / (sizeof(u32) + sizeof(dma_addr_t)) ); numSgeThisFrame = (sges_left < numSgeSlots) ? sges_left : numSgeSlots; sgflags = MPT_SGE_FLAGS_SIMPLE_ELEMENT | MPT_SGE_FLAGS_ADDRESSING | sgdir; /* Get first (num - 1) SG elements * Skip any SG entries with a length of 0 * NOTE: at finish, sg and psge pointed to NEXT data/location positions */ for (ii=0; ii < (numSgeThisFrame-1); ii++) { thisxfer = sg_dma_len(sg); if (thisxfer == 0) { sg ++; /* Get next SG element from the OS */ sg_done++; continue; } v2 = sg_dma_address(sg); mptscsih_add_sge(psge, sgflags | thisxfer, v2); sg++; /* Get next SG element from the OS */ psge += (sizeof(u32) + sizeof(dma_addr_t)); sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); sg_done++; } if (numSgeThisFrame == sges_left) { /* Add last element, end of buffer and end of list flags. */ sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT | MPT_SGE_FLAGS_END_OF_BUFFER | MPT_SGE_FLAGS_END_OF_LIST; /* Add last SGE and set termination flags. * Note: Last SGE may have a length of 0 - which should be ok. */ thisxfer = sg_dma_len(sg); v2 = sg_dma_address(sg); mptscsih_add_sge(psge, sgflags | thisxfer, v2); /* sg++; psge += (sizeof(u32) + sizeof(dma_addr_t)); */ sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); sg_done++; if (chainSge) { /* The current buffer is a chain buffer, * but there is not another one. * Update the chain element * Offset and Length fields. */ mptscsih_add_chain((char *)chainSge, 0, sgeOffset, hd->ChainBufferDMA + chain_dma_off); } else { /* The current buffer is the original MF * and there is no Chain buffer. */ pReq->ChainOffset = 0; } } else { /* At least one chain buffer is needed. * Complete the first MF * - last SGE element, set the LastElement bit * - set ChainOffset (words) for orig MF * (OR finish previous MF chain buffer) * - update MFStructPtr ChainIndex * - Populate chain element * Also * Loop until done. */ dsgprintk((MYIOC_s_INFO_FMT "SG: Chain Required! sg done %d\n", hd->ioc->name, sg_done)); /* Set LAST_ELEMENT flag for last non-chain element * in the buffer. Since psge points at the NEXT * SGE element, go back one SGE element, update the flags * and reset the pointer. (Note: sgflags & thisxfer are already * set properly). */ if (sg_done) { u32 *ptmp = (u32 *) (psge - (sizeof(u32) + sizeof(dma_addr_t))); sgflags = le32_to_cpu(*ptmp); sgflags |= MPT_SGE_FLAGS_LAST_ELEMENT; *ptmp = cpu_to_le32(sgflags); } if (chainSge) { /* The current buffer is a chain buffer. * chainSge points to the previous Chain Element. * Update its chain element Offset and Length (must * include chain element size) fields. * Old chain element is now complete. */ u8 nextChain = (u8) (sgeOffset >> 2); sgeOffset += (sizeof(u32) + sizeof(dma_addr_t)); mptscsih_add_chain((char *)chainSge, nextChain, sgeOffset, hd->ChainBufferDMA + chain_dma_off); } else { /* The original MF buffer requires a chain buffer - * set the offset. * Last element in this MF is a chain element. */ pReq->ChainOffset = (u8) (sgeOffset >> 2); } sges_left -= sg_done; /* NOTE: psge points to the beginning of the chain element * in current buffer. Get a chain buffer. */ if ((mptscsih_getFreeChainBuffer(hd, &newIndex)) == FAILED) return FAILED; /* Update the tracking arrays. * If chainSge == NULL, update ReqToChain, else ChainToChain */ if (chainSge) { hd->ChainToChain[chain_idx] = newIndex; } else { hd->ReqToChain[req_idx] = newIndex; } chain_idx = newIndex; chain_dma_off = hd->ioc->req_sz * chain_idx; /* Populate the chainSGE for the current buffer. * - Set chain buffer pointer to psge and fill * out the Address and Flags fields. */ chainSge = (char *) psge; dsgprintk((KERN_INFO " Current buff @ %p (index 0x%x)", psge, req_idx)); /* Start the SGE for the next buffer */ psge = (char *) (hd->ChainBuffer + chain_dma_off); sgeOffset = 0; sg_done = 0; dsgprintk((KERN_INFO " Chain buff @ %p (index 0x%x)\n", psge, chain_idx)); /* Start the SGE for the next buffer */ goto nextSGEset; } return SUCCESS;} /* mptscsih_AddSGE() *//*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*//* * mptscsih_io_done - Main SCSI IO callback routine registered to * Fusion MPT (base) driver * @ioc: Pointer to MPT_ADAPTER structure * @mf: Pointer to original MPT request frame * @r: Pointer to MPT reply frame (NULL if TurboReply) * * This routine is called from mpt.c::mpt_interrupt() at the completion * of any SCSI IO request. * This routine is registered with the Fusion MPT (base) driver at driver * load/init time via the mpt_register() API call. * * Returns 1 indicating alloc'd request frame ptr should be freed. */static intmptscsih_io_done(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf, MPT_FRAME_HDR *mr){ Scsi_Cmnd *sc; MPT_SCSI_HOST *hd; SCSIIORequest_t *pScsiReq; SCSIIOReply_t *pScsiReply;#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0) unsigned long flags;#endif u16 req_idx; hd = (MPT_SCSI_HOST *) ioc->sh->hostdata; 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; /* 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(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)); sc->result = DID_OK << 16; /* Set default reply as OK */ pScsiReq = (SCSIIORequest_t *) mf; pScsiReply = (SCSIIOReply_t *) mr;#ifdef MPTSCSIH_DBG_TIMEOUT if (ioc->timeout_cnt > 0) { int ii, left = 0; for (ii=0; ii < 8; ii++) { if (sc == foo_to[ii]) { printk(MYIOC_s_INFO_FMT "complete (%p, %ld)\n", ioc->name, sc, jiffies); foo_to[ii] = NULL; } if (foo_to[ii] != NULL) left++; } if (left == 0) { ioc->timeout_maxcnt = 0; ioc->timeout_cnt = 0; } }#endif if (pScsiReply == NULL) { /* special context reply handling */ /* If regular Inquiry cmd - save inquiry data */ if (pScsiReq->CDB[0] == INQUIRY) { int dlen; dlen = le32_to_cpu(pScsiReq->DataLength); mptscsih_initTarget(hd, sc->channel, sc->target, pScsiReq->LUN[1], sc->buffer, dlen); } } else { u32 xfer_cnt; u16 status; u8 scsi_state, scsi_status; status = le16_to_cpu(pScsiReply->IOCStatus) & MPI_IOCSTATUS_MASK; scsi_state = pScsiReply->SCSIState; dprintk((KERN_NOTICE " Uh-Oh! (%d:%d:%d) mf=%p, mr=%p, sc=%p\n", ioc->id, pScsiReq->TargetID, pScsiReq->LUN[1], mf, mr, sc)); dprintk((KERN_NOTICE " IOCStatus=%04xh, SCSIState=%02xh" ", SCSIStatus=%02xh, IOCLogInfo=%08xh\n", status, scsi_state, pScsiReply->SCSIStatus, le32_to_cpu(pScsiReply->IOCLogInfo))); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) copy_sense_data(sc, hd, mf, pScsiReply); switch(status) { case MPI_IOCSTATUS_BUSY: /* 0x0002 */ /* CHECKME! * Maybe: DRIVER_BUSY | SUGGEST_RETRY | DID_SOFT_ERROR (retry) * But not: DID_BUS_BUSY lest one risk * killing interrupt handler:-( */ sc->result = STS_BUSY; break; case MPI_IOCSTATUS_SCSI_INVALID_BUS: /* 0x0041 */ case MPI_IOCSTATUS_SCSI_INVALID_TARGETID: /* 0x0042 */ sc->result = DID_BAD_TARGET << 16; break; case MPI_IOCSTATUS_SCSI_DEVICE_NOT_THERE: /* 0x0043 */ /* Spoof to SCSI Selection Timeout! */ sc->result = DID_NO_CONNECT << 16; if (hd->sel_timeout[pScsiReq->TargetID] < 0xFFFF) hd->sel_timeout[pScsiReq->TargetID]++; break; case MPI_IOCSTATUS_SCSI_TASK_TERMINATED: /* 0x0048 */#ifndef MPT_SCSI_USE_NEW_EH search_taskQ_for_cmd(sc, hd);#endif /* Linux handles an unsolicited DID_RESET better * than an unsolicited DID_ABORT. */ sc->result = DID_RESET << 16; /* GEM Workaround. */ if (hd->is_spi) mptscsih_no_negotiate(hd, sc->target); break; case MPI_IOCSTATUS_SCSI_IOC_TERMINATED: /* 0x004B */ case MPI_IOCSTATUS_SCSI_EXT_TERMINATED: /* 0x004C */#ifndef MPT_SCSI_USE_NEW_EH search_taskQ_for_cmd(sc, hd);#endif sc->result = DID_RESET << 16; /* GEM Workaround. */ if (hd->is_spi) mptscsih_no_negotiate(hd, sc->target); break; case MPI_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: /* 0x0049 */ sc->result = (DRIVER_SENSE << 24) | (DID_OK << 16) | (CHECK_CONDITION << 1); sc->sense_buffer[0] = 0x70; sc->sense_buffer[2] = NO_SENSE; sc->sense_buffer[12] = 0; sc->sense_buffer[13] = 0; dprintk((KERN_NOTICE "RESIDUAL_MISMATCH: result=%x on id=%d\n", sc->result, sc->target)); break; case MPI_IOCSTATUS_SCSI_DATA_UNDERRUN: /* 0x0045 */ /* * Do upfront check for valid SenseData and give it * precedence! */ scsi_status = pScsiReply->SCSIStatus; sc->result = (DID_OK << 16) | scsi_status; xfer_cnt = le32_to_cpu(pScsiReply->TransferCount); if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { /* Have already saved the status and sense data */ ; } else { if ( (xfer_cnt == 0) || (sc->underflow > xfer_cnt)) { sc->result = DID_SOFT_ERROR << 16; } if (scsi_state & (MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)) { /* What to do? */ sc->result = DID_SOFT_ERROR << 16; } else if (scsi_state & MPI_SCSI_STATE_TERMINATED) { /* Not real sure here either... */ sc->result = DID_RESET << 16; } } /* Give report and update residual count. */ dprintk((KERN_NOTICE " sc->underflow={report ERR if < %02xh bytes xfer'd}\n", sc->underflow)); dprintk((KERN_NOTICE " ActBytesXferd=%02xh\n", xfer_cnt));#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) sc->resid = sc->request_bufflen - xfer_cnt; dprintk((KERN_NOTICE " SET sc->resid=%02xh\n", sc->resid));#endif /* Report Queue Full */ if (scsi_status == 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) && xfer_cnt ) { mptscsih_initTarget(hd, sc->channel, 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; if (scsi_state == 0) { ; } else if (scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID) { /* * If running against circa 200003dd 909 MPT f/w, * may get this (AUTOSENSE_VALID) for actual TASK_SET_FULL * (QUEUE_FULL) returned from device! --> get 0x0000?128 * and with SenseBytes set to 0. */ if (pScsiReply->SCSIStatus == MPI_SCSI_STATUS_TASK_SET_FULL) mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -