/* * 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
*/ /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * 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
*/ staticinlineint
mptscsih_getFreeChainBuffer(MPT_ADAPTER *ioc, int *retIndex)
{
MPT_FRAME_HDR *chainBuf; unsignedlong 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;
/* 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)
*/
/* 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;
}
/* 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);
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.
*/
/* 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;
}
/* 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;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * 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;
/* 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;
/* 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);
/* * 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);
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"); elseif (vdevice &&
vdevice->vtarget)
vdevice->vtarget->
inDMD = 1;
sc->result =
(DID_TRANSPORT_DISRUPTED
<< 16); break;
}
} elseif (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
*/
fallthrough;
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_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 << 16) | scsi_status; if (!(scsi_state & MPI_SCSI_STATE_AUTOSENSE_VALID)) {
/* * For an Errata on LSI53C1030 * When the length of request data * and transfer data are different * with result of command (READ or VERIFY), * DID_SOFT_ERROR is set.
*/ if (ioc->bus_type == SPI) { if ((pScsiReq->CDB[0] == READ_6 && ((pScsiReq->CDB[1] & 0x02) == 0)) ||
pScsiReq->CDB[0] == READ_10 ||
pScsiReq->CDB[0] == READ_12 ||
(pScsiReq->CDB[0] == READ_16 &&
((pScsiReq->CDB[1] & 0x02) == 0)) ||
pScsiReq->CDB[0] == VERIFY ||
pScsiReq->CDB[0] == VERIFY_16) { if (scsi_bufflen(sc) !=
xfer_cnt) {
sc->result =
DID_SOFT_ERROR << 16;
printk(KERN_WARNING "Errata" "on LSI53C1030 occurred." "sc->req_bufflen=0x%02x," "xfer_cnt=0x%02x\n",
scsi_bufflen(sc),
xfer_cnt);
}
}
}
if (xfer_cnt < sc->underflow) { if (scsi_status == SAM_STAT_BUSY)
sc->result = SAM_STAT_BUSY; else
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;
} elseif (scsi_state & MPI_SCSI_STATE_TERMINATED) { /* Not real sure here either... */
sc->result = DID_RESET << 16;
}
}
/* Report Queue Full
*/ if (scsi_status == MPI_SCSI_STATUS_TASK_SET_FULL)
mptscsih_report_queue_full(sc, pScsiReply, pScsiReq);
break;
case MPI_IOCSTATUS_SCSI_DATA_OVERRUN: /* 0x0044 */
scsi_set_resid(sc, 0);
fallthrough; case MPI_IOCSTATUS_SCSI_RECOVERED_ERROR: /* 0x0040 */ case MPI_IOCSTATUS_SUCCESS: /* 0x0000 */
sc->result = (DID_OK << 16) | scsi_status; if (scsi_state == 0) {
;
} elseif (scsi_state &
MPI_SCSI_STATE_AUTOSENSE_VALID) {
/* * For potential trouble on LSI53C1030. * (date:2007.xx.) * It is checked whether the length of * request data is equal to * the length of transfer and residual. * MEDIUM_ERROR is set by incorrect data.
*/ if ((ioc->bus_type == SPI) &&
(sc->sense_buffer[2] & 0x20)) {
u32 difftransfer;
difftransfer =
sc->sense_buffer[3] << 24 |
sc->sense_buffer[4] << 16 |
sc->sense_buffer[5] << 8 |
sc->sense_buffer[6]; if (((sc->sense_buffer[3] & 0x80) ==
0x80) && (scsi_bufflen(sc)
!= xfer_cnt)) {
sc->sense_buffer[2] =
MEDIUM_ERROR;
sc->sense_buffer[12] = 0xff;
sc->sense_buffer[13] = 0xff;
printk(KERN_WARNING"Errata" "on LSI53C1030 occurred." "sc->req_bufflen=0x%02x," "xfer_cnt=0x%02x\n" ,
scsi_bufflen(sc),
xfer_cnt);
} if (((sc->sense_buffer[3] & 0x80)
!= 0x80) &&
(scsi_bufflen(sc) !=
xfer_cnt + difftransfer)) {
sc->sense_buffer[2] =
MEDIUM_ERROR;
sc->sense_buffer[12] = 0xff;
sc->sense_buffer[13] = 0xff;
printk(KERN_WARNING "Errata on LSI53C1030 occurred" "sc->req_bufflen=0x%02x," " xfer_cnt=0x%02x," "difftransfer=0x%02x\n",
scsi_bufflen(sc),
xfer_cnt,
difftransfer);
}
}
/* * 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);
} elseif (scsi_state &
(MPI_SCSI_STATE_AUTOSENSE_FAILED | MPI_SCSI_STATE_NO_SCSI_STATUS)
) { /* * What to do?
*/
sc->result = DID_SOFT_ERROR << 16;
} elseif (scsi_state & MPI_SCSI_STATE_TERMINATED) { /* Not real sure here either... */
sc->result = DID_RESET << 16;
} elseif (scsi_state & 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);
/* * 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.
*/ void
mptscsih_flush_running_cmds(MPT_SCSI_HOST *hd)
{
MPT_ADAPTER *ioc = hd->ioc; struct scsi_cmnd *sc;
SCSIIORequest_t *mf = NULL; int ii; int channel, id;
/* * mptscsih_search_running_cmds - Delete any commands associated * with the specified target and lun. Function called only * when a lun is disable by mid-layer. * Do NOT access the referenced scsi_cmnd structure or * members. Will cause either a paging or NULL ptr error. * (BUT, BUT, BUT, the code does reference it! - mdr) * @hd: Pointer to a SCSI HOST structure * @vdevice: per device private data * * Returns: None. * * Called from sdev_destroy.
*/ staticvoid
mptscsih_search_running_cmds(MPT_SCSI_HOST *hd, VirtDevice *vdevice)
{
SCSIIORequest_t *mf = NULL; int ii; struct scsi_cmnd *sc; struct scsi_lun lun;
MPT_ADAPTER *ioc = hd->ioc; unsignedlong flags;
spin_lock_irqsave(&ioc->scsi_lookup_lock, flags); for (ii = 0; ii < ioc->req_depth; ii++) { if ((sc = ioc->ScsiLookup[ii]) != NULL) {
mf = (SCSIIORequest_t *)MPT_INDEX_2_MFPTR(ioc, ii); if (mf == NULL) continue; /* If the device is a hidden raid component, then its * expected that the mf->function will be RAID_SCSI_IO
*/ if (vdevice->vtarget->tflags &
MPT_TARGET_FLAGS_RAID_COMPONENT && mf->Function !=
MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH) continue;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * 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.
*/ staticvoid
mptscsih_report_queue_full(struct scsi_cmnd *sc, SCSIIOReply_t *pScsiReply, SCSIIORequest_t *pScsiReq)
{ long time = jiffies;
MPT_SCSI_HOST *hd;
MPT_ADAPTER *ioc;
if (sc->device == NULL) return; if (sc->device->host == NULL) return; if ((hd = shost_priv(sc->device->host)) == NULL) return;
ioc = hd->ioc; if (time - hd->last_queue_full > 10 * HZ) {
dprintk(ioc, printk(MYIOC_s_WARN_FMT "Device (%d:%d:%llu) reported QUEUE_FULL!\n",
ioc->name, 0, sc->device->id, sc->device->lun));
hd->last_queue_full = time;
}
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ #define ADD_INDEX_LOG(req_ent) do { } while(0)
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mptscsih_qcmd - Primary Fusion MPT SCSI initiator IO start routine. * @SCpnt: Pointer to scsi_cmnd structure * * (linux scsi_host_template.queuecommand routine) * This is the primary SCSI IO start routine. Create a MPI SCSIIORequest * from a linux scsi_cmnd request and send it to the IOC. * * Returns 0. (rtn value discarded by linux scsi mid-layer)
*/ int
mptscsih_qcmd(struct scsi_cmnd *SCpnt)
{
MPT_SCSI_HOST *hd;
MPT_FRAME_HDR *mf;
SCSIIORequest_t *pScsiReq;
VirtDevice *vdevice = SCpnt->device->hostdata;
u32 datalen;
u32 scsictl;
u32 scsidir;
u32 cmd_len; int my_idx; int ii;
MPT_ADAPTER *ioc;
hd = shost_priv(SCpnt->device->host);
ioc = hd->ioc;
/* 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...
*/ if (SCpnt->sc_data_direction == DMA_FROM_DEVICE) {
datalen = scsi_bufflen(SCpnt);
scsidir = MPI_SCSIIO_CONTROL_READ; /* DATA IN (host<--ioc<--dev) */
} elseif (SCpnt->sc_data_direction == DMA_TO_DEVICE) {
datalen = scsi_bufflen(SCpnt);
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.
*/ if ((vdevice->vtarget->tflags & MPT_TARGET_FLAGS_Q_YES) &&
SCpnt->device->tagged_supported)
scsictl = scsidir | MPI_SCSIIO_CONTROL_SIMPLEQ; else
scsictl = scsidir | MPI_SCSIIO_CONTROL_UNTAGGED;
/* Use the above information to set up the message frame
*/
pScsiReq->TargetID = (u8) vdevice->vtarget->id;
pScsiReq->Bus = vdevice->vtarget->channel;
pScsiReq->ChainOffset = 0; if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT)
pScsiReq->Function = MPI_FUNCTION_RAID_SCSI_IO_PASSTHROUGH; else
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(ioc);
int_to_scsilun(SCpnt->device->lun, (struct scsi_lun *)pScsiReq->LUN);
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;
/* Now add the SG list * Always have a SGE even if null length.
*/ if (datalen == 0) { /* Add a NULL SGE */
ioc->add_sge((char *)&pScsiReq->SGL,
MPT_SGE_FLAGS_SSIMPLE_READ | 0,
(dma_addr_t) -1);
} else { /* Add a 32 or 64 bit SGE */ if (mptscsih_AddSGE(ioc, SCpnt, pScsiReq, my_idx) != SUCCESS) goto fail;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * 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.
*/ staticvoid
mptscsih_freeChainBuffers(MPT_ADAPTER *ioc, int req_idx)
{
MPT_FRAME_HDR *chain; unsignedlong flags; int chain_idx; int next;
/* Get the first chain index and reset * tracker state.
*/
chain_idx = ioc->ReqToChain[req_idx];
ioc->ReqToChain[req_idx] = MPT_HOST_NO_CHAIN;
while (chain_idx != MPT_HOST_NO_CHAIN) {
/* Save the next chain buffer index */
next = ioc->ChainToChain[chain_idx];
/* Free this chain buffer and reset * tracker
*/
ioc->ChainToChain[chain_idx] = MPT_HOST_NO_CHAIN;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * mptscsih_IssueTaskMgmt - Generic send Task Management function. * @hd: Pointer to MPT_SCSI_HOST structure * @type: Task Management type * @channel: channel number for task management * @id: Logical Target ID for reset (if appropriate) * @lun: Logical Unit for reset (if appropriate) * @ctx2abort: Context for the task to be aborted (if appropriate) * @timeout: timeout for task management control * * 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, or FAILED. *
**/ int
mptscsih_IssueTaskMgmt(MPT_SCSI_HOST *hd, u8 type, u8 channel, u8 id, u64 lun, int ctx2abort, ulong timeout)
{
MPT_FRAME_HDR *mf;
SCSITaskMgmt_t *pScsiTm; int ii; int retval;
MPT_ADAPTER *ioc = hd->ioc;
u8 issue_hard_reset;
u32 ioc_raw_state; unsignedlong time_count;
/* Task aborts are not supported for hidden raid components.
*/ if (vdevice->vtarget->tflags & MPT_TARGET_FLAGS_RAID_COMPONENT) {
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: hidden raid component (sc=%p)\n",
ioc->name, SCpnt));
SCpnt->result = DID_RESET << 16;
retval = FAILED; goto out;
}
/* Task aborts are not supported for volumes.
*/ if (vdevice->vtarget->raidVolume) {
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: raid volume (sc=%p)\n",
ioc->name, SCpnt));
SCpnt->result = DID_RESET << 16;
retval = FAILED; goto out;
}
/* Find this command
*/ if ((scpnt_idx = SCPNT_TO_LOOKUP_IDX(ioc, SCpnt)) < 0) { /* Cmd not found in ScsiLookup. * Do OS callback.
*/
SCpnt->result = DID_RESET << 16;
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: " "Command not in the active list! (sc=%p)\n", ioc->name,
SCpnt));
retval = SUCCESS; goto out;
}
if (ioc->timeouts < -1)
ioc->timeouts++;
if (mpt_fwfault_debug)
mpt_halt_firmware(ioc);
/* 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(ioc, scpnt_idx);
ctx2abort = mf->u.frame.hwhdr.msgctxu.MsgContext;
retval = mptscsih_IssueTaskMgmt(hd,
MPI_SCSITASKMGMT_TASKTYPE_ABORT_TASK,
vdevice->vtarget->channel,
vdevice->vtarget->id, vdevice->lun,
ctx2abort, mptscsih_get_tm_timeout(ioc));
if (SCPNT_TO_LOOKUP_IDX(ioc, SCpnt) == scpnt_idx) {
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: command still in active list! (sc=%p)\n",
ioc->name, SCpnt));
retval = FAILED;
} else {
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "task abort: command cleared from active list! (sc=%p)\n",
ioc->name, SCpnt));
retval = SUCCESS;
}
if (retval == 0) return SUCCESS; else return FAILED;
}
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * 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(struct scsi_cmnd *SCpnt)
{
MPT_SCSI_HOST * hd; int status = SUCCESS;
MPT_ADAPTER *ioc; int retval;
/* If we can't locate the host to reset, then we failed. */ if ((hd = shost_priv(SCpnt->device->host)) == NULL){
printk(KERN_ERR MYNAM ": host reset: " "Can't locate host! (sc=%p)\n", SCpnt); return FAILED;
}
/* make sure we have no outstanding commands at this stage */
mptscsih_flush_running_cmds(hd);
ioc = hd->ioc;
printk(MYIOC_s_INFO_FMT "attempting host reset! (sc=%p)\n",
ioc->name, SCpnt);
/* 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.
*/
retval = mpt_Soft_Hard_ResetHandler(ioc, CAN_SLEEP); if (retval < 0)
status = FAILED; else
status = SUCCESS;
switch (response_code) { case MPI_SCSITASKMGMT_RSP_TM_COMPLETE:
desc = "The task completed."; break; case MPI_SCSITASKMGMT_RSP_INVALID_FRAME:
desc = "The IOC received an invalid frame status."; break; case MPI_SCSITASKMGMT_RSP_TM_NOT_SUPPORTED:
desc = "The task type is not supported."; break; case MPI_SCSITASKMGMT_RSP_TM_FAILED:
desc = "The requested task failed."; break; case MPI_SCSITASKMGMT_RSP_TM_SUCCEEDED:
desc = "The task completed successfully."; break; case MPI_SCSITASKMGMT_RSP_TM_INVALID_LUN:
desc = "The LUN request is invalid."; break; case MPI_SCSITASKMGMT_RSP_IO_QUEUED_ON_IOC:
desc = "The task is in the IOC queue and has not been sent to target."; break; default:
desc = "unknown"; break;
}
printk(MYIOC_s_INFO_FMT "Response Code(0x%08x): F/W: %s\n",
ioc->name, response_code, desc);
}
EXPORT_SYMBOL(mptscsih_taskmgmt_response_code);
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /** * 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.
**/ int
mptscsih_taskmgmt_complete(MPT_ADAPTER *ioc, MPT_FRAME_HDR *mf,
MPT_FRAME_HDR *mr)
{
dtmprintk(ioc, printk(MYIOC_s_DEBUG_FMT "TaskMgmt completed (mf=%p, mr=%p)\n", ioc->name, mf, mr));
/* Search IOC page 3 to determine if this is hidden physical disk *
*/ int
mptscsih_is_phys_disk(MPT_ADAPTER *ioc, u8 channel, u8 id)
{ struct inactive_raid_component_info *component_info; int i, j;
RaidPhysDiskPage1_t *phys_disk; int rc = 0; int num_paths;
if (!ioc->raid_data.pIocPg3) goto out; for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) { if ((id == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskID) &&
(channel == ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskBus)) {
rc = 1; goto out;
}
}
if (ioc->bus_type != SAS) goto out;
/* * Check if dual path
*/ for (i = 0; i < ioc->raid_data.pIocPg3->NumPhysDisks; i++) {
num_paths = mpt_raid_phys_disk_get_num_paths(ioc,
ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum); if (num_paths < 2) continue;
phys_disk = kzalloc(offsetof(RaidPhysDiskPage1_t, Path) +
(num_paths * sizeof(RAID_PHYS_DISK1_PATH)), GFP_KERNEL); if (!phys_disk) continue; if ((mpt_raid_phys_disk_pg1(ioc,
ioc->raid_data.pIocPg3->PhysDisk[i].PhysDiskNum,
phys_disk))) {
kfree(phys_disk); continue;
} for (j = 0; j < num_paths; j++) { if ((phys_disk->Path[j].Flags &
MPI_RAID_PHYSDISK1_FLAG_INVALID)) continue; if ((phys_disk->Path[j].Flags &
MPI_RAID_PHYSDISK1_FLAG_BROKEN)) continue; if ((id == phys_disk->Path[j].PhysDiskID) &&
(channel == phys_disk->Path[j].PhysDiskBus)) {
rc = 1;
kfree(phys_disk); goto out;
}
}
kfree(phys_disk);
}
/* * Check inactive list for matching phys disks
*/ if (list_empty(&ioc->raid_data.inactive_list)) goto out;
/* * OS entry point to allow for host driver to free allocated memory * Called if no device present or device being unloaded
*/ void
mptscsih_sdev_destroy(struct scsi_device *sdev)
{ struct Scsi_Host *host = sdev->host;
MPT_SCSI_HOST *hd = shost_priv(host);
VirtTarget *vtarget;
VirtDevice *vdevice; struct scsi_target *starget;
/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ /* * mptscsih_change_queue_depth - This function will set a devices queue depth * @sdev: per scsi_device pointer
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5
¤ Dauer der Verarbeitung: 0.24 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.