/******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * * Copyright (C) 2004-2012 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * * www.emulex.com * * Portions Copyright (C) 2004-2005 Christoph Hellwig * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of version 2 of the GNU General * * Public License as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful. * * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND * * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE * * DISCLAIMED, EXCEPT TO THE EXTENT THAT SUCH DISCLAIMERS ARE HELD * * TO BE LEGALLY INVALID. See the GNU General Public License for * * more details, a copy of which can be found in the file COPYING * * included with this package. * *******************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include "lpfc_version.h" #include "lpfc_hw4.h" #include "lpfc_hw.h" #include "lpfc_sli.h" #include "lpfc_sli4.h" #include "lpfc_nl.h" #include "lpfc_disc.h" #include "lpfc.h" #include "lpfc_scsi.h" #include "lpfc_logmsg.h" #include "lpfc_crtn.h" #include "lpfc_vport.h" #define LPFC_RESET_WAIT 2 #define LPFC_ABORT_WAIT 2 int _dump_buf_done; static char *dif_op_str[] = { "PROT_NORMAL", "PROT_READ_INSERT", "PROT_WRITE_STRIP", "PROT_READ_STRIP", "PROT_WRITE_INSERT", "PROT_READ_PASS", "PROT_WRITE_PASS", }; static char *dif_grd_str[] = { "NO_GUARD", "DIF_CRC", "DIX_IP", }; struct scsi_dif_tuple { __be16 guard_tag; /* Checksum */ __be16 app_tag; /* Opaque storage */ __be32 ref_tag; /* Target LBA or indirect LBA */ }; static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb); static void lpfc_debug_save_data(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) { void *src, *dst; struct scatterlist *sgde = scsi_sglist(cmnd); if (!_dump_buf_data) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9050 BLKGRD: ERROR %s _dump_buf_data is NULL\n", __func__); return; } if (!sgde) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9051 BLKGRD: ERROR: data scatterlist is null\n"); return; } dst = (void *) _dump_buf_data; while (sgde) { src = sg_virt(sgde); memcpy(dst, src, sgde->length); dst += sgde->length; sgde = sg_next(sgde); } } static void lpfc_debug_save_dif(struct lpfc_hba *phba, struct scsi_cmnd *cmnd) { void *src, *dst; struct scatterlist *sgde = scsi_prot_sglist(cmnd); if (!_dump_buf_dif) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9052 BLKGRD: ERROR %s _dump_buf_data is NULL\n", __func__); return; } if (!sgde) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9053 BLKGRD: ERROR: prot scatterlist is null\n"); return; } dst = _dump_buf_dif; while (sgde) { src = sg_virt(sgde); memcpy(dst, src, sgde->length); dst += sgde->length; sgde = sg_next(sgde); } } /** * lpfc_sli4_set_rsp_sgl_last - Set the last bit in the response sge. * @phba: Pointer to HBA object. * @lpfc_cmd: lpfc scsi command object pointer. * * This function is called from the lpfc_prep_task_mgmt_cmd function to * set the last bit in the response sge entry. **/ static void lpfc_sli4_set_rsp_sgl_last(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) { struct sli4_sge *sgl = (struct sli4_sge *)lpfc_cmd->fcp_bpl; if (sgl) { sgl += 1; sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); } } /** * lpfc_update_stats - Update statistical data for the command completion * @phba: Pointer to HBA object. * @lpfc_cmd: lpfc scsi command object pointer. * * This function is called when there is a command completion and this * function updates the statistical data for the command completion. **/ static void lpfc_update_stats(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) { struct lpfc_rport_data *rdata = lpfc_cmd->rdata; struct lpfc_nodelist *pnode = rdata->pnode; struct scsi_cmnd *cmd = lpfc_cmd->pCmd; unsigned long flags; struct Scsi_Host *shost = cmd->device->host; struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; unsigned long latency; int i; if (cmd->result) return; latency = jiffies_to_msecs((long)jiffies - (long)lpfc_cmd->start_time); spin_lock_irqsave(shost->host_lock, flags); if (!vport->stat_data_enabled || vport->stat_data_blocked || !pnode || !pnode->lat_data || (phba->bucket_type == LPFC_NO_BUCKET)) { spin_unlock_irqrestore(shost->host_lock, flags); return; } if (phba->bucket_type == LPFC_LINEAR_BUCKET) { i = (latency + phba->bucket_step - 1 - phba->bucket_base)/ phba->bucket_step; /* check array subscript bounds */ if (i < 0) i = 0; else if (i >= LPFC_MAX_BUCKET_COUNT) i = LPFC_MAX_BUCKET_COUNT - 1; } else { for (i = 0; i < LPFC_MAX_BUCKET_COUNT-1; i++) if (latency <= (phba->bucket_base + ((1<bucket_step))) break; } pnode->lat_data[i].cmd_count++; spin_unlock_irqrestore(shost->host_lock, flags); } /** * lpfc_send_sdev_queuedepth_change_event - Posts a queuedepth change event * @phba: Pointer to HBA context object. * @vport: Pointer to vport object. * @ndlp: Pointer to FC node associated with the target. * @lun: Lun number of the scsi device. * @old_val: Old value of the queue depth. * @new_val: New value of the queue depth. * * This function sends an event to the mgmt application indicating * there is a change in the scsi device queue depth. **/ static void lpfc_send_sdev_queuedepth_change_event(struct lpfc_hba *phba, struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, uint32_t lun, uint32_t old_val, uint32_t new_val) { struct lpfc_fast_path_event *fast_path_evt; unsigned long flags; fast_path_evt = lpfc_alloc_fast_evt(phba); if (!fast_path_evt) return; fast_path_evt->un.queue_depth_evt.scsi_event.event_type = FC_REG_SCSI_EVENT; fast_path_evt->un.queue_depth_evt.scsi_event.subcategory = LPFC_EVENT_VARQUEDEPTH; /* Report all luns with change in queue depth */ fast_path_evt->un.queue_depth_evt.scsi_event.lun = lun; if (ndlp && NLP_CHK_NODE_ACT(ndlp)) { memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwpn, &ndlp->nlp_portname, sizeof(struct lpfc_name)); memcpy(&fast_path_evt->un.queue_depth_evt.scsi_event.wwnn, &ndlp->nlp_nodename, sizeof(struct lpfc_name)); } fast_path_evt->un.queue_depth_evt.oldval = old_val; fast_path_evt->un.queue_depth_evt.newval = new_val; fast_path_evt->vport = vport; fast_path_evt->work_evt.evt = LPFC_EVT_FASTPATH_MGMT_EVT; spin_lock_irqsave(&phba->hbalock, flags); list_add_tail(&fast_path_evt->work_evt.evt_listp, &phba->work_list); spin_unlock_irqrestore(&phba->hbalock, flags); lpfc_worker_wake_up(phba); return; } /** * lpfc_change_queue_depth - Alter scsi device queue depth * @sdev: Pointer the scsi device on which to change the queue depth. * @qdepth: New queue depth to set the sdev to. * @reason: The reason for the queue depth change. * * This function is called by the midlayer and the LLD to alter the queue * depth for a scsi device. This function sets the queue depth to the new * value and sends an event out to log the queue depth change. **/ int lpfc_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason) { struct lpfc_vport *vport = (struct lpfc_vport *) sdev->host->hostdata; struct lpfc_hba *phba = vport->phba; struct lpfc_rport_data *rdata; unsigned long new_queue_depth, old_queue_depth; old_queue_depth = sdev->queue_depth; scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth); new_queue_depth = sdev->queue_depth; rdata = sdev->hostdata; if (rdata) lpfc_send_sdev_queuedepth_change_event(phba, vport, rdata->pnode, sdev->lun, old_queue_depth, new_queue_depth); return sdev->queue_depth; } /** * lpfc_rampdown_queue_depth - Post RAMP_DOWN_QUEUE event to worker thread * @phba: The Hba for which this call is being executed. * * This routine is called when there is resource error in driver or firmware. * This routine posts WORKER_RAMP_DOWN_QUEUE event for @phba. This routine * posts at most 1 event each second. This routine wakes up worker thread of * @phba to process WORKER_RAM_DOWN_EVENT event. * * This routine should be called with no lock held. **/ void lpfc_rampdown_queue_depth(struct lpfc_hba *phba) { unsigned long flags; uint32_t evt_posted; spin_lock_irqsave(&phba->hbalock, flags); atomic_inc(&phba->num_rsrc_err); phba->last_rsrc_error_time = jiffies; if ((phba->last_ramp_down_time + QUEUE_RAMP_DOWN_INTERVAL) > jiffies) { spin_unlock_irqrestore(&phba->hbalock, flags); return; } phba->last_ramp_down_time = jiffies; spin_unlock_irqrestore(&phba->hbalock, flags); spin_lock_irqsave(&phba->pport->work_port_lock, flags); evt_posted = phba->pport->work_port_events & WORKER_RAMP_DOWN_QUEUE; if (!evt_posted) phba->pport->work_port_events |= WORKER_RAMP_DOWN_QUEUE; spin_unlock_irqrestore(&phba->pport->work_port_lock, flags); if (!evt_posted) lpfc_worker_wake_up(phba); return; } /** * lpfc_rampup_queue_depth - Post RAMP_UP_QUEUE event for worker thread * @phba: The Hba for which this call is being executed. * * This routine post WORKER_RAMP_UP_QUEUE event for @phba vport. This routine * post at most 1 event every 5 minute after last_ramp_up_time or * last_rsrc_error_time. This routine wakes up worker thread of @phba * to process WORKER_RAM_DOWN_EVENT event. * * This routine should be called with no lock held. **/ static inline void lpfc_rampup_queue_depth(struct lpfc_vport *vport, uint32_t queue_depth) { unsigned long flags; struct lpfc_hba *phba = vport->phba; uint32_t evt_posted; atomic_inc(&phba->num_cmd_success); if (vport->cfg_lun_queue_depth <= queue_depth) return; spin_lock_irqsave(&phba->hbalock, flags); if (time_before(jiffies, phba->last_ramp_up_time + QUEUE_RAMP_UP_INTERVAL) || time_before(jiffies, phba->last_rsrc_error_time + QUEUE_RAMP_UP_INTERVAL)) { spin_unlock_irqrestore(&phba->hbalock, flags); return; } phba->last_ramp_up_time = jiffies; spin_unlock_irqrestore(&phba->hbalock, flags); spin_lock_irqsave(&phba->pport->work_port_lock, flags); evt_posted = phba->pport->work_port_events & WORKER_RAMP_UP_QUEUE; if (!evt_posted) phba->pport->work_port_events |= WORKER_RAMP_UP_QUEUE; spin_unlock_irqrestore(&phba->pport->work_port_lock, flags); if (!evt_posted) lpfc_worker_wake_up(phba); return; } /** * lpfc_ramp_down_queue_handler - WORKER_RAMP_DOWN_QUEUE event handler * @phba: The Hba for which this call is being executed. * * This routine is called to process WORKER_RAMP_DOWN_QUEUE event for worker * thread.This routine reduces queue depth for all scsi device on each vport * associated with @phba. **/ void lpfc_ramp_down_queue_handler(struct lpfc_hba *phba) { struct lpfc_vport **vports; struct Scsi_Host *shost; struct scsi_device *sdev; unsigned long new_queue_depth; unsigned long num_rsrc_err, num_cmd_success; int i; num_rsrc_err = atomic_read(&phba->num_rsrc_err); num_cmd_success = atomic_read(&phba->num_cmd_success); /* * The error and success command counters are global per * driver instance. If another handler has already * operated on this error event, just exit. */ if (num_rsrc_err == 0) return; vports = lpfc_create_vport_work_array(phba); if (vports != NULL) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { shost = lpfc_shost_from_vport(vports[i]); shost_for_each_device(sdev, shost) { new_queue_depth = sdev->queue_depth * num_rsrc_err / (num_rsrc_err + num_cmd_success); if (!new_queue_depth) new_queue_depth = sdev->queue_depth - 1; else new_queue_depth = sdev->queue_depth - new_queue_depth; lpfc_change_queue_depth(sdev, new_queue_depth, SCSI_QDEPTH_DEFAULT); } } lpfc_destroy_vport_work_array(phba, vports); atomic_set(&phba->num_rsrc_err, 0); atomic_set(&phba->num_cmd_success, 0); } /** * lpfc_ramp_up_queue_handler - WORKER_RAMP_UP_QUEUE event handler * @phba: The Hba for which this call is being executed. * * This routine is called to process WORKER_RAMP_UP_QUEUE event for worker * thread.This routine increases queue depth for all scsi device on each vport * associated with @phba by 1. This routine also sets @phba num_rsrc_err and * num_cmd_success to zero. **/ void lpfc_ramp_up_queue_handler(struct lpfc_hba *phba) { struct lpfc_vport **vports; struct Scsi_Host *shost; struct scsi_device *sdev; int i; vports = lpfc_create_vport_work_array(phba); if (vports != NULL) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { shost = lpfc_shost_from_vport(vports[i]); shost_for_each_device(sdev, shost) { if (vports[i]->cfg_lun_queue_depth <= sdev->queue_depth) continue; lpfc_change_queue_depth(sdev, sdev->queue_depth+1, SCSI_QDEPTH_RAMP_UP); } } lpfc_destroy_vport_work_array(phba, vports); atomic_set(&phba->num_rsrc_err, 0); atomic_set(&phba->num_cmd_success, 0); } /** * lpfc_scsi_dev_block - set all scsi hosts to block state * @phba: Pointer to HBA context object. * * This function walks vport list and set each SCSI host to block state * by invoking fc_remote_port_delete() routine. This function is invoked * with EEH when device's PCI slot has been permanently disabled. **/ void lpfc_scsi_dev_block(struct lpfc_hba *phba) { struct lpfc_vport **vports; struct Scsi_Host *shost; struct scsi_device *sdev; struct fc_rport *rport; int i; vports = lpfc_create_vport_work_array(phba); if (vports != NULL) for (i = 0; i <= phba->max_vports && vports[i] != NULL; i++) { shost = lpfc_shost_from_vport(vports[i]); shost_for_each_device(sdev, shost) { rport = starget_to_rport(scsi_target(sdev)); fc_remote_port_delete(rport); } } lpfc_destroy_vport_work_array(phba, vports); } /** * lpfc_new_scsi_buf_s3 - Scsi buffer allocator for HBA with SLI3 IF spec * @vport: The virtual port for which this call being executed. * @num_to_allocate: The requested number of buffers to allocate. * * This routine allocates a scsi buffer for device with SLI-3 interface spec, * the scsi buffer contains all the necessary information needed to initiate * a SCSI I/O. The non-DMAable buffer region contains information to build * the IOCB. The DMAable region contains memory for the FCP CMND, FCP RSP, * and the initial BPL. In addition to allocating memory, the FCP CMND and * FCP RSP BDEs are setup in the BPL and the BPL BDE is setup in the IOCB. * * Return codes: * int - number of scsi buffers that were allocated. * 0 = failure, less than num_to_alloc is a partial failure. **/ static int lpfc_new_scsi_buf_s3(struct lpfc_vport *vport, int num_to_alloc) { struct lpfc_hba *phba = vport->phba; struct lpfc_scsi_buf *psb; struct ulp_bde64 *bpl; IOCB_t *iocb; dma_addr_t pdma_phys_fcp_cmd; dma_addr_t pdma_phys_fcp_rsp; dma_addr_t pdma_phys_bpl; uint16_t iotag; int bcnt; for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL); if (!psb) break; /* * Get memory from the pci pool to map the virt space to pci * bus space for an I/O. The DMA buffer includes space for the * struct fcp_cmnd, struct fcp_rsp and the number of bde's * necessary to support the sg_tablesize. */ psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool, GFP_KERNEL, &psb->dma_handle); if (!psb->data) { kfree(psb); break; } /* Initialize virtual ptrs to dma_buf region. */ memset(psb->data, 0, phba->cfg_sg_dma_buf_size); /* Allocate iotag for psb->cur_iocbq. */ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); if (iotag == 0) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; } psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP; psb->fcp_cmnd = psb->data; psb->fcp_rsp = psb->data + sizeof(struct fcp_cmnd); psb->fcp_bpl = psb->data + sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp); /* Initialize local short-hand pointers. */ bpl = psb->fcp_bpl; pdma_phys_fcp_cmd = psb->dma_handle; pdma_phys_fcp_rsp = psb->dma_handle + sizeof(struct fcp_cmnd); pdma_phys_bpl = psb->dma_handle + sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp); /* * The first two bdes are the FCP_CMD and FCP_RSP. The balance * are sg list bdes. Initialize the first two and leave the * rest for queuecommand. */ bpl[0].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_cmd)); bpl[0].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_cmd)); bpl[0].tus.f.bdeSize = sizeof(struct fcp_cmnd); bpl[0].tus.f.bdeFlags = BUFF_TYPE_BDE_64; bpl[0].tus.w = le32_to_cpu(bpl[0].tus.w); /* Setup the physical region for the FCP RSP */ bpl[1].addrHigh = le32_to_cpu(putPaddrHigh(pdma_phys_fcp_rsp)); bpl[1].addrLow = le32_to_cpu(putPaddrLow(pdma_phys_fcp_rsp)); bpl[1].tus.f.bdeSize = sizeof(struct fcp_rsp); bpl[1].tus.f.bdeFlags = BUFF_TYPE_BDE_64; bpl[1].tus.w = le32_to_cpu(bpl[1].tus.w); /* * Since the IOCB for the FCP I/O is built into this * lpfc_scsi_buf, initialize it with all known data now. */ iocb = &psb->cur_iocbq.iocb; iocb->un.fcpi64.bdl.ulpIoTag32 = 0; if ((phba->sli_rev == 3) && !(phba->sli3_options & LPFC_SLI3_BG_ENABLED)) { /* fill in immediate fcp command BDE */ iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_IMMED; iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd); iocb->un.fcpi64.bdl.addrLow = offsetof(IOCB_t, unsli3.fcp_ext.icd); iocb->un.fcpi64.bdl.addrHigh = 0; iocb->ulpBdeCount = 0; iocb->ulpLe = 0; /* fill in response BDE */ iocb->unsli3.fcp_ext.rbde.tus.f.bdeFlags = BUFF_TYPE_BDE_64; iocb->unsli3.fcp_ext.rbde.tus.f.bdeSize = sizeof(struct fcp_rsp); iocb->unsli3.fcp_ext.rbde.addrLow = putPaddrLow(pdma_phys_fcp_rsp); iocb->unsli3.fcp_ext.rbde.addrHigh = putPaddrHigh(pdma_phys_fcp_rsp); } else { iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BLP_64; iocb->un.fcpi64.bdl.bdeSize = (2 * sizeof(struct ulp_bde64)); iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_bpl); iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_bpl); iocb->ulpBdeCount = 1; iocb->ulpLe = 1; } iocb->ulpClass = CLASS3; psb->status = IOSTAT_SUCCESS; /* Put it back into the SCSI buffer list */ psb->cur_iocbq.context1 = psb; lpfc_release_scsi_buf_s3(phba, psb); } return bcnt; } /** * lpfc_sli4_vport_delete_fcp_xri_aborted -Remove all ndlp references for vport * @vport: pointer to lpfc vport data structure. * * This routine is invoked by the vport cleanup for deletions and the cleanup * for an ndlp on removal. **/ void lpfc_sli4_vport_delete_fcp_xri_aborted(struct lpfc_vport *vport) { struct lpfc_hba *phba = vport->phba; struct lpfc_scsi_buf *psb, *next_psb; unsigned long iflag = 0; spin_lock_irqsave(&phba->hbalock, iflag); spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); list_for_each_entry_safe(psb, next_psb, &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { if (psb->rdata && psb->rdata->pnode && psb->rdata->pnode->vport == vport) psb->rdata = NULL; } spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); spin_unlock_irqrestore(&phba->hbalock, iflag); } /** * lpfc_sli4_fcp_xri_aborted - Fast-path process of fcp xri abort * @phba: pointer to lpfc hba data structure. * @axri: pointer to the fcp xri abort wcqe structure. * * This routine is invoked by the worker thread to process a SLI4 fast-path * FCP aborted xri. **/ void lpfc_sli4_fcp_xri_aborted(struct lpfc_hba *phba, struct sli4_wcqe_xri_aborted *axri) { uint16_t xri = bf_get(lpfc_wcqe_xa_xri, axri); uint16_t rxid = bf_get(lpfc_wcqe_xa_remote_xid, axri); struct lpfc_scsi_buf *psb, *next_psb; unsigned long iflag = 0; struct lpfc_iocbq *iocbq; int i; struct lpfc_nodelist *ndlp; int rrq_empty = 0; struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING]; spin_lock_irqsave(&phba->hbalock, iflag); spin_lock(&phba->sli4_hba.abts_scsi_buf_list_lock); list_for_each_entry_safe(psb, next_psb, &phba->sli4_hba.lpfc_abts_scsi_buf_list, list) { if (psb->cur_iocbq.sli4_xritag == xri) { list_del(&psb->list); psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; spin_unlock( &phba->sli4_hba.abts_scsi_buf_list_lock); if (psb->rdata && psb->rdata->pnode) ndlp = psb->rdata->pnode; else ndlp = NULL; rrq_empty = list_empty(&phba->active_rrq_list); spin_unlock_irqrestore(&phba->hbalock, iflag); if (ndlp) { lpfc_set_rrq_active(phba, ndlp, psb->cur_iocbq.sli4_lxritag, rxid, 1); lpfc_sli4_abts_err_handler(phba, ndlp, axri); } lpfc_release_scsi_buf_s4(phba, psb); if (rrq_empty) lpfc_worker_wake_up(phba); return; } } spin_unlock(&phba->sli4_hba.abts_scsi_buf_list_lock); for (i = 1; i <= phba->sli.last_iotag; i++) { iocbq = phba->sli.iocbq_lookup[i]; if (!(iocbq->iocb_flag & LPFC_IO_FCP) || (iocbq->iocb_flag & LPFC_IO_LIBDFC)) continue; if (iocbq->sli4_xritag != xri) continue; psb = container_of(iocbq, struct lpfc_scsi_buf, cur_iocbq); psb->exch_busy = 0; spin_unlock_irqrestore(&phba->hbalock, iflag); if (pring->txq_cnt) lpfc_worker_wake_up(phba); return; } spin_unlock_irqrestore(&phba->hbalock, iflag); } /** * lpfc_sli4_post_scsi_sgl_list - Psot blocks of scsi buffer sgls from a list * @phba: pointer to lpfc hba data structure. * @post_sblist: pointer to the scsi buffer list. * * This routine walks a list of scsi buffers that was passed in. It attempts * to construct blocks of scsi buffer sgls which contains contiguous xris and * uses the non-embedded SGL block post mailbox commands to post to the port. * For single SCSI buffer sgl with non-contiguous xri, if any, it shall use * embedded SGL post mailbox command for posting. The @post_sblist passed in * must be local list, thus no lock is needed when manipulate the list. * * Returns: 0 = failure, non-zero number of successfully posted buffers. **/ int lpfc_sli4_post_scsi_sgl_list(struct lpfc_hba *phba, struct list_head *post_sblist, int sb_count) { struct lpfc_scsi_buf *psb, *psb_next; int status; int post_cnt = 0, block_cnt = 0, num_posting = 0, num_posted = 0; dma_addr_t pdma_phys_bpl1; int last_xritag = NO_XRI; LIST_HEAD(prep_sblist); LIST_HEAD(blck_sblist); LIST_HEAD(scsi_sblist); /* sanity check */ if (sb_count <= 0) return -EINVAL; list_for_each_entry_safe(psb, psb_next, post_sblist, list) { list_del_init(&psb->list); block_cnt++; if ((last_xritag != NO_XRI) && (psb->cur_iocbq.sli4_xritag != last_xritag + 1)) { /* a hole in xri block, form a sgl posting block */ list_splice_init(&prep_sblist, &blck_sblist); post_cnt = block_cnt - 1; /* prepare list for next posting block */ list_add_tail(&psb->list, &prep_sblist); block_cnt = 1; } else { /* prepare list for next posting block */ list_add_tail(&psb->list, &prep_sblist); /* enough sgls for non-embed sgl mbox command */ if (block_cnt == LPFC_NEMBED_MBOX_SGL_CNT) { list_splice_init(&prep_sblist, &blck_sblist); post_cnt = block_cnt; block_cnt = 0; } } num_posting++; last_xritag = psb->cur_iocbq.sli4_xritag; /* end of repost sgl list condition for SCSI buffers */ if (num_posting == sb_count) { if (post_cnt == 0) { /* last sgl posting block */ list_splice_init(&prep_sblist, &blck_sblist); post_cnt = block_cnt; } else if (block_cnt == 1) { /* last single sgl with non-contiguous xri */ if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) pdma_phys_bpl1 = psb->dma_phys_bpl + SGL_PAGE_SIZE; else pdma_phys_bpl1 = 0; status = lpfc_sli4_post_sgl(phba, psb->dma_phys_bpl, pdma_phys_bpl1, psb->cur_iocbq.sli4_xritag); if (status) { /* failure, put on abort scsi list */ psb->exch_busy = 1; } else { /* success, put on SCSI buffer list */ psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; num_posted++; } /* success, put on SCSI buffer sgl list */ list_add_tail(&psb->list, &scsi_sblist); } } /* continue until a nembed page worth of sgls */ if (post_cnt == 0) continue; /* post block of SCSI buffer list sgls */ status = lpfc_sli4_post_scsi_sgl_block(phba, &blck_sblist, post_cnt); /* don't reset xirtag due to hole in xri block */ if (block_cnt == 0) last_xritag = NO_XRI; /* reset SCSI buffer post count for next round of posting */ post_cnt = 0; /* put posted SCSI buffer-sgl posted on SCSI buffer sgl list */ while (!list_empty(&blck_sblist)) { list_remove_head(&blck_sblist, psb, struct lpfc_scsi_buf, list); if (status) { /* failure, put on abort scsi list */ psb->exch_busy = 1; } else { /* success, put on SCSI buffer list */ psb->exch_busy = 0; psb->status = IOSTAT_SUCCESS; num_posted++; } list_add_tail(&psb->list, &scsi_sblist); } } /* Push SCSI buffers with sgl posted to the availble list */ while (!list_empty(&scsi_sblist)) { list_remove_head(&scsi_sblist, psb, struct lpfc_scsi_buf, list); lpfc_release_scsi_buf_s4(phba, psb); } return num_posted; } /** * lpfc_sli4_repost_scsi_sgl_list - Repsot all the allocated scsi buffer sgls * @phba: pointer to lpfc hba data structure. * * This routine walks the list of scsi buffers that have been allocated and * repost them to the port by using SGL block post. This is needed after a * pci_function_reset/warm_start or start. The lpfc_hba_down_post_s4 routine * is responsible for moving all scsi buffers on the lpfc_abts_scsi_sgl_list * to the lpfc_scsi_buf_list. If the repost fails, reject all scsi buffers. * * Returns: 0 = success, non-zero failure. **/ int lpfc_sli4_repost_scsi_sgl_list(struct lpfc_hba *phba) { LIST_HEAD(post_sblist); int num_posted, rc = 0; /* get all SCSI buffers need to repost to a local list */ spin_lock(&phba->scsi_buf_list_lock); list_splice_init(&phba->lpfc_scsi_buf_list, &post_sblist); spin_unlock(&phba->scsi_buf_list_lock); /* post the list of scsi buffer sgls to port if available */ if (!list_empty(&post_sblist)) { num_posted = lpfc_sli4_post_scsi_sgl_list(phba, &post_sblist, phba->sli4_hba.scsi_xri_cnt); /* failed to post any scsi buffer, return error */ if (num_posted == 0) rc = -EIO; } return rc; } /** * lpfc_new_scsi_buf_s4 - Scsi buffer allocator for HBA with SLI4 IF spec * @vport: The virtual port for which this call being executed. * @num_to_allocate: The requested number of buffers to allocate. * * This routine allocates scsi buffers for device with SLI-4 interface spec, * the scsi buffer contains all the necessary information needed to initiate * a SCSI I/O. After allocating up to @num_to_allocate SCSI buffers and put * them on a list, it post them to the port by using SGL block post. * * Return codes: * int - number of scsi buffers that were allocated and posted. * 0 = failure, less than num_to_alloc is a partial failure. **/ static int lpfc_new_scsi_buf_s4(struct lpfc_vport *vport, int num_to_alloc) { struct lpfc_hba *phba = vport->phba; struct lpfc_scsi_buf *psb; struct sli4_sge *sgl; IOCB_t *iocb; dma_addr_t pdma_phys_fcp_cmd; dma_addr_t pdma_phys_fcp_rsp; dma_addr_t pdma_phys_bpl, pdma_phys_bpl1; uint16_t iotag, lxri = 0; int bcnt, num_posted; LIST_HEAD(prep_sblist); LIST_HEAD(post_sblist); LIST_HEAD(scsi_sblist); for (bcnt = 0; bcnt < num_to_alloc; bcnt++) { psb = kzalloc(sizeof(struct lpfc_scsi_buf), GFP_KERNEL); if (!psb) break; /* * Get memory from the pci pool to map the virt space to * pci bus space for an I/O. The DMA buffer includes space * for the struct fcp_cmnd, struct fcp_rsp and the number * of bde's necessary to support the sg_tablesize. */ psb->data = pci_pool_alloc(phba->lpfc_scsi_dma_buf_pool, GFP_KERNEL, &psb->dma_handle); if (!psb->data) { kfree(psb); break; } memset(psb->data, 0, phba->cfg_sg_dma_buf_size); /* Allocate iotag for psb->cur_iocbq. */ iotag = lpfc_sli_next_iotag(phba, &psb->cur_iocbq); if (iotag == 0) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; } lxri = lpfc_sli4_next_xritag(phba); if (lxri == NO_XRI) { pci_pool_free(phba->lpfc_scsi_dma_buf_pool, psb->data, psb->dma_handle); kfree(psb); break; } psb->cur_iocbq.sli4_lxritag = lxri; psb->cur_iocbq.sli4_xritag = phba->sli4_hba.xri_ids[lxri]; psb->cur_iocbq.iocb_flag |= LPFC_IO_FCP; psb->fcp_bpl = psb->data; psb->fcp_cmnd = (psb->data + phba->cfg_sg_dma_buf_size) - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); psb->fcp_rsp = (struct fcp_rsp *)((uint8_t *)psb->fcp_cmnd + sizeof(struct fcp_cmnd)); /* Initialize local short-hand pointers. */ sgl = (struct sli4_sge *)psb->fcp_bpl; pdma_phys_bpl = psb->dma_handle; pdma_phys_fcp_cmd = (psb->dma_handle + phba->cfg_sg_dma_buf_size) - (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp)); pdma_phys_fcp_rsp = pdma_phys_fcp_cmd + sizeof(struct fcp_cmnd); /* * The first two bdes are the FCP_CMD and FCP_RSP. * The balance are sg list bdes. Initialize the * first two and leave the rest for queuecommand. */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_cmd)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_cmd)); sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 0); sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(sizeof(struct fcp_cmnd)); sgl++; /* Setup the physical region for the FCP RSP */ sgl->addr_hi = cpu_to_le32(putPaddrHigh(pdma_phys_fcp_rsp)); sgl->addr_lo = cpu_to_le32(putPaddrLow(pdma_phys_fcp_rsp)); sgl->word2 = le32_to_cpu(sgl->word2); bf_set(lpfc_sli4_sge_last, sgl, 1); sgl->word2 = cpu_to_le32(sgl->word2); sgl->sge_len = cpu_to_le32(sizeof(struct fcp_rsp)); /* * Since the IOCB for the FCP I/O is built into this * lpfc_scsi_buf, initialize it with all known data now. */ iocb = &psb->cur_iocbq.iocb; iocb->un.fcpi64.bdl.ulpIoTag32 = 0; iocb->un.fcpi64.bdl.bdeFlags = BUFF_TYPE_BDE_64; /* setting the BLP size to 2 * sizeof BDE may not be correct. * We are setting the bpl to point to out sgl. An sgl's * entries are 16 bytes, a bpl entries are 12 bytes. */ iocb->un.fcpi64.bdl.bdeSize = sizeof(struct fcp_cmnd); iocb->un.fcpi64.bdl.addrLow = putPaddrLow(pdma_phys_fcp_cmd); iocb->un.fcpi64.bdl.addrHigh = putPaddrHigh(pdma_phys_fcp_cmd); iocb->ulpBdeCount = 1; iocb->ulpLe = 1; iocb->ulpClass = CLASS3; psb->cur_iocbq.context1 = psb; if (phba->cfg_sg_dma_buf_size > SGL_PAGE_SIZE) pdma_phys_bpl1 = pdma_phys_bpl + SGL_PAGE_SIZE; else pdma_phys_bpl1 = 0; psb->dma_phys_bpl = pdma_phys_bpl; /* add the scsi buffer to a post list */ list_add_tail(&psb->list, &post_sblist); spin_lock_irq(&phba->scsi_buf_list_lock); phba->sli4_hba.scsi_xri_cnt++; spin_unlock_irq(&phba->scsi_buf_list_lock); } lpfc_printf_log(phba, KERN_INFO, LOG_BG, "3021 Allocate %d out of %d requested new SCSI " "buffers\n", bcnt, num_to_alloc); /* post the list of scsi buffer sgls to port if available */ if (!list_empty(&post_sblist)) num_posted = lpfc_sli4_post_scsi_sgl_list(phba, &post_sblist, bcnt); else num_posted = 0; return num_posted; } /** * lpfc_new_scsi_buf - Wrapper funciton for scsi buffer allocator * @vport: The virtual port for which this call being executed. * @num_to_allocate: The requested number of buffers to allocate. * * This routine wraps the actual SCSI buffer allocator function pointer from * the lpfc_hba struct. * * Return codes: * int - number of scsi buffers that were allocated. * 0 = failure, less than num_to_alloc is a partial failure. **/ static inline int lpfc_new_scsi_buf(struct lpfc_vport *vport, int num_to_alloc) { return vport->phba->lpfc_new_scsi_buf(vport, num_to_alloc); } /** * lpfc_get_scsi_buf_s3 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA * @phba: The HBA for which this call is being executed. * * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list * and returns to caller. * * Return codes: * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* lpfc_get_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { struct lpfc_scsi_buf * lpfc_cmd = NULL; struct list_head *scsi_buf_list = &phba->lpfc_scsi_buf_list; unsigned long iflag = 0; spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); list_remove_head(scsi_buf_list, lpfc_cmd, struct lpfc_scsi_buf, list); if (lpfc_cmd) { lpfc_cmd->seg_cnt = 0; lpfc_cmd->nonsg_phys = 0; lpfc_cmd->prot_seg_cnt = 0; } spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag); return lpfc_cmd; } /** * lpfc_get_scsi_buf_s4 - Get a scsi buffer from lpfc_scsi_buf_list of the HBA * @phba: The HBA for which this call is being executed. * * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list * and returns to caller. * * Return codes: * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* lpfc_get_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { struct lpfc_scsi_buf *lpfc_cmd ; unsigned long iflag = 0; int found = 0; spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); list_for_each_entry(lpfc_cmd, &phba->lpfc_scsi_buf_list, list) { if (lpfc_test_rrq_active(phba, ndlp, lpfc_cmd->cur_iocbq.sli4_lxritag)) continue; list_del(&lpfc_cmd->list); found = 1; lpfc_cmd->seg_cnt = 0; lpfc_cmd->nonsg_phys = 0; lpfc_cmd->prot_seg_cnt = 0; break; } spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag); if (!found) return NULL; else return lpfc_cmd; } /** * lpfc_get_scsi_buf - Get a scsi buffer from lpfc_scsi_buf_list of the HBA * @phba: The HBA for which this call is being executed. * * This routine removes a scsi buffer from head of @phba lpfc_scsi_buf_list list * and returns to caller. * * Return codes: * NULL - Error * Pointer to lpfc_scsi_buf - Success **/ static struct lpfc_scsi_buf* lpfc_get_scsi_buf(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp) { return phba->lpfc_get_scsi_buf(phba, ndlp); } /** * lpfc_release_scsi_buf - Return a scsi buffer back to hba scsi buf list * @phba: The Hba for which this call is being executed. * @psb: The scsi buffer which is being released. * * This routine releases @psb scsi buffer by adding it to tail of @phba * lpfc_scsi_buf_list list. **/ static void lpfc_release_scsi_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) { unsigned long iflag = 0; spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); psb->pCmd = NULL; list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list); spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag); } /** * lpfc_release_scsi_buf_s4: Return a scsi buffer back to hba scsi buf list. * @phba: The Hba for which this call is being executed. * @psb: The scsi buffer which is being released. * * This routine releases @psb scsi buffer by adding it to tail of @phba * lpfc_scsi_buf_list list. For SLI4 XRI's are tied to the scsi buffer * and cannot be reused for at least RA_TOV amount of time if it was * aborted. **/ static void lpfc_release_scsi_buf_s4(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) { unsigned long iflag = 0; if (psb->exch_busy) { spin_lock_irqsave(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag); psb->pCmd = NULL; list_add_tail(&psb->list, &phba->sli4_hba.lpfc_abts_scsi_buf_list); spin_unlock_irqrestore(&phba->sli4_hba.abts_scsi_buf_list_lock, iflag); } else { spin_lock_irqsave(&phba->scsi_buf_list_lock, iflag); psb->pCmd = NULL; list_add_tail(&psb->list, &phba->lpfc_scsi_buf_list); spin_unlock_irqrestore(&phba->scsi_buf_list_lock, iflag); } } /** * lpfc_release_scsi_buf: Return a scsi buffer back to hba scsi buf list. * @phba: The Hba for which this call is being executed. * @psb: The scsi buffer which is being released. * * This routine releases @psb scsi buffer by adding it to tail of @phba * lpfc_scsi_buf_list list. **/ static void lpfc_release_scsi_buf(struct lpfc_hba *phba, struct lpfc_scsi_buf *psb) { phba->lpfc_release_scsi_buf(phba, psb); } /** * lpfc_scsi_prep_dma_buf_s3 - DMA mapping for scsi buffer to SLI3 IF spec * @phba: The Hba for which this call is being executed. * @lpfc_cmd: The scsi buffer which is going to be mapped. * * This routine does the pci dma mapping for scatter-gather list of scsi cmnd * field of @lpfc_cmd for device with SLI-3 interface spec. This routine scans * through sg elements and format the bdea. This routine also initializes all * IOCB fields which are dependent on scsi command request buffer. * * Return codes: * 1 - Error * 0 - Success **/ static int lpfc_scsi_prep_dma_buf_s3(struct lpfc_hba *phba, struct lpfc_scsi_buf *lpfc_cmd) { struct scsi_cmnd *scsi_cmnd = lpfc_cmd->pCmd; struct scatterlist *sgel = NULL; struct fcp_cmnd *fcp_cmnd = lpfc_cmd->fcp_cmnd; struct ulp_bde64 *bpl = lpfc_cmd->fcp_bpl; struct lpfc_iocbq *iocbq = &lpfc_cmd->cur_iocbq; IOCB_t *iocb_cmd = &lpfc_cmd->cur_iocbq.iocb; struct ulp_bde64 *data_bde = iocb_cmd->unsli3.fcp_ext.dbde; dma_addr_t physaddr; uint32_t num_bde = 0; int nseg, datadir = scsi_cmnd->sc_data_direction; /* * There are three possibilities here - use scatter-gather segment, use * the single mapping, or neither. Start the lpfc command prep by * bumping the bpl beyond the fcp_cmnd and fcp_rsp regions to the first * data bde entry. */ bpl += 2; if (scsi_sg_count(scsi_cmnd)) { /* * The driver stores the segment count returned from pci_map_sg * because this a count of dma-mappings used to map the use_sg * pages. They are not guaranteed to be the same for those * architectures that implement an IOMMU. */ nseg = dma_map_sg(&phba->pcidev->dev, scsi_sglist(scsi_cmnd), scsi_sg_count(scsi_cmnd), datadir); if (unlikely(!nseg)) return 1; lpfc_cmd->seg_cnt = nseg; if (lpfc_cmd->seg_cnt > phba->cfg_sg_seg_cnt) { lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9064 BLKGRD: %s: Too many sg segments from " "dma_map_sg. Config %d, seg_cnt %d\n", __func__, phba->cfg_sg_seg_cnt, lpfc_cmd->seg_cnt); scsi_dma_unmap(scsi_cmnd); return 1; } /* * The driver established a maximum scatter-gather segment count * during probe that limits the number of sg elements in any * single scsi command. Just run through the seg_cnt and format * the bde's. * When using SLI-3 the driver will try to fit all the BDEs into * the IOCB. If it can't then the BDEs get added to a BPL as it * does for SLI-2 mode. */ scsi_for_each_sg(scsi_cmnd, sgel, nseg, num_bde) { physaddr = sg_dma_address(sgel); if (phba->sli_rev == 3 && !(phba->sli3_options & LPFC_SLI3_BG_ENABLED) && !(iocbq->iocb_flag & DSS_SECURITY_OP) && nseg <= LPFC_EXT_DATA_BDE_COUNT) { data_bde->tus.f.bdeFlags = BUFF_TYPE_BDE_64; data_bde->tus.f.bdeSize = sg_dma_len(sgel); data_bde->addrLow = putPaddrLow(physaddr); data_bde->addrHigh = putPaddrHigh(physaddr); data_bde++; } else { bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; bpl->tus.f.bdeSize = sg_dma_len(sgel); bpl->tus.w = le32_to_cpu(bpl->tus.w); bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); bpl++; } } } /* * Finish initializing those IOCB fields that are dependent on the * scsi_cmnd request_buffer. Note that for SLI-2 the bdeSize is * explicitly reinitialized and for SLI-3 the extended bde count is * explicitly reinitialized since all iocb memory resources are reused. */ if (phba->sli_rev == 3 && !(phba->sli3_options & LPFC_SLI3_BG_ENABLED) && !(iocbq->iocb_flag & DSS_SECURITY_OP)) { if (num_bde > LPFC_EXT_DATA_BDE_COUNT) { /* * The extended IOCB format can only fit 3 BDE or a BPL. * This I/O has more than 3 BDE so the 1st data bde will * be a BPL that is filled in here. */ physaddr = lpfc_cmd->dma_handle; data_bde->tus.f.bdeFlags = BUFF_TYPE_BLP_64; data_bde->tus.f.bdeSize = (num_bde * sizeof(struct ulp_bde64)); physaddr += (sizeof(struct fcp_cmnd) + sizeof(struct fcp_rsp) + (2 * sizeof(struct ulp_bde64))); data_bde->addrHigh = putPaddrHigh(physaddr); data_bde->addrLow = putPaddrLow(physaddr); /* ebde count includes the response bde and data bpl */ iocb_cmd->unsli3.fcp_ext.ebde_count = 2; } else { /* ebde count includes the response bde and data bdes */ iocb_cmd->unsli3.fcp_ext.ebde_count = (num_bde + 1); } } else { iocb_cmd->un.fcpi64.bdl.bdeSize = ((num_bde + 2) * sizeof(struct ulp_bde64)); iocb_cmd->unsli3.fcp_ext.ebde_count = (num_bde + 1); } fcp_cmnd->fcpDl = cpu_to_be32(scsi_bufflen(scsi_cmnd)); /* * Due to difference in data length between DIF/non-DIF paths, * we need to set word 4 of IOCB here */ iocb_cmd->un.fcpi.fcpi_parm = scsi_bufflen(scsi_cmnd); return 0; } static inline unsigned lpfc_cmd_blksize(struct scsi_cmnd *sc) { return sc->device->sector_size; } #ifdef CONFIG_SCSI_LPFC_DEBUG_FS /* Return if if error injection is detected by Initiator */ #define BG_ERR_INIT 0x1 /* Return if if error injection is detected by Target */ #define BG_ERR_TGT 0x2 /* Return if if swapping CSUM<-->CRC is required for error injection */ #define BG_ERR_SWAP 0x10 /* Return if disabling Guard/Ref/App checking is required for error injection */ #define BG_ERR_CHECK 0x20 /** * lpfc_bg_err_inject - Determine if we should inject an error * @phba: The Hba for which this call is being executed. * @sc: The SCSI command to examine * @reftag: (out) BlockGuard reference tag for transmitted data * @apptag: (out) BlockGuard application tag for transmitted data * @new_guard (in) Value to replace CRC with if needed * * Returns BG_ERR_* bit mask or 0 if request ignored **/ static int lpfc_bg_err_inject(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint32_t *reftag, uint16_t *apptag, uint32_t new_guard) { struct scatterlist *sgpe; /* s/g prot entry */ struct scatterlist *sgde; /* s/g data entry */ struct lpfc_scsi_buf *lpfc_cmd = NULL; struct scsi_dif_tuple *src = NULL; struct lpfc_nodelist *ndlp; struct lpfc_rport_data *rdata; uint32_t op = scsi_get_prot_op(sc); uint32_t blksize; uint32_t numblks; sector_t lba; int rc = 0; int blockoff = 0; if (op == SCSI_PROT_NORMAL) return 0; sgpe = scsi_prot_sglist(sc); sgde = scsi_sglist(sc); lba = scsi_get_lba(sc); /* First check if we need to match the LBA */ if (phba->lpfc_injerr_lba != LPFC_INJERR_LBA_OFF) { blksize = lpfc_cmd_blksize(sc); numblks = (scsi_bufflen(sc) + blksize - 1) / blksize; /* Make sure we have the right LBA if one is specified */ if ((phba->lpfc_injerr_lba < lba) || (phba->lpfc_injerr_lba >= (lba + numblks))) return 0; if (sgpe) { blockoff = phba->lpfc_injerr_lba - lba; numblks = sg_dma_len(sgpe) / sizeof(struct scsi_dif_tuple); if (numblks < blockoff) blockoff = numblks; } } /* Next check if we need to match the remote NPortID or WWPN */ rdata = sc->device->hostdata; if (rdata && rdata->pnode) { ndlp = rdata->pnode; /* Make sure we have the right NPortID if one is specified */ if (phba->lpfc_injerr_nportid && (phba->lpfc_injerr_nportid != ndlp->nlp_DID)) return 0; /* * Make sure we have the right WWPN if one is specified. * wwn[0] should be a non-zero NAA in a good WWPN. */ if (phba->lpfc_injerr_wwpn.u.wwn[0] && (memcmp(&ndlp->nlp_portname, &phba->lpfc_injerr_wwpn, sizeof(struct lpfc_name)) != 0)) return 0; } /* Setup a ptr to the protection data if the SCSI host provides it */ if (sgpe) { src = (struct scsi_dif_tuple *)sg_virt(sgpe); src += blockoff; lpfc_cmd = (struct lpfc_scsi_buf *)sc->host_scribble; } /* Should we change the Reference Tag */ if (reftag) { if (phba->lpfc_injerr_wref_cnt) { switch (op) { case SCSI_PROT_WRITE_PASS: if (src) { /* * For WRITE_PASS, force the error * to be sent on the wire. It should * be detected by the Target. * If blockoff != 0 error will be * inserted in middle of the IO. */ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9076 BLKGRD: Injecting reftag error: " "write lba x%lx + x%x oldrefTag x%x\n", (unsigned long)lba, blockoff, be32_to_cpu(src->ref_tag)); /* * Save the old ref_tag so we can * restore it on completion. */ if (lpfc_cmd) { lpfc_cmd->prot_data_type = LPFC_INJERR_REFTAG; lpfc_cmd->prot_data_segment = src; lpfc_cmd->prot_data = src->ref_tag; } src->ref_tag = cpu_to_be32(0xDEADBEEF); phba->lpfc_injerr_wref_cnt--; if (phba->lpfc_injerr_wref_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_TGT | BG_ERR_CHECK; break; } /* Drop thru */ case SCSI_PROT_WRITE_INSERT: /* * For WRITE_INSERT, force the error * to be sent on the wire. It should be * detected by the Target. */ /* DEADBEEF will be the reftag on the wire */ *reftag = 0xDEADBEEF; phba->lpfc_injerr_wref_cnt--; if (phba->lpfc_injerr_wref_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_TGT | BG_ERR_CHECK; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9078 BLKGRD: Injecting reftag error: " "write lba x%lx\n", (unsigned long)lba); break; case SCSI_PROT_WRITE_STRIP: /* * For WRITE_STRIP and WRITE_PASS, * force the error on data * being copied from SLI-Host to SLI-Port. */ *reftag = 0xDEADBEEF; phba->lpfc_injerr_wref_cnt--; if (phba->lpfc_injerr_wref_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9077 BLKGRD: Injecting reftag error: " "write lba x%lx\n", (unsigned long)lba); break; } } if (phba->lpfc_injerr_rref_cnt) { switch (op) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_READ_STRIP: case SCSI_PROT_READ_PASS: /* * For READ_STRIP and READ_PASS, force the * error on data being read off the wire. It * should force an IO error to the driver. */ *reftag = 0xDEADBEEF; phba->lpfc_injerr_rref_cnt--; if (phba->lpfc_injerr_rref_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9079 BLKGRD: Injecting reftag error: " "read lba x%lx\n", (unsigned long)lba); break; } } } /* Should we change the Application Tag */ if (apptag) { if (phba->lpfc_injerr_wapp_cnt) { switch (op) { case SCSI_PROT_WRITE_PASS: if (src) { /* * For WRITE_PASS, force the error * to be sent on the wire. It should * be detected by the Target. * If blockoff != 0 error will be * inserted in middle of the IO. */ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9080 BLKGRD: Injecting apptag error: " "write lba x%lx + x%x oldappTag x%x\n", (unsigned long)lba, blockoff, be16_to_cpu(src->app_tag)); /* * Save the old app_tag so we can * restore it on completion. */ if (lpfc_cmd) { lpfc_cmd->prot_data_type = LPFC_INJERR_APPTAG; lpfc_cmd->prot_data_segment = src; lpfc_cmd->prot_data = src->app_tag; } src->app_tag = cpu_to_be16(0xDEAD); phba->lpfc_injerr_wapp_cnt--; if (phba->lpfc_injerr_wapp_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_TGT | BG_ERR_CHECK; break; } /* Drop thru */ case SCSI_PROT_WRITE_INSERT: /* * For WRITE_INSERT, force the * error to be sent on the wire. It should be * detected by the Target. */ /* DEAD will be the apptag on the wire */ *apptag = 0xDEAD; phba->lpfc_injerr_wapp_cnt--; if (phba->lpfc_injerr_wapp_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_TGT | BG_ERR_CHECK; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0813 BLKGRD: Injecting apptag error: " "write lba x%lx\n", (unsigned long)lba); break; case SCSI_PROT_WRITE_STRIP: /* * For WRITE_STRIP and WRITE_PASS, * force the error on data * being copied from SLI-Host to SLI-Port. */ *apptag = 0xDEAD; phba->lpfc_injerr_wapp_cnt--; if (phba->lpfc_injerr_wapp_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0812 BLKGRD: Injecting apptag error: " "write lba x%lx\n", (unsigned long)lba); break; } } if (phba->lpfc_injerr_rapp_cnt) { switch (op) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_READ_STRIP: case SCSI_PROT_READ_PASS: /* * For READ_STRIP and READ_PASS, force the * error on data being read off the wire. It * should force an IO error to the driver. */ *apptag = 0xDEAD; phba->lpfc_injerr_rapp_cnt--; if (phba->lpfc_injerr_rapp_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT; lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0814 BLKGRD: Injecting apptag error: " "read lba x%lx\n", (unsigned long)lba); break; } } } /* Should we change the Guard Tag */ if (new_guard) { if (phba->lpfc_injerr_wgrd_cnt) { switch (op) { case SCSI_PROT_WRITE_PASS: rc = BG_ERR_CHECK; /* Drop thru */ case SCSI_PROT_WRITE_INSERT: /* * For WRITE_INSERT, force the * error to be sent on the wire. It should be * detected by the Target. */ phba->lpfc_injerr_wgrd_cnt--; if (phba->lpfc_injerr_wgrd_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc |= BG_ERR_TGT | BG_ERR_SWAP; /* Signals the caller to swap CRC->CSUM */ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0817 BLKGRD: Injecting guard error: " "write lba x%lx\n", (unsigned long)lba); break; case SCSI_PROT_WRITE_STRIP: /* * For WRITE_STRIP and WRITE_PASS, * force the error on data * being copied from SLI-Host to SLI-Port. */ phba->lpfc_injerr_wgrd_cnt--; if (phba->lpfc_injerr_wgrd_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT | BG_ERR_SWAP; /* Signals the caller to swap CRC->CSUM */ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0816 BLKGRD: Injecting guard error: " "write lba x%lx\n", (unsigned long)lba); break; } } if (phba->lpfc_injerr_rgrd_cnt) { switch (op) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_READ_STRIP: case SCSI_PROT_READ_PASS: /* * For READ_STRIP and READ_PASS, force the * error on data being read off the wire. It * should force an IO error to the driver. */ phba->lpfc_injerr_rgrd_cnt--; if (phba->lpfc_injerr_rgrd_cnt == 0) { phba->lpfc_injerr_nportid = 0; phba->lpfc_injerr_lba = LPFC_INJERR_LBA_OFF; memset(&phba->lpfc_injerr_wwpn, 0, sizeof(struct lpfc_name)); } rc = BG_ERR_INIT | BG_ERR_SWAP; /* Signals the caller to swap CRC->CSUM */ lpfc_printf_log(phba, KERN_ERR, LOG_BG, "0818 BLKGRD: Injecting guard error: " "read lba x%lx\n", (unsigned long)lba); } } } return rc; } #endif /** * lpfc_sc_to_bg_opcodes - Determine the BlockGuard opcodes to be used with * the specified SCSI command. * @phba: The Hba for which this call is being executed. * @sc: The SCSI command to examine * @txopt: (out) BlockGuard operation for transmitted data * @rxopt: (out) BlockGuard operation for received data * * Returns: zero on success; non-zero if tx and/or rx op cannot be determined * **/ static int lpfc_sc_to_bg_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint8_t *txop, uint8_t *rxop) { uint8_t guard_type = scsi_host_get_guard(sc->device->host); uint8_t ret = 0; if (guard_type == SHOST_DIX_GUARD_IP) { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: *rxop = BG_OP_IN_NODIF_OUT_CSUM; *txop = BG_OP_IN_CSUM_OUT_NODIF; break; case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: *rxop = BG_OP_IN_CRC_OUT_NODIF; *txop = BG_OP_IN_NODIF_OUT_CRC; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: *rxop = BG_OP_IN_CRC_OUT_CSUM; *txop = BG_OP_IN_CSUM_OUT_CRC; break; case SCSI_PROT_NORMAL: default: lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9063 BLKGRD: Bad op/guard:%d/IP combination\n", scsi_get_prot_op(sc)); ret = 1; break; } } else { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: *rxop = BG_OP_IN_CRC_OUT_NODIF; *txop = BG_OP_IN_NODIF_OUT_CRC; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: *rxop = BG_OP_IN_CRC_OUT_CRC; *txop = BG_OP_IN_CRC_OUT_CRC; break; case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: *rxop = BG_OP_IN_NODIF_OUT_CRC; *txop = BG_OP_IN_CRC_OUT_NODIF; break; case SCSI_PROT_NORMAL: default: lpfc_printf_log(phba, KERN_ERR, LOG_BG, "9075 BLKGRD: Bad op/guard:%d/CRC combination\n", scsi_get_prot_op(sc)); ret = 1; break; } } return ret; } #ifdef CONFIG_SCSI_LPFC_DEBUG_FS /** * lpfc_bg_err_opcodes - reDetermine the BlockGuard opcodes to be used with * the specified SCSI command in order to force a guard tag error. * @phba: The Hba for which this call is being executed. * @sc: The SCSI command to examine * @txopt: (out) BlockGuard operation for transmitted data * @rxopt: (out) BlockGuard operation for received data * * Returns: zero on success; non-zero if tx and/or rx op cannot be determined * **/ static int lpfc_bg_err_opcodes(struct lpfc_hba *phba, struct scsi_cmnd *sc, uint8_t *txop, uint8_t *rxop) { uint8_t guard_type = scsi_host_get_guard(sc->device->host); uint8_t ret = 0; if (guard_type == SHOST_DIX_GUARD_IP) { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: *rxop = BG_OP_IN_NODIF_OUT_CRC; *txop = BG_OP_IN_CRC_OUT_NODIF; break; case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: *rxop = BG_OP_IN_CSUM_OUT_NODIF; *txop = BG_OP_IN_NODIF_OUT_CSUM; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: *rxop = BG_OP_IN_CSUM_OUT_CRC; *txop = BG_OP_IN_CRC_OUT_CSUM; break; case SCSI_PROT_NORMAL: default: break; } } else { switch (scsi_get_prot_op(sc)) { case SCSI_PROT_READ_STRIP: case SCSI_PROT_WRITE_INSERT: *rxop = BG_OP_IN_CSUM_OUT_NODIF; *txop = BG_OP_IN_NODIF_OUT_CSUM; break; case SCSI_PROT_READ_PASS: case SCSI_PROT_WRITE_PASS: *rxop = BG_OP_IN_CSUM_OUT_CSUM; *txop = BG_OP_IN_CSUM_OUT_CSUM; break; case SCSI_PROT_READ_INSERT: case SCSI_PROT_WRITE_STRIP: *rxop = BG_OP_IN_NODIF_OUT_CSUM; *txop = BG_OP_IN_CSUM_OUT_NODIF; break; case SCSI_PROT_NORMAL: default: break; } } return ret; } #endif /** * lpfc_bg_setup_bpl - Setup BlockGuard BPL with no protection data * @phba: The Hba for which this call is being executed. * @sc: pointer to scsi command we're working on * @bpl: pointer to buffer list for protection groups * @datacnt: number of segments of data that have been dma mapped * * This function sets up BPL buffer list for protection groups of * type LPFC_PG_TYPE_NO_DIF * * This is usually used when the HBA is instructed to generate * DIFs and insert them into data stream (or strip DIF from * incoming data stream) * * The buffer list consists of just one protection group described * below: * +-------------------------+ * start of prot group --> | PDE_5 | * +-------------------------+ * | PDE_6 | * +-------------------------+ * | Data BDE | * +-------------------------+ * |more Data BDE's ... (opt)| * +-------------------------+ * * * Note: Data s/g buffers have been dma mapped * * Returns the number of BDEs added to the BPL. **/ static int lpfc_bg_setup_bpl(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct ulp_bde64 *bpl, int datasegcnt) { struct scatterlist *sgde = NULL; /* s/g data entry */ struct lpfc_pde5 *pde5 = NULL; struct lpfc_pde6 *pde6 = NULL; dma_addr_t physaddr; int i = 0, num_bde = 0, status; int datadir = sc->sc_data_direction; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint32_t rc; #endif uint32_t checking = 1; uint32_t reftag; unsigned blksize; uint8_t txop, rxop; status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); if (status) goto out; /* extract some info from the scsi command for pde*/ blksize = lpfc_cmd_blksize(sc); reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */ #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); if (rc) { if (rc & BG_ERR_SWAP) lpfc_bg_err_opcodes(phba, sc, &txop, &rxop); if (rc & BG_ERR_CHECK) checking = 0; } #endif /* setup PDE5 with what we have */ pde5 = (struct lpfc_pde5 *) bpl; memset(pde5, 0, sizeof(struct lpfc_pde5)); bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR); /* Endianness conversion if necessary for PDE5 */ pde5->word0 = cpu_to_le32(pde5->word0); pde5->reftag = cpu_to_le32(reftag); /* advance bpl and increment bde count */ num_bde++; bpl++; pde6 = (struct lpfc_pde6 *) bpl; /* setup PDE6 with the rest of the info */ memset(pde6, 0, sizeof(struct lpfc_pde6)); bf_set(pde6_type, pde6, LPFC_PDE6_DESCRIPTOR); bf_set(pde6_optx, pde6, txop); bf_set(pde6_oprx, pde6, rxop); if (datadir == DMA_FROM_DEVICE) { bf_set(pde6_ce, pde6, checking); bf_set(pde6_re, pde6, checking); } bf_set(pde6_ai, pde6, 1); bf_set(pde6_ae, pde6, 0); bf_set(pde6_apptagval, pde6, 0); /* Endianness conversion if necessary for PDE6 */ pde6->word0 = cpu_to_le32(pde6->word0); pde6->word1 = cpu_to_le32(pde6->word1); pde6->word2 = cpu_to_le32(pde6->word2); /* advance bpl and increment bde count */ num_bde++; bpl++; /* assumption: caller has already run dma_map_sg on command data */ scsi_for_each_sg(sc, sgde, datasegcnt, i) { physaddr = sg_dma_address(sgde); bpl->addrLow = le32_to_cpu(putPaddrLow(physaddr)); bpl->addrHigh = le32_to_cpu(putPaddrHigh(physaddr)); bpl->tus.f.bdeSize = sg_dma_len(sgde); if (datadir == DMA_TO_DEVICE) bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64; else bpl->tus.f.bdeFlags = BUFF_TYPE_BDE_64I; bpl->tus.w = le32_to_cpu(bpl->tus.w); bpl++; num_bde++; } out: return num_bde; } /** * lpfc_bg_setup_bpl_prot - Setup BlockGuard BPL with protection data * @phba: The Hba for which this call is being executed. * @sc: pointer to scsi command we're working on * @bpl: pointer to buffer list for protection groups * @datacnt: number of segments of data that have been dma mapped * @protcnt: number of segment of protection data that have been dma mapped * * This function sets up BPL buffer list for protection groups of * type LPFC_PG_TYPE_DIF * * This is usually used when DIFs are in their own buffers, * separate from the data. The HBA can then by instructed * to place the DIFs in the outgoing stream. For read operations, * The HBA could extract the DIFs and place it in DIF buffers. * * The buffer list for this type consists of one or more of the * protection groups described below: * +-------------------------+ * start of first prot group --> | PDE_5 | * +-------------------------+ * | PDE_6 | * +-------------------------+ * | PDE_7 (Prot BDE) | * +-------------------------+ * | Data BDE | * +-------------------------+ * |more Data BDE's ... (opt)| * +-------------------------+ * start of new prot group --> | PDE_5 | * +-------------------------+ * | ... | * +-------------------------+ * * Note: It is assumed that both data and protection s/g buffers have been * mapped for DMA * * Returns the number of BDEs added to the BPL. **/ static int lpfc_bg_setup_bpl_prot(struct lpfc_hba *phba, struct scsi_cmnd *sc, struct ulp_bde64 *bpl, int datacnt, int protcnt) { struct scatterlist *sgde = NULL; /* s/g data entry */ struct scatterlist *sgpe = NULL; /* s/g prot entry */ struct lpfc_pde5 *pde5 = NULL; struct lpfc_pde6 *pde6 = NULL; struct lpfc_pde7 *pde7 = NULL; dma_addr_t dataphysaddr, protphysaddr; unsigned short curr_data = 0, curr_prot = 0; unsigned int split_offset; unsigned int protgroup_len, protgroup_offset = 0, protgroup_remainder; unsigned int protgrp_blks, protgrp_bytes; unsigned int remainder, subtotal; int status; int datadir = sc->sc_data_direction; unsigned char pgdone = 0, alldone = 0; unsigned blksize; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS uint32_t rc; #endif uint32_t checking = 1; uint32_t reftag; uint8_t txop, rxop; int num_bde = 0; sgpe = scsi_prot_sglist(sc); sgde = scsi_sglist(sc); if (!sgpe || !sgde) { lpfc_printf_log(phba, KERN_ERR, LOG_FCP, "9020 Invalid s/g entry: data=0x%p prot=0x%p\n", sgpe, sgde); return 0; } status = lpfc_sc_to_bg_opcodes(phba, sc, &txop, &rxop); if (status) goto out; /* extract some info from the scsi command */ blksize = lpfc_cmd_blksize(sc); reftag = (uint32_t)scsi_get_lba(sc); /* Truncate LBA */ #ifdef CONFIG_SCSI_LPFC_DEBUG_FS rc = lpfc_bg_err_inject(phba, sc, &reftag, NULL, 1); if (rc) { if (rc & BG_ERR_SWAP) lpfc_bg_err_opcodes(phba, sc, &txop, &rxop); if (rc & BG_ERR_CHECK) checking = 0; } #endif split_offset = 0; do { /* setup PDE5 with what we have */ pde5 = (struct lpfc_pde5 *) bpl; memset(pde5, 0, sizeof(struct lpfc_pde5)); bf_set(pde5_type, pde5, LPFC_PDE5_DESCRIPTOR);