#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
+#include <linux/slab.h>
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
static int lpfc_sli_issue_mbox_s4(struct lpfc_hba *, LPFC_MBOXQ_t *,
uint32_t);
static int lpfc_sli4_read_rev(struct lpfc_hba *, LPFC_MBOXQ_t *,
- uint8_t *, uint32_t *);
+ uint8_t *, uint32_t *);
+static struct lpfc_iocbq *lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *,
+ struct lpfc_iocbq *);
static void lpfc_sli4_send_seq_to_ulp(struct lpfc_vport *,
struct hbq_dmabuf *);
static IOCB_t *
bf_set(lpfc_eqcq_doorbell_qt, &doorbell, LPFC_QUEUE_TYPE_EVENT);
bf_set(lpfc_eqcq_doorbell_eqid, &doorbell, q->queue_id);
writel(doorbell.word0, q->phba->sli4_hba.EQCQDBregaddr);
+ /* PCI read to flush PCI pipeline on re-arming for INTx mode */
+ if ((q->phba->intr_type == INTx) && (arm == LPFC_QUEUE_REARM))
+ readl(q->phba->sli4_hba.EQCQDBregaddr);
return released;
}
*
* Returns sglq ponter = success, NULL = Failure.
**/
-static struct lpfc_sglq *
+struct lpfc_sglq *
__lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
{
uint16_t adj_xri;
return NULL;
adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
+ sglq->state = SGL_ALLOCATED;
return sglq;
}
else
sglq = __lpfc_clear_active_sglq(phba, iocbq->sli4_xritag);
if (sglq) {
- if (iocbq->iocb_flag & LPFC_DRIVER_ABORTED
- && ((iocbq->iocb.ulpStatus == IOSTAT_LOCAL_REJECT)
- && (iocbq->iocb.un.ulpWord[4]
- == IOERR_ABORT_REQUESTED))) {
+ if ((iocbq->iocb_flag & LPFC_EXCHANGE_BUSY) &&
+ (sglq->state != SGL_XRI_ABORTED)) {
spin_lock_irqsave(&phba->sli4_hba.abts_sgl_list_lock,
iflag);
list_add(&sglq->list,
&phba->sli4_hba.lpfc_abts_els_sgl_list);
spin_unlock_irqrestore(
&phba->sli4_hba.abts_sgl_list_lock, iflag);
- } else
+ } else {
+ sglq->state = SGL_FREED;
list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list);
+ }
}
case DSSCMD_IWRITE64_CX:
case DSSCMD_IREAD64_CR:
case DSSCMD_IREAD64_CX:
- case DSSCMD_INVALIDATE_DEK:
- case DSSCMD_SET_KEK:
- case DSSCMD_GET_KEK_ID:
- case DSSCMD_GEN_XFER:
type = LPFC_SOL_IOCB;
break;
case CMD_ABORT_XRI_CN:
/* HBQ for ELS and CT traffic. */
static struct lpfc_hbq_init lpfc_els_hbq = {
.rn = 1,
- .entry_count = 200,
+ .entry_count = 256,
.mask_count = 0,
.profile = 0,
.ring_mask = (1 << LPFC_ELS_RING),
int
lpfc_sli_hbqbuf_add_hbqs(struct lpfc_hba *phba, uint32_t qno)
{
- return(lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
- lpfc_hbq_defs[qno]->add_count));
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return 0;
+ else
+ return lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
+ lpfc_hbq_defs[qno]->add_count);
}
/**
static int
lpfc_sli_hbqbuf_init_hbqs(struct lpfc_hba *phba, uint32_t qno)
{
- return(lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
- lpfc_hbq_defs[qno]->init_count));
+ if (phba->sli_rev == LPFC_SLI_REV4)
+ return lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
+ lpfc_hbq_defs[qno]->entry_count);
+ else
+ return lpfc_sli_hbqbuf_fill_hbqs(phba, qno,
+ lpfc_hbq_defs[qno]->init_count);
}
/**
struct lpfc_dmabuf *mp;
uint16_t rpi, vpi;
int rc;
+ struct lpfc_vport *vport = pmb->vport;
mp = (struct lpfc_dmabuf *) (pmb->context1);
return;
}
+ /* Unreg VPI, if the REG_VPI succeed after VLink failure */
+ if ((pmb->u.mb.mbxCommand == MBX_REG_VPI) &&
+ !(phba->pport->load_flag & FC_UNLOADING) &&
+ !pmb->u.mb.mbxStatus) {
+ lpfc_unreg_vpi(phba, pmb->u.mb.un.varRegVpi.vpi, pmb);
+ pmb->vport = vport;
+ pmb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ rc = lpfc_sli_issue_mbox(phba, pmb, MBX_NOWAIT);
+ if (rc != MBX_NOT_FINISHED)
+ return;
+ }
+
if (bf_get(lpfc_mqe_command, &pmb->u.mqe) == MBX_SLI4_CONFIG)
lpfc_sli4_mbox_cmd_free(phba, pmb);
else
*/
if (lpfc_sli_chk_mbx_command(pmbox->mbxCommand) ==
MBX_SHUTDOWN) {
- /* Unknow mailbox command compl */
+ /* Unknown mailbox command compl */
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
"(%d):0323 Unknown Mailbox command "
"x%x (x%x) Cmpl\n",
* All other are passed to the completion callback.
*/
if (pring->ringno == LPFC_ELS_RING) {
- if (cmdiocbp->iocb_flag & LPFC_DRIVER_ABORTED) {
+ if ((phba->sli_rev < LPFC_SLI_REV4) &&
+ (cmdiocbp->iocb_flag &
+ LPFC_DRIVER_ABORTED)) {
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
cmdiocbp->iocb_flag &=
~LPFC_DRIVER_ABORTED;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
saveq->iocb.ulpStatus =
IOSTAT_LOCAL_REJECT;
saveq->iocb.un.ulpWord[4] =
* of DMAing payload, so don't free data
* buffer till after a hbeat.
*/
+ spin_lock_irqsave(&phba->hbalock,
+ iflag);
saveq->iocb_flag |= LPFC_DELAY_MEM_FREE;
+ spin_unlock_irqrestore(&phba->hbalock,
+ iflag);
+ }
+ if (phba->sli_rev == LPFC_SLI_REV4) {
+ if (saveq->iocb_flag &
+ LPFC_EXCHANGE_BUSY) {
+ /* Set cmdiocb flag for the
+ * exchange busy so sgl (xri)
+ * will not be released until
+ * the abort xri is received
+ * from hba.
+ */
+ spin_lock_irqsave(
+ &phba->hbalock, iflag);
+ cmdiocbp->iocb_flag |=
+ LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(
+ &phba->hbalock, iflag);
+ }
+ if (cmdiocbp->iocb_flag &
+ LPFC_DRIVER_ABORTED) {
+ /*
+ * Clear LPFC_DRIVER_ABORTED
+ * bit in case it was driver
+ * initiated abort.
+ */
+ spin_lock_irqsave(
+ &phba->hbalock, iflag);
+ cmdiocbp->iocb_flag &=
+ ~LPFC_DRIVER_ABORTED;
+ spin_unlock_irqrestore(
+ &phba->hbalock, iflag);
+ cmdiocbp->iocb.ulpStatus =
+ IOSTAT_LOCAL_REJECT;
+ cmdiocbp->iocb.un.ulpWord[4] =
+ IOERR_ABORT_REQUESTED;
+ /*
+ * For SLI4, irsiocb contains
+ * NO_XRI in sli_xritag, it
+ * shall not affect releasing
+ * sgl (xri) process.
+ */
+ saveq->iocb.ulpStatus =
+ IOSTAT_LOCAL_REJECT;
+ saveq->iocb.un.ulpWord[4] =
+ IOERR_SLI_ABORTED;
+ spin_lock_irqsave(
+ &phba->hbalock, iflag);
+ saveq->iocb_flag |=
+ LPFC_DELAY_MEM_FREE;
+ spin_unlock_irqrestore(
+ &phba->hbalock, iflag);
+ }
}
}
(cmdiocbp->iocb_cmpl) (phba, cmdiocbp, saveq);
return;
}
-/**
- * lpfc_sli_poll_fcp_ring - Handle FCP ring completion in polling mode
- * @phba: Pointer to HBA context object.
- *
- * This function is called from lpfc_queuecommand, lpfc_poll_timeout,
- * lpfc_abort_handler and lpfc_slave_configure when FCP_RING_POLLING
- * is enabled.
- *
- * The caller does not hold any lock.
- * The function processes each response iocb in the response ring until it
- * finds an iocb with LE bit set and chains all the iocbs upto the iocb with
- * LE bit set. The function will call the completion handler of the command iocb
- * if the response iocb indicates a completion for a command iocb or it is
- * an abort completion.
- **/
-void lpfc_sli_poll_fcp_ring(struct lpfc_hba *phba)
-{
- struct lpfc_sli *psli = &phba->sli;
- struct lpfc_sli_ring *pring = &psli->ring[LPFC_FCP_RING];
- IOCB_t *irsp = NULL;
- IOCB_t *entry = NULL;
- struct lpfc_iocbq *cmdiocbq = NULL;
- struct lpfc_iocbq rspiocbq;
- struct lpfc_pgp *pgp = &phba->port_gp[pring->ringno];
- uint32_t status;
- uint32_t portRspPut, portRspMax;
- int type;
- uint32_t rsp_cmpl = 0;
- uint32_t ha_copy;
- unsigned long iflags;
-
- pring->stats.iocb_event++;
-
- /*
- * The next available response entry should never exceed the maximum
- * entries. If it does, treat it as an adapter hardware error.
- */
- portRspMax = pring->numRiocb;
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- if (unlikely(portRspPut >= portRspMax)) {
- lpfc_sli_rsp_pointers_error(phba, pring);
- return;
- }
-
- rmb();
- while (pring->rspidx != portRspPut) {
- entry = lpfc_resp_iocb(phba, pring);
- if (++pring->rspidx >= portRspMax)
- pring->rspidx = 0;
-
- lpfc_sli_pcimem_bcopy((uint32_t *) entry,
- (uint32_t *) &rspiocbq.iocb,
- phba->iocb_rsp_size);
- irsp = &rspiocbq.iocb;
- type = lpfc_sli_iocb_cmd_type(irsp->ulpCommand & CMD_IOCB_MASK);
- pring->stats.iocb_rsp++;
- rsp_cmpl++;
-
- if (unlikely(irsp->ulpStatus)) {
- /* Rsp ring <ringno> error: IOCB */
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0326 Rsp Ring %d error: IOCB Data: "
- "x%x x%x x%x x%x x%x x%x x%x x%x\n",
- pring->ringno,
- irsp->un.ulpWord[0],
- irsp->un.ulpWord[1],
- irsp->un.ulpWord[2],
- irsp->un.ulpWord[3],
- irsp->un.ulpWord[4],
- irsp->un.ulpWord[5],
- *(uint32_t *)&irsp->un1,
- *((uint32_t *)&irsp->un1 + 1));
- }
-
- switch (type) {
- case LPFC_ABORT_IOCB:
- case LPFC_SOL_IOCB:
- /*
- * Idle exchange closed via ABTS from port. No iocb
- * resources need to be recovered.
- */
- if (unlikely(irsp->ulpCommand == CMD_XRI_ABORTED_CX)) {
- lpfc_printf_log(phba, KERN_INFO, LOG_SLI,
- "0314 IOCB cmd 0x%x "
- "processed. Skipping "
- "completion",
- irsp->ulpCommand);
- break;
- }
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
- &rspiocbq);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- }
- break;
- default:
- if (irsp->ulpCommand == CMD_ADAPTER_MSG) {
- char adaptermsg[LPFC_MAX_ADPTMSG];
- memset(adaptermsg, 0, LPFC_MAX_ADPTMSG);
- memcpy(&adaptermsg[0], (uint8_t *) irsp,
- MAX_MSG_DATA);
- dev_warn(&((phba->pcidev)->dev),
- "lpfc%d: %s\n",
- phba->brd_no, adaptermsg);
- } else {
- /* Unknown IOCB command */
- lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
- "0321 Unknown IOCB command "
- "Data: x%x, x%x x%x x%x x%x\n",
- type, irsp->ulpCommand,
- irsp->ulpStatus,
- irsp->ulpIoTag,
- irsp->ulpContext);
- }
- break;
- }
-
- /*
- * The response IOCB has been processed. Update the ring
- * pointer in SLIM. If the port response put pointer has not
- * been updated, sync the pgp->rspPutInx and fetch the new port
- * response put pointer.
- */
- writel(pring->rspidx, &phba->host_gp[pring->ringno].rspGetInx);
-
- if (pring->rspidx == portRspPut)
- portRspPut = le32_to_cpu(pgp->rspPutInx);
- }
-
- ha_copy = readl(phba->HAregaddr);
- ha_copy >>= (LPFC_FCP_RING * 4);
-
- if ((rsp_cmpl > 0) && (ha_copy & HA_R0RE_REQ)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_rsp_full++;
- status = ((CA_R0ATT | CA_R0RE_RSP) << (LPFC_FCP_RING * 4));
- writel(status, phba->CAregaddr);
- readl(phba->CAregaddr);
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
- if ((ha_copy & HA_R0CE_RSP) &&
- (pring->flag & LPFC_CALL_RING_AVAILABLE)) {
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->flag &= ~LPFC_CALL_RING_AVAILABLE;
- pring->stats.iocb_cmd_empty++;
-
- /* Force update of the local copy of cmdGetInx */
- pring->local_getidx = le32_to_cpu(pgp->cmdGetInx);
- lpfc_sli_resume_iocb(phba, pring);
-
- if ((pring->lpfc_sli_cmd_available))
- (pring->lpfc_sli_cmd_available) (phba, pring);
-
- spin_unlock_irqrestore(&phba->hbalock, iflags);
- }
-
- return;
-}
/**
* lpfc_sli_handle_fast_ring_event - Handle ring events on FCP ring
* an abort completion. The function will call lpfc_sli_process_unsol_iocb
* function if this is an unsolicited iocb.
* This routine presumes LPFC_FCP_RING handling and doesn't bother
- * to check it explicitly. This function always returns 1.
- **/
-static int
+ * to check it explicitly.
+ */
+int
lpfc_sli_handle_fast_ring_event(struct lpfc_hba *phba,
struct lpfc_sli_ring *pring, uint32_t mask)
{
spin_unlock_irqrestore(&phba->hbalock, iflag);
return 1;
}
+ if (phba->fcp_ring_in_use) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ return 1;
+ } else
+ phba->fcp_ring_in_use = 1;
rmb();
while (pring->rspidx != portRspPut) {
cmdiocbq = lpfc_sli_iocbq_lookup(phba, pring,
&rspiocbq);
- if ((cmdiocbq) && (cmdiocbq->iocb_cmpl)) {
- if (phba->cfg_poll & ENABLE_FCP_RING_POLLING) {
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- } else {
- spin_unlock_irqrestore(&phba->hbalock,
- iflag);
- (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
- &rspiocbq);
- spin_lock_irqsave(&phba->hbalock,
- iflag);
- }
+ if (unlikely(!cmdiocbq))
+ break;
+ if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED)
+ cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED;
+ if (cmdiocbq->iocb_cmpl) {
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ (cmdiocbq->iocb_cmpl)(phba, cmdiocbq,
+ &rspiocbq);
+ spin_lock_irqsave(&phba->hbalock, iflag);
}
break;
case LPFC_UNSOL_IOCB:
}
+ phba->fcp_ring_in_use = 0;
spin_unlock_irqrestore(&phba->hbalock, iflag);
return rc;
}
struct lpfc_cq_event *cq_event;
unsigned long iflag;
- while (!list_empty(&phba->sli4_hba.sp_rspiocb_work_queue)) {
+ spin_lock_irqsave(&phba->hbalock, iflag);
+ phba->hba_flag &= ~HBA_SP_QUEUE_EVT;
+ spin_unlock_irqrestore(&phba->hbalock, iflag);
+ while (!list_empty(&phba->sli4_hba.sp_queue_event)) {
/* Get the response iocb from the head of work queue */
spin_lock_irqsave(&phba->hbalock, iflag);
- list_remove_head(&phba->sli4_hba.sp_rspiocb_work_queue,
+ list_remove_head(&phba->sli4_hba.sp_queue_event,
cq_event, struct lpfc_cq_event, list);
spin_unlock_irqrestore(&phba->hbalock, iflag);
case CQE_CODE_COMPL_WQE:
irspiocbq = container_of(cq_event, struct lpfc_iocbq,
cq_event);
- lpfc_sli_sp_handle_rspiocb(phba, pring, irspiocbq);
+ /* Translate ELS WCQE to response IOCBQ */
+ irspiocbq = lpfc_sli4_els_wcqe_to_rspiocbq(phba,
+ irspiocbq);
+ if (irspiocbq)
+ lpfc_sli_sp_handle_rspiocb(phba, pring,
+ irspiocbq);
break;
case CQE_CODE_RECEIVE:
dmabuf = container_of(cq_event, struct hbq_dmabuf,
/* Check to see if any errors occurred during init */
if ((status & HS_FFERM) || (i >= 20)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "2751 Adapter failed to restart, "
+ "status reg x%x, FW Data: A8 x%x AC x%x\n",
+ status,
+ readl(phba->MBslimaddr + 0xa8),
+ readl(phba->MBslimaddr + 0xac));
phba->link_state = LPFC_HBA_ERROR;
retval = 1;
}
if (retval != MBX_SUCCESS) {
if (retval != MBX_BUSY)
mempool_free(pmb, phba->mbox_mem_pool);
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2752 KILL_BOARD command failed retval %d\n",
+ retval);
spin_lock_irq(&phba->hbalock);
phba->link_flag &= ~LS_IGNORE_ERATT;
spin_unlock_irq(&phba->hbalock);
lpfc_sli_hba_setup_error:
phba->link_state = LPFC_HBA_ERROR;
- lpfc_printf_log(phba, KERN_INFO, LOG_INIT,
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
"0445 Firmware initialization failed\n");
return rc;
}
* addition, this routine gets the port vpd data.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - could not allocated memory.
**/
static int
if (rc) {
dma_free_coherent(&phba->pcidev->dev, dma_size,
dmabuf->virt, dmabuf->phys);
+ kfree(dmabuf);
return -EIO;
}
phba->sli_rev = bf_get(lpfc_mbx_rd_rev_sli_lvl, &mqe->un.read_rev);
if (bf_get(lpfc_mbx_rd_rev_fcoe, &mqe->un.read_rev))
phba->hba_flag |= HBA_FCOE_SUPPORT;
+
+ if (bf_get(lpfc_mbx_rd_rev_cee_ver, &mqe->un.read_rev) ==
+ LPFC_DCBX_CEE_MODE)
+ phba->hba_flag |= HBA_FIP_SUPPORT;
+ else
+ phba->hba_flag &= ~HBA_FIP_SUPPORT;
+
if (phba->sli_rev != LPFC_SLI_REV4 ||
!(phba->hba_flag & HBA_FCOE_SUPPORT)) {
lpfc_printf_log(phba, KERN_ERR, LOG_MBOX | LOG_SLI,
spin_unlock_irq(&phba->hbalock);
/* Read the port's service parameters. */
- lpfc_read_sparam(phba, mboxq, vport->vpi);
+ rc = lpfc_read_sparam(phba, mboxq, vport->vpi);
+ if (rc) {
+ phba->link_state = LPFC_HBA_ERROR;
+ rc = -ENOMEM;
+ goto out_free_vpd;
+ }
+
mboxq->vport = vport;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_POLL);
mp = (struct lpfc_dmabuf *) mboxq->context1;
rc = -ENODEV;
goto out_free_vpd;
}
- if (phba->cfg_enable_fip)
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 1);
- else
- bf_set(lpfc_fip_flag, &phba->sli4_hba.sli4_flags, 0);
/* Set up all the queues to the device */
rc = lpfc_sli4_queue_setup(phba);
/* Post receive buffers to the device */
lpfc_sli4_rb_setup(phba);
+ /* Reset HBA FCF states after HBA reset */
+ phba->fcf.fcf_flag = 0;
+ phba->fcf.current_rec.flag = 0;
+
/* Start the ELS watchdog timer */
mod_timer(&vport->els_tmofunc,
jiffies + HZ * (phba->fc_ratov * 2));
for (i = 0; i < numBdes; i++) {
/* Should already be byte swapped. */
- sgl->addr_hi = bpl->addrHigh;
- sgl->addr_lo = bpl->addrLow;
- /* swap the size field back to the cpu so we
- * can assign it to the sgl.
- */
- bde.tus.w = le32_to_cpu(bpl->tus.w);
- bf_set(lpfc_sli4_sge_len, sgl, bde.tus.f.bdeSize);
+ sgl->addr_hi = bpl->addrHigh;
+ sgl->addr_lo = bpl->addrLow;
+
if ((i+1) == numBdes)
bf_set(lpfc_sli4_sge_last, sgl, 1);
else
bf_set(lpfc_sli4_sge_last, sgl, 0);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ /* swap the size field back to the cpu so we
+ * can assign it to the sgl.
+ */
+ bde.tus.w = le32_to_cpu(bpl->tus.w);
+ sgl->sge_len = cpu_to_le32(bde.tus.f.bdeSize);
bpl++;
sgl++;
}
cpu_to_le32(icmd->un.genreq64.bdl.addrHigh);
sgl->addr_lo =
cpu_to_le32(icmd->un.genreq64.bdl.addrLow);
- bf_set(lpfc_sli4_sge_len, sgl,
- icmd->un.genreq64.bdl.bdeSize);
bf_set(lpfc_sli4_sge_last, sgl, 1);
sgl->word2 = cpu_to_le32(sgl->word2);
- sgl->word3 = cpu_to_le32(sgl->word3);
+ sgl->sge_len =
+ cpu_to_le32(icmd->un.genreq64.bdl.bdeSize);
}
return sglq->sli4_xritag;
}
lpfc_sli4_iocb2wqe(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq,
union lpfc_wqe *wqe)
{
- uint32_t payload_len = 0;
+ uint32_t xmit_len = 0, total_len = 0;
uint8_t ct = 0;
uint32_t fip;
uint32_t abort_tag;
uint8_t cmnd;
uint16_t xritag;
struct ulp_bde64 *bpl = NULL;
+ uint32_t els_id = ELS_ID_DEFAULT;
+ int numBdes, i;
+ struct ulp_bde64 bde;
- fip = bf_get(lpfc_fip_flag, &phba->sli4_hba.sli4_flags);
+ fip = phba->hba_flag & HBA_FIP_SUPPORT;
/* The fcp commands will set command type */
if (iocbq->iocb_flag & LPFC_IO_FCP)
command_type = FCP_COMMAND;
- else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS))
+ else if (fip && (iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK))
command_type = ELS_COMMAND_FIP;
else
command_type = ELS_COMMAND_NON_FIP;
wqe->words[7] = 0; /* The ct field has moved so reset */
/* words0-2 bpl convert bde */
if (iocbq->iocb.un.genreq64.bdl.bdeFlags == BUFF_TYPE_BLP_64) {
+ numBdes = iocbq->iocb.un.genreq64.bdl.bdeSize /
+ sizeof(struct ulp_bde64);
bpl = (struct ulp_bde64 *)
((struct lpfc_dmabuf *)iocbq->context3)->virt;
if (!bpl)
* can assign it to the sgl.
*/
wqe->generic.bde.tus.w = le32_to_cpu(bpl->tus.w);
- payload_len = wqe->generic.bde.tus.f.bdeSize;
+ xmit_len = wqe->generic.bde.tus.f.bdeSize;
+ total_len = 0;
+ for (i = 0; i < numBdes; i++) {
+ bde.tus.w = le32_to_cpu(bpl[i].tus.w);
+ total_len += bde.tus.f.bdeSize;
+ }
} else
- payload_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
+ xmit_len = iocbq->iocb.un.fcpi64.bdl.bdeSize;
iocbq->iocb.ulpIoTag = iocbq->iotag;
cmnd = iocbq->iocb.ulpCommand;
iocbq->iocb.ulpCommand);
return IOCB_ERROR;
}
- wqe->els_req.payload_len = payload_len;
+ wqe->els_req.payload_len = xmit_len;
/* Els_reguest64 has a TMO */
bf_set(wqe_tmo, &wqe->els_req.wqe_com,
iocbq->iocb.ulpTimeout);
bf_set(lpfc_wqe_gen_ct, &wqe->generic, ct);
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
/* CCP CCPE PV PRI in word10 were set in the memcpy */
+
+ if (command_type == ELS_COMMAND_FIP) {
+ els_id = ((iocbq->iocb_flag & LPFC_FIP_ELS_ID_MASK)
+ >> LPFC_FIP_ELS_ID_SHIFT);
+ }
+ bf_set(lpfc_wqe_gen_els_id, &wqe->generic, els_id);
+
break;
+ case CMD_XMIT_SEQUENCE64_CX:
+ bf_set(lpfc_wqe_gen_context, &wqe->generic,
+ iocbq->iocb.un.ulpWord[3]);
+ wqe->generic.word3 = 0;
+ bf_set(wqe_rcvoxid, &wqe->generic, iocbq->iocb.ulpContext);
+ /* The entire sequence is transmitted for this IOCB */
+ xmit_len = total_len;
+ cmnd = CMD_XMIT_SEQUENCE64_CR;
case CMD_XMIT_SEQUENCE64_CR:
/* word3 iocb=io_tag32 wqe=payload_offset */
/* payload offset used for multilpe outstanding
/* word4 relative_offset memcpy */
/* word5 r_ctl/df_ctl memcpy */
bf_set(lpfc_wqe_gen_pu, &wqe->generic, 0);
- wqe->xmit_sequence.xmit_len = payload_len;
+ wqe->xmit_sequence.xmit_len = xmit_len;
+ command_type = OTHER_COMMAND;
break;
case CMD_XMIT_BCAST64_CN:
/* word3 iocb=iotag32 wqe=payload_len */
case CMD_FCP_IREAD64_CR:
/* FCP_CMD is always the 1st sgl entry */
wqe->fcp_iread.payload_len =
- payload_len + sizeof(struct fcp_rsp);
+ xmit_len + sizeof(struct fcp_rsp);
/* word 4 (xfer length) should have been set on the memcpy */
* sgl[1] = rsp.
*
*/
- wqe->gen_req.command_len = payload_len;
+ wqe->gen_req.command_len = xmit_len;
/* Word4 parameter copied in the memcpy */
/* Word5 [rctl, type, df_ctl, la] copied in memcpy */
/* word6 context tag copied in memcpy */
else
bf_set(abort_cmd_ia, &wqe->abort_cmd, 0);
bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG);
- abort_tag = iocbq->iocb.un.acxri.abortIoTag;
wqe->words[5] = 0;
bf_set(lpfc_wqe_gen_ct, &wqe->generic,
((iocbq->iocb.ulpCt_h << 1) | iocbq->iocb.ulpCt_l));
abort_tag = iocbq->iocb.un.acxri.abortIoTag;
- wqe->generic.abort_tag = abort_tag;
/*
* The abort handler will send us CMD_ABORT_XRI_CN or
* CMD_CLOSE_XRI_CN and the fw only accepts CMD_ABORT_XRI_CX
* iocbq from scratch.
*/
memset(wqe, 0, sizeof(union lpfc_wqe));
+ /* OX_ID is invariable to who sent ABTS to CT exchange */
bf_set(xmit_bls_rsp64_oxid, &wqe->xmit_bls_rsp,
- iocbq->iocb.un.ulpWord[3]);
- bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
- iocbq->sli4_xritag);
+ bf_get(lpfc_abts_oxid, &iocbq->iocb.un.bls_acc));
+ if (bf_get(lpfc_abts_orig, &iocbq->iocb.un.bls_acc) ==
+ LPFC_ABTS_UNSOL_INT) {
+ /* ABTS sent by initiator to CT exchange, the
+ * RX_ID field will be filled with the newly
+ * allocated responder XRI.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ iocbq->sli4_xritag);
+ } else {
+ /* ABTS sent by responder to CT exchange, the
+ * RX_ID field will be filled with the responder
+ * RX_ID from ABTS.
+ */
+ bf_set(xmit_bls_rsp64_rxid, &wqe->xmit_bls_rsp,
+ bf_get(lpfc_abts_rxid, &iocbq->iocb.un.bls_acc));
+ }
bf_set(xmit_bls_rsp64_seqcnthi, &wqe->xmit_bls_rsp, 0xffff);
bf_set(wqe_xmit_bls_pt, &wqe->xmit_bls_rsp.wqe_dest, 0x1);
bf_set(wqe_ctxt_tag, &wqe->xmit_bls_rsp.wqe_com,
uint16_t xritag;
union lpfc_wqe wqe;
struct lpfc_sli_ring *pring = &phba->sli.ring[ring_number];
- uint32_t fcp_wqidx;
if (piocb->sli4_xritag == NO_XRI) {
if (piocb->iocb.ulpCommand == CMD_ABORT_XRI_CN ||
if (lpfc_sli4_iocb2wqe(phba, piocb, &wqe))
return IOCB_ERROR;
- if (piocb->iocb_flag & LPFC_IO_FCP) {
- fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
- if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[fcp_wqidx], &wqe))
+ if ((piocb->iocb_flag & LPFC_IO_FCP) ||
+ (piocb->iocb_flag & LPFC_USE_FCPWQIDX)) {
+ /*
+ * For FCP command IOCB, get a new WQ index to distribute
+ * WQE across the WQsr. On the other hand, for abort IOCB,
+ * it carries the same WQ index to the original command
+ * IOCB.
+ */
+ if (piocb->iocb_flag & LPFC_IO_FCP)
+ piocb->fcp_wqidx = lpfc_sli4_scmd_to_wqidx_distr(phba);
+ if (lpfc_sli4_wq_put(phba->sli4_hba.fcp_wq[piocb->fcp_wqidx],
+ &wqe))
return IOCB_ERROR;
} else {
if (lpfc_sli4_wq_put(phba->sli4_hba.els_wq, &wqe))
abort_iotag = cmdiocb->iocb.un.acxri.abortIoTag;
spin_lock_irq(&phba->hbalock);
- if (abort_iotag != 0 && abort_iotag <= phba->sli.last_iotag)
- abort_iocb = phba->sli.iocbq_lookup[abort_iotag];
+ if (phba->sli_rev < LPFC_SLI_REV4) {
+ if (abort_iotag != 0 &&
+ abort_iotag <= phba->sli.last_iotag)
+ abort_iocb =
+ phba->sli.iocbq_lookup[abort_iotag];
+ } else
+ /* For sli4 the abort_tag is the XRI,
+ * so the abort routine puts the iotag of the iocb
+ * being aborted in the context field of the abort
+ * IOCB.
+ */
+ abort_iocb = phba->sli.iocbq_lookup[abort_context];
lpfc_printf_log(phba, KERN_INFO, LOG_ELS | LOG_SLI,
"0327 Cannot abort els iocb %p "
* might have completed already. Do not free it again.
*/
if (irsp->ulpStatus == IOSTAT_LOCAL_REJECT) {
- spin_unlock_irq(&phba->hbalock);
- lpfc_sli_release_iocbq(phba, cmdiocb);
- return;
+ if (irsp->un.ulpWord[4] != IOERR_NO_XRI) {
+ spin_unlock_irq(&phba->hbalock);
+ lpfc_sli_release_iocbq(phba, cmdiocb);
+ return;
+ }
+ /* For SLI4 the ulpContext field for abort IOCB
+ * holds the iotag of the IOCB being aborted so
+ * the local abort_context needs to be reset to
+ * match the aborted IOCBs ulpContext.
+ */
+ if (abort_iocb && phba->sli_rev == LPFC_SLI_REV4)
+ abort_context = abort_iocb->iocb.ulpContext;
}
/*
* make sure we have the right iocbq before taking it
abort_iocb->iocb.ulpContext != abort_context ||
(abort_iocb->iocb_flag & LPFC_DRIVER_ABORTED) == 0)
spin_unlock_irq(&phba->hbalock);
- else {
+ else if (phba->sli_rev < LPFC_SLI_REV4) {
+ /*
+ * leave the SLI4 aborted command on the txcmplq
+ * list and the command complete WCQE's XB bit
+ * will tell whether the SGL (XRI) can be released
+ * immediately or to the aborted SGL list for the
+ * following abort XRI from the HBA.
+ */
list_del_init(&abort_iocb->list);
pring->txcmplq_cnt--;
spin_unlock_irq(&phba->hbalock);
* payload, so don't free data buffer till after
* a hbeat.
*/
+ spin_lock_irq(&phba->hbalock);
abort_iocb->iocb_flag |= LPFC_DELAY_MEM_FREE;
-
abort_iocb->iocb_flag &= ~LPFC_DRIVER_ABORTED;
+ spin_unlock_irq(&phba->hbalock);
+
abort_iocb->iocb.ulpStatus = IOSTAT_LOCAL_REJECT;
- abort_iocb->iocb.un.ulpWord[4] = IOERR_SLI_ABORTED;
+ abort_iocb->iocb.un.ulpWord[4] = IOERR_ABORT_REQUESTED;
(abort_iocb->iocb_cmpl)(phba, abort_iocb, abort_iocb);
}
}
return 0;
/* This signals the response to set the correct status
- * before calling the completion handler.
+ * before calling the completion handler
*/
cmdiocb->iocb_flag |= LPFC_DRIVER_ABORTED;
iabt = &abtsiocbp->iocb;
iabt->un.acxri.abortType = ABORT_TYPE_ABTS;
iabt->un.acxri.abortContextTag = icmd->ulpContext;
- if (phba->sli_rev == LPFC_SLI_REV4)
+ if (phba->sli_rev == LPFC_SLI_REV4) {
iabt->un.acxri.abortIoTag = cmdiocb->sli4_xritag;
+ iabt->un.acxri.abortContextTag = cmdiocb->iotag;
+ }
else
iabt->un.acxri.abortIoTag = icmd->ulpIoTag;
iabt->ulpLe = 1;
iabt->ulpClass = icmd->ulpClass;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocbp->fcp_wqidx = cmdiocb->fcp_wqidx;
+ if (cmdiocb->iocb_flag & LPFC_IO_FCP)
+ abtsiocbp->iocb_flag |= LPFC_USE_FCPWQIDX;
+
if (phba->link_state >= LPFC_LINK_UP)
iabt->ulpCommand = CMD_ABORT_XRI_CN;
else
abtsiocb->iocb.ulpClass = cmd->ulpClass;
abtsiocb->vport = phba->pport;
+ /* ABTS WQE must go to the same WQ as the WQE to be aborted */
+ abtsiocb->fcp_wqidx = iocbq->fcp_wqidx;
+ if (iocbq->iocb_flag & LPFC_IO_FCP)
+ abtsiocb->iocb_flag |= LPFC_USE_FCPWQIDX;
+
if (lpfc_is_link_up(phba))
abtsiocb->iocb.ulpCommand = CMD_ABORT_XRI_CN;
else
{
wait_queue_head_t *pdone_q;
unsigned long iflags;
+ struct lpfc_scsi_buf *lpfc_cmd;
spin_lock_irqsave(&phba->hbalock, iflags);
cmdiocbq->iocb_flag |= LPFC_IO_WAKE;
memcpy(&((struct lpfc_iocbq *)cmdiocbq->context2)->iocb,
&rspiocbq->iocb, sizeof(IOCB_t));
+ /* Set the exchange busy flag for task management commands */
+ if ((cmdiocbq->iocb_flag & LPFC_IO_FCP) &&
+ !(cmdiocbq->iocb_flag & LPFC_IO_LIBDFC)) {
+ lpfc_cmd = container_of(cmdiocbq, struct lpfc_scsi_buf,
+ cur_iocbq);
+ lpfc_cmd->exch_busy = rspiocbq->iocb_flag & LPFC_EXCHANGE_BUSY;
+ }
+
pdone_q = cmdiocbq->context_un.wait_queue;
if (pdone_q)
wake_up(pdone_q);
lpfc_sli4_eratt_read(struct lpfc_hba *phba)
{
uint32_t uerr_sta_hi, uerr_sta_lo;
- uint32_t onlnreg0, onlnreg1;
/* For now, use the SLI4 device internal unrecoverable error
* registers for error attention. This can be changed later.
*/
- onlnreg0 = readl(phba->sli4_hba.ONLINE0regaddr);
- onlnreg1 = readl(phba->sli4_hba.ONLINE1regaddr);
- if ((onlnreg0 != LPFC_ONLINE_NERR) || (onlnreg1 != LPFC_ONLINE_NERR)) {
- uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
- uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
- if (uerr_sta_lo || uerr_sta_hi) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "1423 HBA Unrecoverable error: "
- "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
- "online0_reg=0x%x, online1_reg=0x%x\n",
- uerr_sta_lo, uerr_sta_hi,
- onlnreg0, onlnreg1);
- phba->work_status[0] = uerr_sta_lo;
- phba->work_status[1] = uerr_sta_hi;
- /* Set the driver HA work bitmap */
- phba->work_ha |= HA_ERATT;
- /* Indicate polling handles this ERATT */
- phba->hba_flag |= HBA_ERATT_HANDLED;
- return 1;
- }
+ uerr_sta_lo = readl(phba->sli4_hba.UERRLOregaddr);
+ uerr_sta_hi = readl(phba->sli4_hba.UERRHIregaddr);
+ if ((~phba->sli4_hba.ue_mask_lo & uerr_sta_lo) ||
+ (~phba->sli4_hba.ue_mask_hi & uerr_sta_hi)) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
+ "1423 HBA Unrecoverable error: "
+ "uerr_lo_reg=0x%x, uerr_hi_reg=0x%x, "
+ "ue_mask_lo_reg=0x%x, ue_mask_hi_reg=0x%x\n",
+ uerr_sta_lo, uerr_sta_hi,
+ phba->sli4_hba.ue_mask_lo,
+ phba->sli4_hba.ue_mask_hi);
+ phba->work_status[0] = uerr_sta_lo;
+ phba->work_status[1] = uerr_sta_hi;
+ /* Set the driver HA work bitmap */
+ phba->work_ha |= HA_ERATT;
+ /* Indicate polling handles this ERATT */
+ phba->hba_flag |= HBA_ERATT_HANDLED;
+ return 1;
}
return 0;
}
lpfc_sli_sp_intr_handler(int irq, void *dev_id)
{
struct lpfc_hba *phba;
- uint32_t ha_copy;
+ uint32_t ha_copy, hc_copy;
uint32_t work_ha_copy;
unsigned long status;
unsigned long iflag;
}
/* Clear up only attention source related to slow-path */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R2INT_ENA |
+ HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((ha_copy & (HA_MBATT | HA_R2_CLR_MSK)),
phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock_irqrestore(&phba->hbalock, iflag);
} else
struct lpfc_hba *phba;
irqreturn_t sp_irq_rc, fp_irq_rc;
unsigned long status1, status2;
+ uint32_t hc_copy;
/*
* Get the driver's phba structure from the dev_id and
}
/* Clear attention sources except link and error attentions */
+ hc_copy = readl(phba->HCregaddr);
+ writel(hc_copy & ~(HC_MBINT_ENA | HC_R0INT_ENA | HC_R1INT_ENA
+ | HC_R2INT_ENA | HC_LAINT_ENA | HC_ERINT_ENA),
+ phba->HCregaddr);
writel((phba->ha_copy & ~(HA_LATT | HA_ERATT)), phba->HAregaddr);
+ writel(hc_copy, phba->HCregaddr);
readl(phba->HAregaddr); /* flush */
spin_unlock(&phba->hbalock);
}
}
+/**
+ * lpfc_sli4_iocb_param_transfer - Transfer pIocbOut and cmpl status to pIocbIn
+ * @phba: pointer to lpfc hba data structure
+ * @pIocbIn: pointer to the rspiocbq
+ * @pIocbOut: pointer to the cmdiocbq
+ * @wcqe: pointer to the complete wcqe
+ *
+ * This routine transfers the fields of a command iocbq to a response iocbq
+ * by copying all the IOCB fields from command iocbq and transferring the
+ * completion status information from the complete wcqe.
+ **/
static void
-lpfc_sli4_iocb_param_transfer(struct lpfc_iocbq *pIocbIn,
+lpfc_sli4_iocb_param_transfer(struct lpfc_hba *phba,
+ struct lpfc_iocbq *pIocbIn,
struct lpfc_iocbq *pIocbOut,
struct lpfc_wcqe_complete *wcqe)
{
+ unsigned long iflags;
size_t offset = offsetof(struct lpfc_iocbq, iocb);
memcpy((char *)pIocbIn + offset, (char *)pIocbOut + offset,
sizeof(struct lpfc_iocbq) - offset);
- pIocbIn->cq_event.cqe.wcqe_cmpl = *wcqe;
/* Map WCQE parameters into irspiocb parameters */
pIocbIn->iocb.ulpStatus = bf_get(lpfc_wcqe_c_status, wcqe);
if (pIocbOut->iocb_flag & LPFC_IO_FCP)
wcqe->total_data_placed;
else
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
- else
+ else {
pIocbIn->iocb.un.ulpWord[4] = wcqe->parameter;
+ pIocbIn->iocb.un.genreq64.bdl.bdeSize = wcqe->total_data_placed;
+ }
+
+ /* Pick up HBA exchange busy condition */
+ if (bf_get(lpfc_wcqe_c_xb, wcqe)) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pIocbIn->iocb_flag |= LPFC_EXCHANGE_BUSY;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
+}
+
+/**
+ * lpfc_sli4_els_wcqe_to_rspiocbq - Get response iocbq from els wcqe
+ * @phba: Pointer to HBA context object.
+ * @wcqe: Pointer to work-queue completion queue entry.
+ *
+ * This routine handles an ELS work-queue completion event and construct
+ * a pseudo response ELS IODBQ from the SLI4 ELS WCQE for the common
+ * discovery engine to handle.
+ *
+ * Return: Pointer to the receive IOCBQ, NULL otherwise.
+ **/
+static struct lpfc_iocbq *
+lpfc_sli4_els_wcqe_to_rspiocbq(struct lpfc_hba *phba,
+ struct lpfc_iocbq *irspiocbq)
+{
+ struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
+ struct lpfc_iocbq *cmdiocbq;
+ struct lpfc_wcqe_complete *wcqe;
+ unsigned long iflags;
+
+ wcqe = &irspiocbq->cq_event.cqe.wcqe_cmpl;
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ pring->stats.iocb_event++;
+ /* Look up the ELS command IOCB and create pseudo response IOCB */
+ cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+
+ if (unlikely(!cmdiocbq)) {
+ lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
+ "0386 ELS complete with no corresponding "
+ "cmdiocb: iotag (%d)\n",
+ bf_get(lpfc_wcqe_c_request_tag, wcqe));
+ lpfc_sli_release_iocbq(phba, irspiocbq);
+ return NULL;
+ }
+
+ /* Fake the irspiocbq and copy necessary response information */
+ lpfc_sli4_iocb_param_transfer(phba, irspiocbq, cmdiocbq, wcqe);
+
+ return irspiocbq;
}
/**
lpfc_sli4_sp_handle_els_wcqe(struct lpfc_hba *phba,
struct lpfc_wcqe_complete *wcqe)
{
- struct lpfc_sli_ring *pring = &phba->sli.ring[LPFC_ELS_RING];
- struct lpfc_iocbq *cmdiocbq;
struct lpfc_iocbq *irspiocbq;
unsigned long iflags;
- bool workposted = false;
-
- spin_lock_irqsave(&phba->hbalock, iflags);
- pring->stats.iocb_event++;
- /* Look up the ELS command IOCB and create pseudo response IOCB */
- cmdiocbq = lpfc_sli_iocbq_lookup_by_tag(phba, pring,
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- spin_unlock_irqrestore(&phba->hbalock, iflags);
-
- if (unlikely(!cmdiocbq)) {
- lpfc_printf_log(phba, KERN_WARNING, LOG_SLI,
- "0386 ELS complete with no corresponding "
- "cmdiocb: iotag (%d)\n",
- bf_get(lpfc_wcqe_c_request_tag, wcqe));
- return workposted;
- }
- /* Fake the irspiocbq and copy necessary response information */
+ /* Get an irspiocbq for later ELS response processing use */
irspiocbq = lpfc_sli_get_iocbq(phba);
if (!irspiocbq) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0387 Failed to allocate an iocbq\n");
- return workposted;
+ return false;
}
- lpfc_sli4_iocb_param_transfer(irspiocbq, cmdiocbq, wcqe);
- /* Add the irspiocb to the response IOCB work list */
+ /* Save off the slow-path queue event for work thread to process */
+ memcpy(&irspiocbq->cq_event.cqe.wcqe_cmpl, wcqe, sizeof(*wcqe));
spin_lock_irqsave(&phba->hbalock, iflags);
list_add_tail(&irspiocbq->cq_event.list,
- &phba->sli4_hba.sp_rspiocb_work_queue);
- /* Indicate ELS ring attention */
- phba->work_ha |= (HA_R0ATT << (4*LPFC_ELS_RING));
+ &phba->sli4_hba.sp_queue_event);
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
- workposted = true;
- return workposted;
+ return true;
}
/**
uint32_t status;
unsigned long iflags;
- lpfc_sli4_rq_release(hrq, drq);
- if (bf_get(lpfc_rcqe_code, rcqe) != CQE_CODE_RECEIVE)
- goto out;
if (bf_get(lpfc_rcqe_rq_id, rcqe) != hrq->queue_id)
goto out;
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"2537 Receive Frame Truncated!!\n");
case FC_STATUS_RQ_SUCCESS:
+ lpfc_sli4_rq_release(hrq, drq);
spin_lock_irqsave(&phba->hbalock, iflags);
dma_buf = lpfc_sli_hbqbuf_get(&phba->hbqs[0].hbq_buffer_list);
if (!dma_buf) {
memcpy(&dma_buf->cq_event.cqe.rcqe_cmpl, rcqe, sizeof(*rcqe));
/* save off the frame for the word thread to process */
list_add_tail(&dma_buf->cq_event.list,
- &phba->sli4_hba.sp_rspiocb_work_queue);
+ &phba->sli4_hba.sp_queue_event);
/* Frame received */
- phba->hba_flag |= HBA_RECEIVE_BUFFER;
+ phba->hba_flag |= HBA_SP_QUEUE_EVT;
spin_unlock_irqrestore(&phba->hbalock, iflags);
workposted = true;
break;
}
out:
return workposted;
-
}
/**
lpfc_sli4_sp_handle_cqe(struct lpfc_hba *phba, struct lpfc_queue *cq,
struct lpfc_cqe *cqe)
{
- struct lpfc_wcqe_complete wcqe;
+ struct lpfc_cqe cqevt;
bool workposted = false;
/* Copy the work queue CQE and convert endian order if needed */
- lpfc_sli_pcimem_bcopy(cqe, &wcqe, sizeof(struct lpfc_cqe));
+ lpfc_sli_pcimem_bcopy(cqe, &cqevt, sizeof(struct lpfc_cqe));
/* Check and process for different type of WCQE and dispatch */
- switch (bf_get(lpfc_wcqe_c_code, &wcqe)) {
+ switch (bf_get(lpfc_cqe_code, &cqevt)) {
case CQE_CODE_COMPL_WQE:
- /* Process the WQ complete event */
+ /* Process the WQ/RQ complete event */
workposted = lpfc_sli4_sp_handle_els_wcqe(phba,
- (struct lpfc_wcqe_complete *)&wcqe);
+ (struct lpfc_wcqe_complete *)&cqevt);
break;
case CQE_CODE_RELEASE_WQE:
/* Process the WQ release event */
lpfc_sli4_sp_handle_rel_wcqe(phba,
- (struct lpfc_wcqe_release *)&wcqe);
+ (struct lpfc_wcqe_release *)&cqevt);
break;
case CQE_CODE_XRI_ABORTED:
/* Process the WQ XRI abort event */
workposted = lpfc_sli4_sp_handle_abort_xri_wcqe(phba, cq,
- (struct sli4_wcqe_xri_aborted *)&wcqe);
+ (struct sli4_wcqe_xri_aborted *)&cqevt);
break;
case CQE_CODE_RECEIVE:
/* Process the RQ event */
workposted = lpfc_sli4_sp_handle_rcqe(phba,
- (struct lpfc_rcqe *)&wcqe);
+ (struct lpfc_rcqe *)&cqevt);
break;
default:
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0388 Not a valid WCQE code: x%x\n",
- bf_get(lpfc_wcqe_c_code, &wcqe));
+ bf_get(lpfc_cqe_code, &cqevt));
break;
}
return workposted;
int ecount = 0;
uint16_t cqid;
- if (bf_get(lpfc_eqe_major_code, eqe) != 0 ||
- bf_get(lpfc_eqe_minor_code, eqe) != 0) {
+ if (bf_get(lpfc_eqe_major_code, eqe) != 0) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0359 Not a valid slow-path completion "
"event: majorcode=x%x, minorcode=x%x\n",
}
/* Fake the irspiocb and copy necessary response information */
- lpfc_sli4_iocb_param_transfer(&irspiocbq, cmdiocbq, wcqe);
+ lpfc_sli4_iocb_param_transfer(phba, &irspiocbq, cmdiocbq, wcqe);
+
+ if (cmdiocbq->iocb_flag & LPFC_DRIVER_ABORTED) {
+ spin_lock_irqsave(&phba->hbalock, iflags);
+ cmdiocbq->iocb_flag &= ~LPFC_DRIVER_ABORTED;
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
+ }
/* Pass the cmd_iocb and the rsp state to the upper layer */
(cmdiocbq->iocb_cmpl)(phba, cmdiocbq, &irspiocbq);
uint16_t cqid;
int ecount = 0;
- if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0) ||
- unlikely(bf_get(lpfc_eqe_minor_code, eqe) != 0)) {
+ if (unlikely(bf_get(lpfc_eqe_major_code, eqe) != 0)) {
lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
"0366 Not a valid fast-path completion "
"event: majorcode=x%x, minorcode=x%x\n",
}
/**
+ * lpfc_update_rcv_time_stamp - Update vport's rcv seq time stamp
+ * @vport: The vport to work on.
+ *
+ * This function updates the receive sequence time stamp for this vport. The
+ * receive sequence time stamp indicates the time that the last frame of the
+ * the sequence that has been idle for the longest amount of time was received.
+ * the driver uses this time stamp to indicate if any received sequences have
+ * timed out.
+ **/
+void
+lpfc_update_rcv_time_stamp(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* get the oldest sequence on the rcv list */
+ h_buf = list_get_first(&vport->rcv_buffer_list,
+ struct lpfc_dmabuf, list);
+ if (!h_buf)
+ return;
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ vport->rcv_buffer_time_stamp = dmabuf->time_stamp;
+}
+
+/**
+ * lpfc_cleanup_rcv_buffers - Cleans up all outstanding receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function cleans up all outstanding received sequences. This is called
+ * by the driver when a link event or user action invalidates all the received
+ * sequences.
+ **/
+void
+lpfc_cleanup_rcv_buffers(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+}
+
+/**
+ * lpfc_rcv_seq_check_edtov - Cleans up timed out receive sequences.
+ * @vport: The vport that the received sequences were sent to.
+ *
+ * This function determines whether any received sequences have timed out by
+ * first checking the vport's rcv_buffer_time_stamp. If this time_stamp
+ * indicates that there is at least one timed out sequence this routine will
+ * go through the received sequences one at a time from most inactive to most
+ * active to determine which ones need to be cleaned up. Once it has determined
+ * that a sequence needs to be cleaned up it will simply free up the resources
+ * without sending an abort.
+ **/
+void
+lpfc_rcv_seq_check_edtov(struct lpfc_vport *vport)
+{
+ struct lpfc_dmabuf *h_buf, *hnext;
+ struct lpfc_dmabuf *d_buf, *dnext;
+ struct hbq_dmabuf *dmabuf = NULL;
+ unsigned long timeout;
+ int abort_count = 0;
+
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ vport->rcv_buffer_time_stamp);
+ if (list_empty(&vport->rcv_buffer_list) ||
+ time_before(jiffies, timeout))
+ return;
+ /* start with the oldest sequence on the rcv list */
+ list_for_each_entry_safe(h_buf, hnext, &vport->rcv_buffer_list, list) {
+ dmabuf = container_of(h_buf, struct hbq_dmabuf, hbuf);
+ timeout = (msecs_to_jiffies(vport->phba->fc_edtov) +
+ dmabuf->time_stamp);
+ if (time_before(jiffies, timeout))
+ break;
+ abort_count++;
+ list_del_init(&dmabuf->hbuf.list);
+ list_for_each_entry_safe(d_buf, dnext,
+ &dmabuf->dbuf.list, list) {
+ list_del_init(&d_buf->list);
+ lpfc_in_buf_free(vport->phba, d_buf);
+ }
+ lpfc_in_buf_free(vport->phba, &dmabuf->dbuf);
+ }
+ if (abort_count)
+ lpfc_update_rcv_time_stamp(vport);
+}
+
+/**
* lpfc_fc_frame_add - Adds a frame to the vport's list of received sequences
* @dmabuf: pointer to a dmabuf that describes the hdr and data of the FC frame
*
struct hbq_dmabuf *temp_dmabuf = NULL;
INIT_LIST_HEAD(&dmabuf->dbuf.list);
+ dmabuf->time_stamp = jiffies;
new_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
/* Use the hdr_buf to find the sequence that this frame belongs to */
list_for_each_entry(h_buf, &vport->rcv_buffer_list, list) {
* Queue the buffer on the vport's rcv_buffer_list.
*/
list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
temp_hdr = seq_dmabuf->hbuf.virt;
- if (new_hdr->fh_seq_cnt < temp_hdr->fh_seq_cnt) {
+ if (be16_to_cpu(new_hdr->fh_seq_cnt) <
+ be16_to_cpu(temp_hdr->fh_seq_cnt)) {
list_del_init(&seq_dmabuf->hbuf.list);
list_add_tail(&dmabuf->hbuf.list, &vport->rcv_buffer_list);
list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
return dmabuf;
}
+ /* move this sequence to the tail to indicate a young sequence */
+ list_move_tail(&seq_dmabuf->hbuf.list, &vport->rcv_buffer_list);
+ seq_dmabuf->time_stamp = jiffies;
+ lpfc_update_rcv_time_stamp(vport);
+ if (list_empty(&seq_dmabuf->dbuf.list)) {
+ temp_hdr = dmabuf->hbuf.virt;
+ list_add_tail(&dmabuf->dbuf.list, &seq_dmabuf->dbuf.list);
+ return seq_dmabuf;
+ }
/* find the correct place in the sequence to insert this frame */
list_for_each_entry_reverse(d_buf, &seq_dmabuf->dbuf.list, list) {
temp_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
* If the frame's sequence count is greater than the frame on
* the list then insert the frame right after this frame
*/
- if (new_hdr->fh_seq_cnt > temp_hdr->fh_seq_cnt) {
+ if (be16_to_cpu(new_hdr->fh_seq_cnt) >
+ be16_to_cpu(temp_hdr->fh_seq_cnt)) {
list_add(&dmabuf->dbuf.list, &temp_dmabuf->dbuf.list);
return seq_dmabuf;
}
{
struct lpfc_iocbq *ctiocb = NULL;
struct lpfc_nodelist *ndlp;
- uint16_t oxid;
- uint32_t sid;
+ uint16_t oxid, rxid;
+ uint32_t sid, fctl;
IOCB_t *icmd;
if (!lpfc_is_link_up(phba))
sid = sli4_sid_from_fc_hdr(fc_hdr);
oxid = be16_to_cpu(fc_hdr->fh_ox_id);
+ rxid = be16_to_cpu(fc_hdr->fh_rx_id);
ndlp = lpfc_findnode_did(phba->pport, sid);
if (!ndlp) {
if (!ctiocb)
return;
+ /* Extract the F_CTL field from FC_HDR */
+ fctl = sli4_fctl_from_fc_hdr(fc_hdr);
+
icmd = &ctiocb->iocb;
- icmd->un.xseq64.bdl.ulpIoTag32 = 0;
icmd->un.xseq64.bdl.bdeSize = 0;
+ icmd->un.xseq64.bdl.ulpIoTag32 = 0;
icmd->un.xseq64.w5.hcsw.Dfctl = 0;
icmd->un.xseq64.w5.hcsw.Rctl = FC_RCTL_BA_ACC;
icmd->un.xseq64.w5.hcsw.Type = FC_TYPE_BLS;
icmd->ulpLe = 1;
icmd->ulpClass = CLASS3;
icmd->ulpContext = ndlp->nlp_rpi;
- icmd->un.ulpWord[3] = oxid;
- ctiocb->sli4_xritag = NO_XRI;
ctiocb->iocb_cmpl = NULL;
ctiocb->vport = phba->pport;
ctiocb->iocb_cmpl = lpfc_sli4_seq_abort_acc_cmpl;
+ if (fctl & FC_FC_EX_CTX) {
+ /* ABTS sent by responder to CT exchange, construction
+ * of BA_ACC will use OX_ID from ABTS for the XRI_TAG
+ * field and RX_ID from ABTS for RX_ID field.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_RSP);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, rxid);
+ ctiocb->sli4_xritag = oxid;
+ } else {
+ /* ABTS sent by initiator to CT exchange, construction
+ * of BA_ACC will need to allocate a new XRI as for the
+ * XRI_TAG and RX_ID fields.
+ */
+ bf_set(lpfc_abts_orig, &icmd->un.bls_acc, LPFC_ABTS_UNSOL_INT);
+ bf_set(lpfc_abts_rxid, &icmd->un.bls_acc, NO_XRI);
+ ctiocb->sli4_xritag = NO_XRI;
+ }
+ bf_set(lpfc_abts_oxid, &icmd->un.bls_acc, oxid);
+
/* Xmit CT abts accept on exchange <xid> */
lpfc_printf_log(phba, KERN_INFO, LOG_ELS,
"1200 Xmit CT ABTS ACC on exchange x%x Data: x%x\n",
{
struct lpfc_hba *phba = vport->phba;
struct fc_frame_header fc_hdr;
+ uint32_t fctl;
bool abts_par;
- /* Try to abort partially assembled seq */
- abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
-
/* Make a copy of fc_hdr before the dmabuf being released */
memcpy(&fc_hdr, dmabuf->hbuf.virt, sizeof(struct fc_frame_header));
+ fctl = sli4_fctl_from_fc_hdr(&fc_hdr);
- /* Send abort to ULP if partially seq abort failed */
- if (abts_par == false)
- lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
- else
+ if (fctl & FC_FC_EX_CTX) {
+ /*
+ * ABTS sent by responder to exchange, just free the buffer
+ */
lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ } else {
+ /*
+ * ABTS sent by initiator to exchange, need to do cleanup
+ */
+ /* Try to abort partially assembled seq */
+ abts_par = lpfc_sli4_abort_partial_seq(vport, dmabuf);
+
+ /* Send abort to ULP if partially seq abort failed */
+ if (abts_par == false)
+ lpfc_sli4_send_seq_to_ulp(vport, dmabuf);
+ else
+ lpfc_in_buf_free(phba, &dmabuf->dbuf);
+ }
/* Send basic accept (BA_ACC) to the abort requester */
lpfc_sli4_seq_abort_acc(phba, &fc_hdr);
}
seq_dmabuf = container_of(d_buf, struct hbq_dmabuf, dbuf);
hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
/* If there is a hole in the sequence count then fail. */
- if (++seq_count != hdr->fh_seq_cnt)
+ if (++seq_count != be16_to_cpu(hdr->fh_seq_cnt))
return 0;
fctl = (hdr->fh_f_ctl[0] << 16 |
hdr->fh_f_ctl[1] << 8 |
struct lpfc_iocbq *first_iocbq, *iocbq;
struct fc_frame_header *fc_hdr;
uint32_t sid;
+ struct ulp_bde64 *pbde;
fc_hdr = (struct fc_frame_header *)seq_dmabuf->hbuf.virt;
/* remove from receive buffer list */
list_del_init(&seq_dmabuf->hbuf.list);
+ lpfc_update_rcv_time_stamp(vport);
/* get the Remote Port's SID */
sid = sli4_sid_from_fc_hdr(fc_hdr);
/* Get an iocbq struct to fill in. */
if (!iocbq->context3) {
iocbq->context3 = d_buf;
iocbq->iocb.ulpBdeCount++;
- iocbq->iocb.unsli3.rcvsli3.bde2.tus.f.bdeSize =
- LPFC_DATA_BUF_SIZE;
+ pbde = (struct ulp_bde64 *)
+ &iocbq->iocb.unsli3.sli3Words[4];
+ pbde->tus.f.bdeSize = LPFC_DATA_BUF_SIZE;
first_iocbq->iocb.unsli3.rcvsli3.acc_len +=
bf_get(lpfc_rcqe_length,
&seq_dmabuf->cq_event.cqe.rcqe_cmpl);
struct lpfc_vport *vport;
uint32_t fcfi;
- /* Clear hba flag and get all received buffers into the cmplq */
- spin_lock_irq(&phba->hbalock);
- phba->hba_flag &= ~HBA_RECEIVE_BUFFER;
- spin_unlock_irq(&phba->hbalock);
-
/* Process each received buffer */
fc_hdr = (struct fc_frame_header *)dmabuf->hbuf.virt;
/* check to see if this a valid type of frame */
}
fcfi = bf_get(lpfc_rcqe_fcf_id, &dmabuf->cq_event.cqe.rcqe_cmpl);
vport = lpfc_fc_frame_to_vport(phba, fc_hdr, fcfi);
- if (!vport) {
+ if (!vport || !(vport->vpi_state & LPFC_VPI_REGISTERED)) {
/* throw out the frame */
lpfc_in_buf_free(phba, &dmabuf->dbuf);
return;
return;
}
/* If not last frame in sequence continue processing frames. */
- if (!lpfc_seq_complete(seq_dmabuf)) {
- /*
- * When saving off frames post a new one and mark this
- * frame to be freed when it is finished.
- **/
- lpfc_sli_hbqbuf_fill_hbqs(phba, LPFC_ELS_HBQ, 1);
- dmabuf->tag = -1;
+ if (!lpfc_seq_complete(seq_dmabuf))
return;
- }
+
/* Send the complete sequence to the upper layer protocol */
lpfc_sli4_send_seq_to_ulp(vport, seq_dmabuf);
}
* sequential.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* EIO - The mailbox failed to complete successfully.
* When this error occurs, the driver is not guaranteed
* to have any rpi regions posted to the device and
* maps up to 64 rpi context regions.
*
* Return codes
- * 0 - sucessful
+ * 0 - successful
* ENOMEM - No available memory
* EIO - The mailbox failed to complete successfully.
**/
* PAGE_SIZE modulo 64 rpi context headers.
*
* Returns
- * A nonzero rpi defined as rpi_base <= rpi < max_rpi if sucessful
+ * A nonzero rpi defined as rpi_base <= rpi < max_rpi if successful
* LPFC_RPI_ALLOC_ERROR if no rpis are available.
**/
int
}
/**
- * lpfc_sli4_read_fcf_record - Read the driver's default FCF Record.
+ * lpfc_sli4_fcf_scan_read_fcf_rec - Read hba fcf record for fcf scan.
* @phba: pointer to lpfc hba data structure.
* @fcf_index: FCF table entry offset.
*
- * This routine is invoked to read up to @fcf_num of FCF record from the
- * device starting with the given @fcf_index.
+ * This routine is invoked to scan the entire FCF table by reading FCF
+ * record and processing it one at a time starting from the @fcf_index
+ * for initial FCF discovery or fast FCF failover rediscovery.
+ *
+ * Return 0 if the mailbox command is submitted sucessfully, none 0
+ * otherwise.
**/
int
-lpfc_sli4_read_fcf_record(struct lpfc_hba *phba, uint16_t fcf_index)
+lpfc_sli4_fcf_scan_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
{
int rc = 0, error;
LPFC_MBOXQ_t *mboxq;
- void *virt_addr;
- dma_addr_t phys_addr;
- uint8_t *bytep;
- struct lpfc_mbx_sge sge;
- uint32_t alloc_len, req_len;
- struct lpfc_mbx_read_fcf_tbl *read_fcf;
phba->fcoe_eventtag_at_fcf_scan = phba->fcoe_eventtag;
mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
"2000 Failed to allocate mbox for "
"READ_FCF cmd\n");
error = -ENOMEM;
- goto fail_fcfscan;
+ goto fail_fcf_scan;
}
-
- req_len = sizeof(struct fcf_record) +
- sizeof(union lpfc_sli4_cfg_shdr) + 2 * sizeof(uint32_t);
-
- /* Set up READ_FCF SLI4_CONFIG mailbox-ioctl command */
- alloc_len = lpfc_sli4_config(phba, mboxq, LPFC_MBOX_SUBSYSTEM_FCOE,
- LPFC_MBOX_OPCODE_FCOE_READ_FCF_TABLE, req_len,
- LPFC_SLI4_MBX_NEMBED);
-
- if (alloc_len < req_len) {
- lpfc_printf_log(phba, KERN_ERR, LOG_INIT,
- "0291 Allocated DMA memory size (x%x) is "
- "less than the requested DMA memory "
- "size (x%x)\n", alloc_len, req_len);
- error = -ENOMEM;
- goto fail_fcfscan;
+ /* Construct the read FCF record mailbox command */
+ rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index);
+ if (rc) {
+ error = -EINVAL;
+ goto fail_fcf_scan;
}
-
- /* Get the first SGE entry from the non-embedded DMA memory. This
- * routine only uses a single SGE.
- */
- lpfc_sli4_mbx_sge_get(mboxq, 0, &sge);
- phys_addr = getPaddr(sge.pa_hi, sge.pa_lo);
- virt_addr = mboxq->sge_array->addr[0];
- read_fcf = (struct lpfc_mbx_read_fcf_tbl *)virt_addr;
-
- /* Set up command fields */
- bf_set(lpfc_mbx_read_fcf_tbl_indx, &read_fcf->u.request, fcf_index);
- /* Perform necessary endian conversion */
- bytep = virt_addr + sizeof(union lpfc_sli4_cfg_shdr);
- lpfc_sli_pcimem_bcopy(bytep, bytep, sizeof(uint32_t));
+ /* Issue the mailbox command asynchronously */
mboxq->vport = phba->pport;
- mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_record;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_fcf_scan_read_fcf_rec;
rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
- if (rc == MBX_NOT_FINISHED) {
+ if (rc == MBX_NOT_FINISHED)
error = -EIO;
- } else {
+ else {
spin_lock_irq(&phba->hbalock);
phba->hba_flag |= FCF_DISC_INPROGRESS;
spin_unlock_irq(&phba->hbalock);
+ /* Reset FCF round robin index bmask for new scan */
+ if (fcf_index == LPFC_FCOE_FCF_GET_FIRST)
+ memset(phba->fcf.fcf_rr_bmask, 0,
+ sizeof(*phba->fcf.fcf_rr_bmask));
error = 0;
}
-fail_fcfscan:
+fail_fcf_scan:
if (error) {
if (mboxq)
lpfc_sli4_mbox_cmd_free(phba, mboxq);
}
/**
+ * lpfc_sli4_fcf_rr_read_fcf_rec - Read hba fcf record for round robin fcf.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: FCF table entry offset.
+ *
+ * This routine is invoked to read an FCF record indicated by @fcf_index
+ * and to use it for FLOGI round robin FCF failover.
+ *
+ * Return 0 if the mailbox command is submitted sucessfully, none 0
+ * otherwise.
+ **/
+int
+lpfc_sli4_fcf_rr_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ int rc = 0, error;
+ LPFC_MBOXQ_t *mboxq;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_INIT,
+ "2763 Failed to allocate mbox for "
+ "READ_FCF cmd\n");
+ error = -ENOMEM;
+ goto fail_fcf_read;
+ }
+ /* Construct the read FCF record mailbox command */
+ rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index);
+ if (rc) {
+ error = -EINVAL;
+ goto fail_fcf_read;
+ }
+ /* Issue the mailbox command asynchronously */
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_fcf_rr_read_fcf_rec;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ error = -EIO;
+ else
+ error = 0;
+
+fail_fcf_read:
+ if (error && mboxq)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return error;
+}
+
+/**
+ * lpfc_sli4_read_fcf_rec - Read hba fcf record for update eligible fcf bmask.
+ * @phba: pointer to lpfc hba data structure.
+ * @fcf_index: FCF table entry offset.
+ *
+ * This routine is invoked to read an FCF record indicated by @fcf_index to
+ * determine whether it's eligible for FLOGI round robin failover list.
+ *
+ * Return 0 if the mailbox command is submitted sucessfully, none 0
+ * otherwise.
+ **/
+int
+lpfc_sli4_read_fcf_rec(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ int rc = 0, error;
+ LPFC_MBOXQ_t *mboxq;
+
+ mboxq = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mboxq) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP | LOG_INIT,
+ "2758 Failed to allocate mbox for "
+ "READ_FCF cmd\n");
+ error = -ENOMEM;
+ goto fail_fcf_read;
+ }
+ /* Construct the read FCF record mailbox command */
+ rc = lpfc_sli4_mbx_read_fcf_rec(phba, mboxq, fcf_index);
+ if (rc) {
+ error = -EINVAL;
+ goto fail_fcf_read;
+ }
+ /* Issue the mailbox command asynchronously */
+ mboxq->vport = phba->pport;
+ mboxq->mbox_cmpl = lpfc_mbx_cmpl_read_fcf_rec;
+ rc = lpfc_sli_issue_mbox(phba, mboxq, MBX_NOWAIT);
+ if (rc == MBX_NOT_FINISHED)
+ error = -EIO;
+ else
+ error = 0;
+
+fail_fcf_read:
+ if (error && mboxq)
+ lpfc_sli4_mbox_cmd_free(phba, mboxq);
+ return error;
+}
+
+/**
+ * lpfc_sli4_fcf_rr_next_index_get - Get next eligible fcf record index
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is to get the next eligible FCF record index in a round
+ * robin fashion. If the next eligible FCF record index equals to the
+ * initial round robin FCF record index, LPFC_FCOE_FCF_NEXT_NONE (0xFFFF)
+ * shall be returned, otherwise, the next eligible FCF record's index
+ * shall be returned.
+ **/
+uint16_t
+lpfc_sli4_fcf_rr_next_index_get(struct lpfc_hba *phba)
+{
+ uint16_t next_fcf_index;
+
+ /* Search from the currently registered FCF index */
+ next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
+ LPFC_SLI4_FCF_TBL_INDX_MAX,
+ phba->fcf.current_rec.fcf_indx);
+ /* Wrap around condition on phba->fcf.fcf_rr_bmask */
+ if (next_fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX)
+ next_fcf_index = find_next_bit(phba->fcf.fcf_rr_bmask,
+ LPFC_SLI4_FCF_TBL_INDX_MAX, 0);
+ /* Round robin failover stop condition */
+ if (next_fcf_index == phba->fcf.fcf_rr_init_indx)
+ return LPFC_FCOE_FCF_NEXT_NONE;
+
+ return next_fcf_index;
+}
+
+/**
+ * lpfc_sli4_fcf_rr_index_set - Set bmask with eligible fcf record index
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine sets the FCF record index in to the eligible bmask for
+ * round robin failover search. It checks to make sure that the index
+ * does not go beyond the range of the driver allocated bmask dimension
+ * before setting the bit.
+ *
+ * Returns 0 if the index bit successfully set, otherwise, it returns
+ * -EINVAL.
+ **/
+int
+lpfc_sli4_fcf_rr_index_set(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ "2610 HBA FCF index reached driver's "
+ "book keeping dimension: fcf_index:%d, "
+ "driver_bmask_max:%d\n",
+ fcf_index, LPFC_SLI4_FCF_TBL_INDX_MAX);
+ return -EINVAL;
+ }
+ /* Set the eligible FCF record index bmask */
+ set_bit(fcf_index, phba->fcf.fcf_rr_bmask);
+
+ return 0;
+}
+
+/**
+ * lpfc_sli4_fcf_rr_index_set - Clear bmask from eligible fcf record index
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine clears the FCF record index from the eligible bmask for
+ * round robin failover search. It checks to make sure that the index
+ * does not go beyond the range of the driver allocated bmask dimension
+ * before clearing the bit.
+ **/
+void
+lpfc_sli4_fcf_rr_index_clear(struct lpfc_hba *phba, uint16_t fcf_index)
+{
+ if (fcf_index >= LPFC_SLI4_FCF_TBL_INDX_MAX) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ "2762 HBA FCF index goes beyond driver's "
+ "book keeping dimension: fcf_index:%d, "
+ "driver_bmask_max:%d\n",
+ fcf_index, LPFC_SLI4_FCF_TBL_INDX_MAX);
+ return;
+ }
+ /* Clear the eligible FCF record index bmask */
+ clear_bit(fcf_index, phba->fcf.fcf_rr_bmask);
+}
+
+/**
+ * lpfc_mbx_cmpl_redisc_fcf_table - completion routine for rediscover FCF table
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is the completion routine for the rediscover FCF table mailbox
+ * command. If the mailbox command returned failure, it will try to stop the
+ * FCF rediscover wait timer.
+ **/
+void
+lpfc_mbx_cmpl_redisc_fcf_table(struct lpfc_hba *phba, LPFC_MBOXQ_t *mbox)
+{
+ struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;
+ uint32_t shdr_status, shdr_add_status;
+
+ redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl;
+
+ shdr_status = bf_get(lpfc_mbox_hdr_status,
+ &redisc_fcf->header.cfg_shdr.response);
+ shdr_add_status = bf_get(lpfc_mbox_hdr_add_status,
+ &redisc_fcf->header.cfg_shdr.response);
+ if (shdr_status || shdr_add_status) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_FIP,
+ "2746 Requesting for FCF rediscovery failed "
+ "status x%x add_status x%x\n",
+ shdr_status, shdr_add_status);
+ if (phba->fcf.fcf_flag & FCF_ACVL_DISC) {
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_ACVL_DISC;
+ spin_unlock_irq(&phba->hbalock);
+ /*
+ * CVL event triggered FCF rediscover request failed,
+ * last resort to re-try current registered FCF entry.
+ */
+ lpfc_retry_pport_discovery(phba);
+ } else {
+ spin_lock_irq(&phba->hbalock);
+ phba->fcf.fcf_flag &= ~FCF_DEAD_DISC;
+ spin_unlock_irq(&phba->hbalock);
+ /*
+ * DEAD FCF event triggered FCF rediscover request
+ * failed, last resort to fail over as a link down
+ * to FCF registration.
+ */
+ lpfc_sli4_fcf_dead_failthrough(phba);
+ }
+ } else {
+ lpfc_printf_log(phba, KERN_INFO, LOG_FIP,
+ "2775 Start FCF rediscovery quiescent period "
+ "wait timer before scaning FCF table\n");
+ /*
+ * Start FCF rediscovery wait timer for pending FCF
+ * before rescan FCF record table.
+ */
+ lpfc_fcf_redisc_wait_start_timer(phba);
+ }
+
+ mempool_free(mbox, phba->mbox_mem_pool);
+}
+
+/**
+ * lpfc_sli4_redisc_all_fcf - Request to rediscover entire FCF table by port.
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This routine is invoked to request for rediscovery of the entire FCF table
+ * by the port.
+ **/
+int
+lpfc_sli4_redisc_fcf_table(struct lpfc_hba *phba)
+{
+ LPFC_MBOXQ_t *mbox;
+ struct lpfc_mbx_redisc_fcf_tbl *redisc_fcf;
+ int rc, length;
+
+ /* Cancel retry delay timers to all vports before FCF rediscover */
+ lpfc_cancel_all_vport_retry_delay_timer(phba);
+
+ mbox = mempool_alloc(phba->mbox_mem_pool, GFP_KERNEL);
+ if (!mbox) {
+ lpfc_printf_log(phba, KERN_ERR, LOG_SLI,
+ "2745 Failed to allocate mbox for "
+ "requesting FCF rediscover.\n");
+ return -ENOMEM;
+ }
+
+ length = (sizeof(struct lpfc_mbx_redisc_fcf_tbl) -
+ sizeof(struct lpfc_sli4_cfg_mhdr));
+ lpfc_sli4_config(phba, mbox, LPFC_MBOX_SUBSYSTEM_FCOE,
+ LPFC_MBOX_OPCODE_FCOE_REDISCOVER_FCF,
+ length, LPFC_SLI4_MBX_EMBED);
+
+ redisc_fcf = &mbox->u.mqe.un.redisc_fcf_tbl;
+ /* Set count to 0 for invalidating the entire FCF database */
+ bf_set(lpfc_mbx_redisc_fcf_count, redisc_fcf, 0);
+
+ /* Issue the mailbox command asynchronously */
+ mbox->vport = phba->pport;
+ mbox->mbox_cmpl = lpfc_mbx_cmpl_redisc_fcf_table;
+ rc = lpfc_sli_issue_mbox(phba, mbox, MBX_NOWAIT);
+
+ if (rc == MBX_NOT_FINISHED) {
+ mempool_free(mbox, phba->mbox_mem_pool);
+ return -EIO;
+ }
+ return 0;
+}
+
+/**
+ * lpfc_sli4_fcf_dead_failthrough - Failthrough routine to fcf dead event
+ * @phba: pointer to lpfc hba data structure.
+ *
+ * This function is the failover routine as a last resort to the FCF DEAD
+ * event when driver failed to perform fast FCF failover.
+ **/
+void
+lpfc_sli4_fcf_dead_failthrough(struct lpfc_hba *phba)
+{
+ uint32_t link_state;
+
+ /*
+ * Last resort as FCF DEAD event failover will treat this as
+ * a link down, but save the link state because we don't want
+ * it to be changed to Link Down unless it is already down.
+ */
+ link_state = phba->link_state;
+ lpfc_linkdown(phba);
+ phba->link_state = link_state;
+
+ /* Unregister FCF if no devices connected to it */
+ lpfc_unregister_unused_fcf(phba);
+}
+
+/**
* lpfc_sli_read_link_ste - Read region 23 to decide if link is disabled.
* @phba: pointer to lpfc hba data structure.
*
kfree(rgn23_data);
return;
}
+
+/**
+ * lpfc_cleanup_pending_mbox - Free up vport discovery mailbox commands.
+ * @vport: pointer to vport data structure.
+ *
+ * This function iterate through the mailboxq and clean up all REG_LOGIN
+ * and REG_VPI mailbox commands associated with the vport. This function
+ * is called when driver want to restart discovery of the vport due to
+ * a Clear Virtual Link event.
+ **/
+void
+lpfc_cleanup_pending_mbox(struct lpfc_vport *vport)
+{
+ struct lpfc_hba *phba = vport->phba;
+ LPFC_MBOXQ_t *mb, *nextmb;
+ struct lpfc_dmabuf *mp;
+
+ spin_lock_irq(&phba->hbalock);
+ list_for_each_entry_safe(mb, nextmb, &phba->sli.mboxq, list) {
+ if (mb->vport != vport)
+ continue;
+
+ if ((mb->u.mb.mbxCommand != MBX_REG_LOGIN64) &&
+ (mb->u.mb.mbxCommand != MBX_REG_VPI))
+ continue;
+
+ if (mb->u.mb.mbxCommand == MBX_REG_LOGIN64) {
+ mp = (struct lpfc_dmabuf *) (mb->context1);
+ if (mp) {
+ __lpfc_mbuf_free(phba, mp->virt, mp->phys);
+ kfree(mp);
+ }
+ }
+ list_del(&mb->list);
+ mempool_free(mb, phba->mbox_mem_pool);
+ }
+ mb = phba->sli.mbox_active;
+ if (mb && (mb->vport == vport)) {
+ if ((mb->u.mb.mbxCommand == MBX_REG_LOGIN64) ||
+ (mb->u.mb.mbxCommand == MBX_REG_VPI))
+ mb->mbox_cmpl = lpfc_sli_def_mbox_cmpl;
+ }
+ spin_unlock_irq(&phba->hbalock);
+}
+