| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE |
| * or http://www.opensolaris.org/os/licensing. |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| * |
| * When distributing Covered Code, include this CDDL HEADER in each |
| * file and include the License file at usr/src/OPENSOLARIS.LICENSE. |
| * If applicable, add the following below this CDDL HEADER, with the |
| * fields enclosed by brackets "[]" replaced with your own identifying |
| * information: Portions Copyright [yyyy] [name of copyright owner] |
| * |
| * CDDL HEADER END |
| */ |
| |
| /* |
| * Copyright 2014 QLogic Corporation |
| * The contents of this file are subject to the terms of the |
| * QLogic End User License (the "License"). |
| * You may not use this file except in compliance with the License. |
| * |
| * You can obtain a copy of the License at |
| * http://www.qlogic.com/Resources/Documents/DriverDownloadHelp/ |
| * QLogic_End_User_Software_License.txt |
| * See the License for the specific language governing permissions |
| * and limitations under the License. |
| */ |
| |
| /* |
| * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved. |
| */ |
| |
| #include "bnxe.h" |
| |
| /* |
| * The interrupt status bit vector is as follows: |
| * |
| * bit 0: default interrupt |
| * |
| * Single Mode: |
| * |
| * bits 1-16: Function 1 (RSS 0-15) |
| * |
| * Multi-Function Mode: |
| * |
| * bits 1-4: Virtual Function 1 (RSS 0-3) |
| * bits 5-8: Virtual Function 2 (RSS 4-7) |
| * bits 9-12: Virtual Function 3 (RSS 8-11) |
| * bits 13-16: Virtual Function 4 (RSS 12-15) |
| * |
| * While processing interrupts the programmatic index used for the default |
| * status block is 16 and the RSS status blocks are shifted down one (i.e. |
| * 0-15). |
| * |
| * Solaris defaults to 2 MSIX interrupts per function so only the default |
| * interrupt plus one RSS interrupt is used. This default behavior can be |
| * modified via the /etc/system configuration file. |
| */ |
| |
| |
| static inline char * BnxeIntrTypeName(int intrType) |
| { |
| return (intrType == DDI_INTR_TYPE_MSIX) ? "MSIX" : |
| (intrType == DDI_INTR_TYPE_MSI) ? "MSI" : |
| (intrType == DDI_INTR_TYPE_FIXED) ? "FIXED" : |
| "UNKNOWN"; |
| } |
| |
| |
| static void BnxeFindDmaHandles(um_device_t * pUM) |
| { |
| lm_address_t physAddr; |
| BnxeMemDma * pTmp; |
| u32_t idx; |
| |
| BNXE_LOCK_ENTER_MEM(pUM); |
| |
| /* find the RSS status blocks */ |
| |
| LM_FOREACH_SB_ID(&pUM->lm_dev, idx) |
| { |
| if (CHIP_IS_E1x(&pUM->lm_dev)) |
| { |
| physAddr.as_u32.low = |
| pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e1x_sb_data.common.host_sb_addr.lo; |
| physAddr.as_u32.high = |
| pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e1x_sb_data.common.host_sb_addr.hi; |
| } |
| else |
| { |
| physAddr.as_u32.low = |
| pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e2_sb_data.common.host_sb_addr.lo; |
| physAddr.as_u32.high = |
| pUM->lm_dev.vars.status_blocks_arr[idx].hc_status_block_data.e2_sb_data.common.host_sb_addr.hi; |
| } |
| |
| pTmp = (BnxeMemDma *)d_list_peek_head(&pUM->memDmaList); |
| while (pTmp) |
| { |
| if (pTmp->physAddr.as_ptr == physAddr.as_ptr) |
| { |
| break; |
| } |
| |
| pTmp = (BnxeMemDma *)d_list_next_entry(&pTmp->link); |
| } |
| |
| if (pTmp == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to find DMA handle for RSS status block %d", idx); |
| } |
| |
| pUM->statusBlocks[idx] = pTmp; |
| } |
| |
| /* find the default status block */ |
| |
| pTmp = (BnxeMemDma *)d_list_peek_head(&pUM->memDmaList); |
| while (pTmp) |
| { |
| if (pTmp->physAddr.as_ptr == |
| pUM->lm_dev.vars.gen_sp_status_block.blk_phy_address.as_ptr) |
| { |
| break; |
| } |
| |
| pTmp = (BnxeMemDma *)d_list_next_entry(&pTmp->link); |
| } |
| |
| if (pTmp == NULL) |
| { |
| BnxeLogWarn(pUM, "Failed to find DMA handle for default status block"); |
| } |
| |
| pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX] = pTmp; |
| |
| BNXE_LOCK_EXIT_MEM(pUM); |
| } |
| |
| |
| void BnxeIntrIguSbEnable(um_device_t * pUM, |
| u32_t idx, |
| boolean_t fromISR) |
| { |
| RxQueue * pRxQ = &pUM->rxq[idx]; |
| u32_t igu_id = (FCOE_CID(&pUM->lm_dev) == idx) ? |
| LM_NON_RSS_SB(&pUM->lm_dev) : idx; |
| |
| BNXE_LOCK_ENTER_INTR_FLIP(pUM, igu_id); |
| |
| if (fromISR) |
| { |
| /* |
| * If in an ISR and poll mode is ON then poll mode was flipped in the |
| * ISR which can occur during Rx processing. If this is the case then |
| * don't do anything. Only re-enable the IGU when poll mode is OFF. |
| */ |
| if (!pRxQ->inPollMode) |
| { |
| lm_int_ack_sb_enable(&pUM->lm_dev, igu_id); |
| } |
| } |
| else |
| { |
| if (!pRxQ->inPollMode) |
| { |
| /* Why are intrs getting enabled on the ring twice...? */ |
| cmn_err(CE_PANIC, |
| "%s: Ring %d, enable intrs and NOT in poll mode!", |
| BnxeDevName(pUM), igu_id); |
| } |
| |
| atomic_swap_32(&pRxQ->inPollMode, B_FALSE); |
| pRxQ->intrEnableCnt++; |
| |
| lm_int_ack_sb_enable(&pUM->lm_dev, igu_id); |
| } |
| |
| BNXE_LOCK_EXIT_INTR_FLIP(pUM, igu_id); |
| } |
| |
| |
| void BnxeIntrIguSbDisable(um_device_t * pUM, |
| u32_t idx, |
| boolean_t fromISR) |
| { |
| RxQueue * pRxQ = &pUM->rxq[idx]; |
| u32_t igu_id = (FCOE_CID(&pUM->lm_dev) == idx) ? |
| LM_NON_RSS_SB(&pUM->lm_dev) : idx; |
| |
| BNXE_LOCK_ENTER_INTR_FLIP(pUM, igu_id); |
| |
| if (fromISR) |
| { |
| /* we should never get here when in poll mode... */ |
| ASSERT(pRxQ->inPollMode == B_FALSE); |
| lm_int_ack_sb_disable(&pUM->lm_dev, igu_id); |
| } |
| else |
| { |
| if (pRxQ->inPollMode) |
| { |
| /* Why are intrs getting disabled on the ring twice...? */ |
| cmn_err(CE_PANIC, |
| "%s: Ring %d, disable intrs and ALREADY in poll mode!", |
| BnxeDevName(pUM), igu_id); |
| } |
| |
| /* |
| * Note here that the interrupt can already be disabled if GLDv3 |
| * is disabling the interrupt under the context of an ISR. This is |
| * OK as the inPollMode flag will tell the ISR not to re-enable the |
| * interrupt upon return. |
| */ |
| |
| lm_int_ack_sb_disable(&pUM->lm_dev, igu_id); |
| |
| atomic_swap_32(&pRxQ->inPollMode, B_TRUE); |
| pRxQ->intrDisableCnt++; |
| } |
| |
| BNXE_LOCK_EXIT_INTR_FLIP(pUM, igu_id); |
| } |
| |
| |
| static void BnxeServiceDefSbIntr(um_device_t * pUM, |
| boolean_t * pPktsRxed, |
| boolean_t * pPktsTxed) |
| { |
| lm_device_t * pLM = (lm_device_t *)pUM; |
| u32_t activity_flg = 0; |
| u16_t lcl_attn_bits = 0; |
| u16_t lcl_attn_ack = 0; |
| u16_t asserted_proc_grps = 0; |
| u16_t deasserted_proc_grps = 0; |
| |
| *pPktsRxed = B_FALSE; |
| *pPktsTxed = B_FALSE; |
| |
| BnxeLogDbg(pUM, "Default INTR: Handling default status block %d", DEF_STATUS_BLOCK_INDEX); |
| |
| ddi_dma_sync(pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX]->dmaHandle, |
| 0, 0, DDI_DMA_SYNC_FORKERNEL); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckDmaHandle(pUM->statusBlocks[DEF_STATUS_BLOCK_IGU_INDEX]->dmaHandle) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| pUM->intrSbCnt[DEF_STATUS_BLOCK_IGU_INDEX]++; |
| |
| if (lm_is_def_sb_updated(pLM) == 0) |
| { |
| BnxeLogDbg(pUM, "Default INTR: No change in default status index so bail!"); |
| pUM->intrSbNoChangeCnt[DEF_STATUS_BLOCK_IGU_INDEX]++; |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| return; |
| } |
| |
| /* get a local copy of the indices from the status block */ |
| lm_update_def_hc_indices(pLM, DEF_STATUS_BLOCK_INDEX, &activity_flg); |
| |
| BnxeDbgBreakIfFastPath(pUM, !(activity_flg & LM_DEF_EVENT_MASK)); |
| |
| BnxeLogDbg(pUM, "Default INTR: processing events on sb: %x, events: 0x%x", |
| DEF_STATUS_BLOCK_INDEX, activity_flg); |
| |
| if (activity_flg & LM_DEF_ATTN_ACTIVE) |
| { |
| /* Attentions! Usually these are bad things we don't want to see */ |
| |
| lm_get_attn_info(pLM, &lcl_attn_bits, &lcl_attn_ack); |
| |
| // NOTE: in case the status index of the attention has changed |
| // already (while processing), we could override with it our local |
| // copy. However, this is not a must, since it will be caught at the |
| // end of the loop with the call to lm_is_sb_updated(). In case the |
| // dpc_loop_cnt has exhausted, no worry, since will get an interrupt |
| // for that at a later time. |
| |
| // find out which lines are asserted/deasserted with account to |
| // their states, ASSERT if necessary. |
| GET_ATTN_CHNG_GROUPS(pLM, lcl_attn_bits, lcl_attn_ack, |
| &asserted_proc_grps, &deasserted_proc_grps); |
| |
| BnxeLogDbg(pUM, "Default INTR: asserted_proc_grps: 0x%x, deasserted_proc_grps:0x%x", |
| asserted_proc_grps, deasserted_proc_grps); |
| |
| if (asserted_proc_grps) |
| { |
| lm_handle_assertion_processing(pLM, asserted_proc_grps); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| } |
| |
| // keep in mind that in the same round, it is possible that this |
| // func will have processing to do regarding deassertion on bits |
| // that are different than the ones processed earlier for assertion |
| // processing. |
| |
| if (deasserted_proc_grps) |
| { |
| lm_handle_deassertion_processing(pLM, deasserted_proc_grps); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| } |
| } |
| |
| if (activity_flg & LM_DEF_USTORM_ACTIVE) |
| { |
| /* Check for L4 TOE/iSCSI/FCoE Rx completions. */ |
| |
| if (lm_is_rx_completion(pLM, ISCSI_CID(pLM))) |
| { |
| BnxeDbgBreakMsg(pUM, "Unknown iSCSI Rx completion!"); |
| } |
| |
| if (lm_is_rx_completion(pLM, FCOE_CID(pLM))) |
| { |
| *pPktsRxed = B_TRUE; |
| } |
| } |
| |
| if (activity_flg & LM_DEF_CSTORM_ACTIVE) |
| { |
| if (lm_is_eq_completion(pLM)) |
| { |
| lm_service_eq_intr(pLM); |
| } |
| |
| if (lm_is_tx_completion(pLM, FWD_CID(pLM))) |
| { |
| /* XXX Possible? */ |
| *pPktsTxed = B_TRUE; |
| } |
| |
| if (lm_is_tx_completion(pLM, ISCSI_CID(pLM))) |
| { |
| /* XXX iSCSI Tx. NO! */ |
| BnxeDbgBreakMsg(pUM, "Unknown iSCSI Tx completion!"); |
| } |
| |
| if (lm_is_tx_completion(pLM, FCOE_CID(pLM))) |
| { |
| *pPktsTxed = B_TRUE; |
| } |
| } |
| } |
| |
| |
| /* |
| * This is the polling path for an individual Rx Ring. Here we simply pull |
| * any pending packets out of the hardware and put them into the wait queue. |
| * Note that there might already be packets in the wait queue which is OK as |
| * the caller will call BnxeRxRingProcess() next to process the queue. |
| */ |
| void BnxePollRxRing(um_device_t * pUM, |
| u32_t idx, |
| boolean_t * pPktsRxed, |
| boolean_t * pPktsTxed) |
| { |
| lm_device_t * pLM = (lm_device_t *)pUM; |
| u32_t activity_flg = 0; |
| u8_t drv_rss_id = (u8_t)idx; |
| |
| *pPktsRxed = B_FALSE; |
| *pPktsTxed = B_FALSE; |
| |
| BnxeLogDbg(pUM, "Ring Poll: Handling status block sb_id:%d drv_rss_id:%d", |
| idx, drv_rss_id); |
| |
| /* use drv_rss_id for mapping into status block array (from LM) */ |
| ddi_dma_sync(pUM->statusBlocks[drv_rss_id]->dmaHandle, |
| 0, 0, DDI_DMA_SYNC_FORKERNEL); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckDmaHandle(pUM->statusBlocks[drv_rss_id]->dmaHandle) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| pUM->intrSbPollCnt[drv_rss_id]++; |
| |
| if (lm_is_sb_updated(pLM, drv_rss_id) == 0) |
| { |
| BnxeLogDbg(pUM, "Ring Poll: No change in status index so bail!"); |
| pUM->intrSbPollNoChangeCnt[drv_rss_id]++; |
| return; |
| } |
| |
| /* get a local copy of the indices from the status block */ |
| lm_update_fp_hc_indices(pLM, drv_rss_id, &activity_flg, &drv_rss_id); |
| |
| BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK)); |
| |
| BnxeLogDbg(pUM, "Ring Poll: processing events on sb: %x, events: 0x%x", |
| drv_rss_id, activity_flg); |
| |
| if (activity_flg & LM_NON_DEF_USTORM_ACTIVE) |
| { |
| /* Rx Completions */ |
| if (lm_is_rx_completion(pLM, drv_rss_id)) |
| { |
| *pPktsRxed = B_TRUE; |
| } |
| |
| /* XXX Check for L4 TOE/FCoE Rx completions. NO! */ |
| } |
| |
| if (activity_flg & LM_NON_DEF_CSTORM_ACTIVE) |
| { |
| /* Tx completions */ |
| if (lm_is_tx_completion(pLM, drv_rss_id)) |
| { |
| *pPktsTxed = B_TRUE; |
| } |
| |
| /* XXX Check for L4 Tx and L5 EQ completions. NO! */ |
| } |
| } |
| |
| |
| /* |
| * This is the polling path for the FCoE Ring. Here we don't pull any |
| * pending packets out of the hardware. We only care about FCoE Fast Path |
| * completions. FCoE slow path L2 packets are processed via the default |
| * status block not the LM_NON_RSS_SB. In this path we're assuming that |
| * the FCoE driver is performing a crashdump. |
| */ |
| void BnxePollRxRingFCOE(um_device_t * pUM) |
| { |
| lm_device_t * pLM = (lm_device_t *)pUM; |
| u32_t activity_flg = 0; |
| |
| u8_t sb_id = LM_NON_RSS_SB(pLM); |
| u8_t drv_rss_id = FCOE_CID(pLM); |
| |
| BnxeLogDbg(pUM, "Ring Poll FCoE: Handling status block sb_id:%d drv_rss_id:%d", |
| sb_id, drv_rss_id); |
| |
| /* use sb_id for mapping into status block array (from LM) */ |
| ddi_dma_sync(pUM->statusBlocks[sb_id]->dmaHandle, |
| 0, 0, DDI_DMA_SYNC_FORKERNEL); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckDmaHandle(pUM->statusBlocks[sb_id]->dmaHandle) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| pUM->intrSbPollCnt[sb_id]++; |
| |
| if (lm_is_sb_updated(pLM, sb_id) == 0) |
| { |
| BnxeLogDbg(pUM, "Ring Poll FCoE: No change in status index so bail!"); |
| pUM->intrSbPollNoChangeCnt[sb_id]++; |
| return; |
| } |
| |
| /* get a local copy of the indices from the status block */ |
| lm_update_fp_hc_indices(pLM, sb_id, &activity_flg, &drv_rss_id); |
| |
| BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK)); |
| |
| BnxeLogDbg(pUM, "Ring Poll FCoE: processing events on sb: %x, events: 0x%x", |
| sb_id, activity_flg); |
| |
| if (activity_flg & LM_NON_DEF_USTORM_ACTIVE) |
| { |
| if (lm_fc_is_eq_completion(pLM, drv_rss_id)) |
| { |
| lm_fc_service_eq_intr(pLM, drv_rss_id); |
| } |
| } |
| } |
| |
| |
| static void BnxeServiceSbIntr(um_device_t * pUM, |
| u8_t sb_id, |
| boolean_t * pPktsRxed, |
| boolean_t * pPktsTxed) |
| { |
| lm_device_t * pLM = (lm_device_t *)pUM; |
| u32_t activity_flg = 0; |
| u8_t drv_rss_id; |
| |
| *pPktsRxed = B_FALSE; |
| *pPktsTxed = B_FALSE; |
| |
| drv_rss_id = lm_map_igu_sb_id_to_drv_rss(pLM, sb_id); |
| |
| BnxeLogDbg(pUM, "Ring INTR: Handling status block sb_id:%d drv_rss_id:%d", |
| sb_id, drv_rss_id); |
| |
| /* use sb_id for mapping into status block array (from LM) */ |
| ddi_dma_sync(pUM->statusBlocks[sb_id]->dmaHandle, |
| 0, 0, DDI_DMA_SYNC_FORKERNEL); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckDmaHandle(pUM->statusBlocks[sb_id]->dmaHandle) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| pUM->intrSbCnt[sb_id]++; |
| |
| if (lm_is_sb_updated(pLM, sb_id) == 0) |
| { |
| BnxeLogDbg(pUM, "Ring INTR: No change in status index so bail!"); |
| pUM->intrSbNoChangeCnt[sb_id]++; |
| return; |
| } |
| |
| /* |
| * get a local copy of the indices from the status block |
| * XXX note that here drv_rss_id is assigned to the sb_id |
| */ |
| lm_update_fp_hc_indices(pLM, sb_id, &activity_flg, &drv_rss_id); |
| |
| BnxeDbgBreakIf(pUM, !(activity_flg & LM_NON_DEF_EVENT_MASK)); |
| |
| BnxeLogDbg(pUM, "Ring INTR: processing events on sb: %x, events: 0x%x", |
| drv_rss_id, activity_flg); |
| |
| if (activity_flg & LM_NON_DEF_USTORM_ACTIVE) |
| { |
| /* Rx Completions */ |
| if (lm_is_rx_completion(pLM, drv_rss_id)) |
| { |
| *pPktsRxed = B_TRUE; |
| } |
| |
| if (lm_fc_is_eq_completion(pLM, drv_rss_id)) |
| { |
| lm_fc_service_eq_intr(pLM, drv_rss_id); |
| } |
| |
| /* XXX Check for ISCSI-OOO and L4 TOE Rx completions. NO! */ |
| } |
| |
| if (activity_flg & LM_NON_DEF_CSTORM_ACTIVE) |
| { |
| /* Tx completions */ |
| if (lm_is_tx_completion(pLM, drv_rss_id)) |
| { |
| *pPktsTxed = B_TRUE; |
| } |
| |
| /* XXX Check for L4 Tx and L5 EQ completions. NO! */ |
| |
| /* L4 Tx completions */ |
| if (lm_toe_is_tx_completion(pLM, drv_rss_id)) |
| { |
| BnxeDbgBreakMsg(pUM, "Unknown TOE Tx completion!"); |
| } |
| |
| /* L5 EQ completions */ |
| if (lm_sc_is_eq_completion(pLM, drv_rss_id)) |
| { |
| BnxeDbgBreakMsg(pUM, "Unknown iSCSI EQ completion!"); |
| //lm_sc_service_eq_intr(pLM, drv_rss_id); |
| } |
| } |
| } |
| |
| |
| uint_t BnxeIntrISR(caddr_t arg1, caddr_t arg2) |
| { |
| um_device_t * pUM = (um_device_t *)arg1; |
| lm_device_t * pLM = &pUM->lm_dev; |
| lm_interrupt_status_t intrStatus = 0; |
| boolean_t pktsRxed = 0; |
| boolean_t pktsTxed = 0; |
| u32_t rss_id = 0; |
| int idx = (int)(uintptr_t)arg2; |
| |
| BNXE_LOCK_ENTER_INTR(pUM, idx); |
| |
| if (!pUM->intrEnabled) |
| { |
| pLM->vars.dbg_intr_in_wrong_state++; |
| |
| BNXE_LOCK_EXIT_INTR(pUM, idx); |
| return DDI_INTR_UNCLAIMED; |
| } |
| |
| BnxeLogDbg(pUM, "-> BNXE INTA Interrupt <-"); |
| |
| if (pLM->vars.enable_intr) |
| { |
| intrStatus = lm_get_interrupt_status(pLM); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pLM->vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| if (intrStatus == 0) |
| { |
| pLM->vars.dbg_intr_zero_status++; |
| |
| BNXE_LOCK_EXIT_INTR(pUM, idx); |
| return DDI_INTR_UNCLAIMED; |
| } |
| } |
| else |
| { |
| pLM->vars.dbg_intr_in_disabled++; |
| BnxeLogDbg(pUM, "INTA INTR: we got an interrupt when disabled"); |
| |
| BNXE_LOCK_EXIT_INTR(pUM, idx); |
| return DDI_INTR_CLAIMED; |
| } |
| |
| atomic_add_64((volatile uint64_t *)&pUM->intrFired, 1); |
| |
| while (intrStatus) |
| { |
| if (intrStatus & 0x1) |
| { |
| if (rss_id == 0) |
| { |
| lm_int_ack_def_sb_disable(pLM); |
| |
| BnxeServiceDefSbIntr(pUM, &pktsRxed, &pktsTxed); |
| |
| /* |
| * Default sb only handles FCoE only right now. If this changes |
| * BnxeServiceDefSbIntr will have to change to return which CIDs |
| * have packets pending. |
| */ |
| |
| if (pktsTxed) BnxeTxRingProcess(pUM, FCOE_CID(pLM)); |
| if (pktsRxed) BnxeRxRingProcess(pUM, FCOE_CID(pLM), B_FALSE, 0); |
| |
| lm_sq_post_pending(pLM); |
| |
| lm_int_ack_def_sb_enable(pLM); |
| } |
| else |
| { |
| /* |
| * (rss_id - 1) is used because the non-default sbs are located |
| * in lm_device at indices 0-15. |
| */ |
| |
| lm_int_ack_sb_disable(pLM, (rss_id - 1)); |
| |
| BnxeServiceSbIntr(pUM, (rss_id - 1), &pktsRxed, &pktsTxed); |
| |
| if (pktsTxed) BnxeTxRingProcess(pUM, (rss_id - 1)); |
| if (pktsRxed) BnxeRxRingProcess(pUM, (rss_id - 1), B_FALSE, 0); |
| |
| lm_sq_post_pending(pLM); |
| |
| lm_int_ack_sb_enable(pLM, (rss_id - 1)); |
| } |
| } |
| |
| intrStatus >>= 1; |
| rss_id++; |
| } |
| |
| BNXE_LOCK_EXIT_INTR(pUM, idx); |
| |
| return DDI_INTR_CLAIMED; |
| } |
| |
| |
| uint_t BnxeIntrMISR(caddr_t arg1, caddr_t arg2) |
| { |
| um_device_t * pUM = (um_device_t *)arg1; |
| lm_device_t * pLM = &pUM->lm_dev; |
| boolean_t pktsRxed = 0; |
| boolean_t pktsTxed = 0; |
| int sb_id = (int)(uintptr_t)arg2; |
| u32_t idx; |
| |
| BNXE_LOCK_ENTER_INTR(pUM, sb_id); |
| |
| if (!pUM->intrEnabled) |
| { |
| pLM->vars.dbg_intr_in_wrong_state++; |
| |
| BNXE_LOCK_EXIT_INTR(pUM, sb_id); |
| return DDI_INTR_UNCLAIMED; |
| } |
| |
| BnxeLogDbg(pUM, "-> BNXE MSIX Interrupt SB %d <-", sb_id); |
| |
| if (!pLM->vars.enable_intr) |
| { |
| pLM->vars.dbg_intr_in_disabled++; |
| BnxeLogDbg(pUM, "MISR INTR: we got an interrupt when disabled"); |
| |
| BNXE_LOCK_EXIT_INTR(pUM, sb_id); |
| return DDI_INTR_CLAIMED; |
| } |
| |
| atomic_add_64((volatile uint64_t *)&pUM->intrFired, 1); |
| |
| if (sb_id == DEF_STATUS_BLOCK_IGU_INDEX) |
| { |
| lm_int_ack_def_sb_disable(pLM); |
| |
| BnxeServiceDefSbIntr(pUM, &pktsRxed, &pktsTxed); |
| |
| /* |
| * Default sb only handles FCoE only right now. If this changes |
| * BnxeServiceDefSbIntr will have to change to return which CIDs |
| * have packets pending. |
| */ |
| |
| if (pktsTxed) BnxeTxRingProcess(pUM, FCOE_CID(pLM)); |
| if (pktsRxed) BnxeRxRingProcess(pUM, FCOE_CID(pLM), FALSE, 0); |
| |
| lm_sq_post_pending(pLM); |
| |
| lm_int_ack_def_sb_enable(pLM); |
| } |
| else |
| { |
| /* |
| * Note that polling is not allowed by GLDv3 on the LM_NON_RSS_SB when |
| * overlapped with FCoE. This is enforced by the BnxeRxRingIntrEnable |
| * and BnxeRxRingIntrDisable routines. The FCoE driver IS ALLOWED to |
| * put the SB into poll mode. FCoE trumps GLDv3/L2 and it's assumed |
| * the FCoE driver is performing a crashdump in this case. |
| */ |
| |
| idx = ((sb_id == LM_NON_RSS_SB(pLM)) && |
| CLIENT_BOUND(pUM, LM_CLI_IDX_FCOE) && |
| (pUM->rssIntr.intrCount == LM_MAX_RSS_CHAINS(&pUM->lm_dev))) ? |
| FCOE_CID(pLM) : sb_id; |
| |
| if (pUM->rxq[idx].inPollMode) |
| { |
| /* Shouldn't be here! */ |
| cmn_err(CE_PANIC, |
| "%s: Interupt on RSS/MSIX ring %d when in poll mode!", |
| BnxeDevName(pUM), idx); |
| } |
| |
| /* accounts for poll mode */ |
| BnxeIntrIguSbDisable(pUM, idx, B_TRUE); |
| |
| BnxeServiceSbIntr(pUM, sb_id, &pktsRxed, &pktsTxed); |
| |
| if (pktsTxed) BnxeTxRingProcess(pUM, sb_id); |
| if (pktsRxed) BnxeRxRingProcess(pUM, sb_id, B_FALSE, 0); |
| |
| lm_sq_post_pending(pLM); |
| |
| /* accounts for poll mode */ |
| BnxeIntrIguSbEnable(pUM, idx, B_TRUE); |
| } |
| |
| BNXE_LOCK_EXIT_INTR(pUM, sb_id); |
| |
| return DDI_INTR_CLAIMED; |
| } |
| |
| |
| static int BnxeGetInterruptCount(dev_info_t * pDev, int type, int intrTypes) |
| { |
| int nintrs = 0; |
| |
| if (intrTypes & type) |
| { |
| return (ddi_intr_get_nintrs(pDev, type, &nintrs) != DDI_SUCCESS) ? |
| -1 : nintrs; |
| } |
| |
| return -1; |
| } |
| |
| |
| static boolean_t BnxeIntrBlockAlloc(um_device_t * pUM, |
| int intrInum, |
| int intrCnt, |
| BnxeIntrBlock * pBlock) |
| |
| { |
| dev_info_t * pDev = pUM->pDev; |
| int intrRequest; |
| int intrActual; |
| int rc, i; |
| |
| if ((pUM->intrType == DDI_INTR_TYPE_FIXED) && (intrCnt != 1)) |
| { |
| return B_FALSE; |
| } |
| |
| intrRequest = intrCnt; |
| intrActual = 0; |
| |
| /* |
| * We need to allocate an interrupt block array of maximum size which is |
| * MAX_RSS_CHAINS plus one for the default interrupt. Even though we |
| * won't allocate all of those handlers the "inum" value passed to |
| * ddi_intr_alloc() determines the starting index where the handlers |
| * will be allocated. See the multi-function block offset documentation |
| * at the top of this file. |
| */ |
| pBlock->intrHandleBlockSize = |
| ((MAX_RSS_CHAINS + 1) * sizeof(ddi_intr_handle_t)); |
| |
| if ((pBlock->pIntrHandleBlockAlloc = |
| (ddi_intr_handle_t *)kmem_zalloc(pBlock->intrHandleBlockSize, |
| KM_SLEEP)) == NULL) |
| { |
| BnxeLogWarn(pUM, "Memory alloc failed for isr handle block (inum=%d)!", |
| intrInum); |
| return B_FALSE; |
| } |
| |
| if ((rc = ddi_intr_alloc(pDev, |
| pBlock->pIntrHandleBlockAlloc, |
| pUM->intrType, |
| intrInum, |
| intrRequest, |
| &intrActual, |
| DDI_INTR_ALLOC_NORMAL)) != DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate isr handle block (%d) (inum=%d cnt=%d)!", |
| rc, intrInum, intrRequest); |
| kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize); |
| return B_FALSE; |
| } |
| |
| /* |
| * Point 'pIntrHandleBlock' to the starting interrupt index in the |
| * allocated interrupt block array. This is done so we can easily enable, |
| * disable, free, etc the interrupts. For 10u8 and beyond the inum value |
| * is also used as an index into the interrupt block so we point |
| * pIntrHandleBlock to the inum'th index. For 10u7 and below all |
| * interrupt allocations start at index 0 per block. |
| */ |
| #if 0 |
| |
| #ifdef DDI_INTR_IRM |
| pBlock->pIntrHandleBlock = |
| &pBlock->pIntrHandleBlockAlloc[intrInum]; |
| #else |
| pBlock->pIntrHandleBlock = |
| &pBlock->pIntrHandleBlockAlloc[0]; |
| #endif |
| |
| #else |
| |
| if (pBlock->pIntrHandleBlockAlloc[0]) |
| { |
| pBlock->pIntrHandleBlock = |
| &pBlock->pIntrHandleBlockAlloc[0]; |
| } |
| else |
| { |
| pBlock->pIntrHandleBlock = |
| &pBlock->pIntrHandleBlockAlloc[intrInum]; |
| } |
| |
| #endif |
| |
| if (intrRequest != intrActual) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate desired isr count (%d/%d)!", |
| intrActual, intrRequest); |
| |
| #if 0 |
| for (i = 0; i < intrActual; i++) |
| { |
| ddi_intr_free(pBlock->pIntrHandleBlock[i]); |
| } |
| |
| kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize); |
| return B_FALSE; |
| #else |
| if (intrActual == 0) |
| { |
| kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize); |
| return B_FALSE; |
| } |
| #endif |
| } |
| |
| pBlock->intrCount = intrActual; |
| |
| if ((rc = ddi_intr_get_cap(pBlock->pIntrHandleBlock[0], |
| &pBlock->intrCapability)) != DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to get isr capability (%d)!", rc); |
| goto BnxeIntrBlockAlloc_fail; |
| } |
| |
| if ((rc = ddi_intr_get_pri(pBlock->pIntrHandleBlock[0], |
| &pBlock->intrPriority)) != DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to get isr priority (%d)!", rc); |
| goto BnxeIntrBlockAlloc_fail; |
| } |
| |
| if (pBlock->intrPriority >= ddi_intr_get_hilevel_pri()) |
| { |
| BnxeLogWarn(pUM, "Interrupt priority is too high!"); |
| goto BnxeIntrBlockAlloc_fail; |
| } |
| |
| return B_TRUE; |
| |
| BnxeIntrBlockAlloc_fail: |
| |
| for (i = 0; i < intrActual; i++) |
| { |
| ddi_intr_free(pBlock->pIntrHandleBlock[i]); |
| } |
| |
| kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize); |
| |
| memset(pBlock, 0, sizeof(BnxeIntrBlock)); |
| |
| return B_FALSE; |
| } |
| |
| |
| static void BnxeIntrBlockFree(um_device_t * pUM, |
| BnxeIntrBlock * pBlock) |
| |
| { |
| int i; |
| |
| if (pBlock->intrCount == 0) |
| { |
| memset(pBlock, 0, sizeof(BnxeIntrBlock)); |
| return; |
| } |
| |
| for (i = 0; i < pBlock->intrCount; i++) |
| { |
| ddi_intr_free(pBlock->pIntrHandleBlock[i]); |
| } |
| |
| kmem_free(pBlock->pIntrHandleBlockAlloc, pBlock->intrHandleBlockSize); |
| |
| memset(pBlock, 0, sizeof(BnxeIntrBlock)); |
| } |
| |
| |
| static boolean_t BnxeIntrAddHandlers(um_device_t * pUM) |
| { |
| int rc, i, j; |
| |
| switch (pUM->intrType) |
| { |
| case DDI_INTR_TYPE_MSIX: |
| |
| if ((rc = ddi_intr_add_handler( |
| pUM->defIntr.pIntrHandleBlock[0], |
| BnxeIntrMISR, |
| (caddr_t)pUM, |
| (caddr_t)(uintptr_t)DEF_STATUS_BLOCK_IGU_INDEX)) != |
| DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to add the MSIX default isr handler (%d)", rc); |
| return B_FALSE; |
| } |
| |
| for (i = 0; i < pUM->rssIntr.intrCount; i++) |
| { |
| if ((rc = ddi_intr_add_handler( |
| pUM->rssIntr.pIntrHandleBlock[i], |
| BnxeIntrMISR, |
| (caddr_t)pUM, |
| (caddr_t)(uintptr_t)i)) != |
| DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to add the MSIX RSS isr handler %d (%d)", |
| (i + NDIS_CID(&pUM->lm_dev)), rc); |
| |
| ddi_intr_remove_handler(pUM->defIntr.pIntrHandleBlock[0]); |
| |
| for (j = 0; j < i; j++) /* unwind */ |
| { |
| ddi_intr_remove_handler(pUM->rssIntr.pIntrHandleBlock[j]); |
| } |
| |
| return B_FALSE; |
| } |
| } |
| |
| /* |
| * fcoeIntr.intrCount == 1 implies LM_NON_RSS_SB (last) status block |
| * was allocated for FCoE and there was no overlap with the RSS |
| * allocation. |
| */ |
| if (pUM->fcoeIntr.intrCount == 1) |
| { |
| if ((rc = ddi_intr_add_handler( |
| pUM->fcoeIntr.pIntrHandleBlock[0], |
| BnxeIntrMISR, |
| (caddr_t)pUM, |
| (caddr_t)(uintptr_t)LM_NON_RSS_SB(&pUM->lm_dev))) != |
| DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to add the MSIX FCoE isr handler (%d)", rc); |
| |
| ddi_intr_remove_handler(pUM->defIntr.pIntrHandleBlock[0]); |
| |
| for (i = 0; i < pUM->rssIntr.intrCount; i++) |
| { |
| ddi_intr_remove_handler(pUM->rssIntr.pIntrHandleBlock[i]); |
| } |
| |
| return B_FALSE; |
| } |
| } |
| |
| break; |
| |
| case DDI_INTR_TYPE_FIXED: |
| |
| if ((rc = ddi_intr_add_handler( |
| pUM->defIntr.pIntrHandleBlock[0], |
| BnxeIntrISR, |
| (caddr_t)pUM, |
| (caddr_t)(uintptr_t)DEF_STATUS_BLOCK_IGU_INDEX)) != |
| DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to add the fixed default isr handler (%d)", rc); |
| return B_FALSE; |
| } |
| |
| break; |
| |
| default: |
| |
| BnxeLogWarn(pUM, "Failed to add isr handler (unsupported type %d)!", |
| pUM->intrType); |
| return B_FALSE; |
| } |
| |
| return B_TRUE; |
| } |
| |
| |
| static void BnxeIntrBlockRemoveHandler(um_device_t * pUM, |
| BnxeIntrBlock * pBlock) |
| { |
| int i; |
| |
| (void)pUM; |
| |
| if (pBlock->intrCount == 0) |
| { |
| return; |
| } |
| |
| for (i = 0; i < pBlock->intrCount; i++) |
| { |
| ddi_intr_remove_handler(pBlock->pIntrHandleBlock[i]); |
| } |
| } |
| |
| |
| static boolean_t BnxeIntrBlockEnable(um_device_t * pUM, |
| BnxeIntrBlock * pBlock) |
| { |
| int rc, i, j; |
| |
| if (pBlock->intrCount == 0) |
| { |
| return B_TRUE; |
| } |
| |
| if (pBlock->intrCapability & DDI_INTR_FLAG_BLOCK) |
| { |
| if ((rc = ddi_intr_block_enable(pBlock->pIntrHandleBlock, |
| pBlock->intrCount)) != DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to enable isr block (%d)", rc); |
| return B_FALSE; |
| } |
| } |
| else |
| { |
| for (i = 0; i < pBlock->intrCount; i++) |
| { |
| if ((rc = ddi_intr_enable(pBlock->pIntrHandleBlock[i])) != |
| DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to enable isr %d (%d)", i, rc); |
| |
| for (j = 0; j < i; j++) /* unwind */ |
| { |
| ddi_intr_disable(pBlock->pIntrHandleBlock[j]); |
| } |
| |
| return B_FALSE; |
| } |
| } |
| } |
| |
| return B_TRUE; |
| } |
| |
| |
| static void BnxeIntrBlockDisable(um_device_t * pUM, |
| BnxeIntrBlock * pBlock) |
| { |
| int i; |
| |
| if (pBlock->intrCount == 0) |
| { |
| return; |
| } |
| |
| if (pBlock->intrCapability & DDI_INTR_FLAG_BLOCK) |
| { |
| ddi_intr_block_disable(pBlock->pIntrHandleBlock, pBlock->intrCount); |
| } |
| else |
| { |
| for (i = 0; i < pBlock->intrCount; i++) |
| { |
| ddi_intr_disable(pBlock->pIntrHandleBlock[i]); |
| } |
| } |
| } |
| |
| |
| int BnxeIntrEnable(um_device_t * pUM) |
| { |
| BnxeMemDma * pDma; |
| int rc, i, j; |
| |
| atomic_swap_64((volatile uint64_t *)&pUM->intrFired, 0); |
| |
| for (i = 0; i < (MAX_RSS_CHAINS + 1); i++) |
| { |
| pUM->intrSbCnt[i] = 0; |
| pUM->intrSbNoChangeCnt[i] = 0; |
| } |
| |
| /* get the DMA handles for quick access to the status blocks for sync */ |
| BnxeFindDmaHandles(pUM); |
| |
| /* Enable the default interrupt... */ |
| |
| if (!BnxeIntrBlockEnable(pUM, &pUM->defIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to enable the default interrupt"); |
| return -1; |
| } |
| |
| /* Enable the FCoE interrupt... */ |
| |
| if (!BnxeIntrBlockEnable(pUM, &pUM->fcoeIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to enable the FCoE interrupt"); |
| BnxeIntrBlockDisable(pUM, &pUM->defIntr); |
| return -1; |
| } |
| |
| /* Enable the RSS interrupts... */ |
| |
| if (!BnxeIntrBlockEnable(pUM, &pUM->rssIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to enable the RSS interrupt"); |
| BnxeIntrBlockDisable(pUM, &pUM->defIntr); |
| BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr); |
| return -1; |
| } |
| |
| /* allow the hardware to generate interrupts */ |
| atomic_swap_32(&pUM->intrEnabled, B_TRUE); |
| lm_enable_int(&pUM->lm_dev); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| /* XXX do not remove this... edavis */ |
| drv_usecwait(1000000); /* :-( */ |
| |
| return 0; |
| } |
| |
| |
| void BnxeIntrDisable(um_device_t * pUM) |
| { |
| int rc, i; |
| |
| /* stop the device from generating any interrupts */ |
| lm_disable_int(&pUM->lm_dev); |
| |
| if (pUM->fmCapabilities && |
| BnxeCheckAccHandle(pUM->lm_dev.vars.reg_handle[BAR_0]) != DDI_FM_OK) |
| { |
| ddi_fm_service_impact(pUM->pDev, DDI_SERVICE_DEGRADED); |
| } |
| |
| atomic_swap_32(&pUM->intrEnabled, B_FALSE); |
| |
| /* |
| * Ensure the ISR no longer touches the hardware by making sure the ISR |
| * is not running or the current run completes. Since interrupts were |
| * disabled before here and intrEnabled is FALSE, we can be sure |
| * interrupts will no longer be processed. |
| */ |
| for (i = 0; i < (MAX_RSS_CHAINS + 1); i++) |
| { |
| BNXE_LOCK_ENTER_INTR(pUM, i); |
| BNXE_LOCK_EXIT_INTR(pUM, i); |
| } |
| |
| /* Disable the default interrupt... */ |
| |
| BnxeIntrBlockDisable(pUM, &pUM->defIntr); |
| |
| /* Disable the FCoE interrupt... */ |
| |
| BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr); |
| |
| /* Disable the RSS interrupts... */ |
| |
| BnxeIntrBlockDisable(pUM, &pUM->rssIntr); |
| } |
| |
| |
| boolean_t BnxeIntrInit(um_device_t * pUM) |
| { |
| dev_info_t * pDev; |
| int intrTypes = 0; |
| int intrTotalAlloc = 0; |
| int numMSIX, numMSI, numFIX; |
| int rc, i; |
| |
| pDev = pUM->pDev; |
| |
| atomic_swap_32(&pUM->intrEnabled, B_FALSE); |
| |
| if ((rc = ddi_intr_get_supported_types(pDev, &intrTypes)) != DDI_SUCCESS) |
| { |
| BnxeLogWarn(pUM, "Failed to get supported interrupt types (%d)", rc); |
| return B_FALSE; |
| } |
| |
| numMSIX = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_MSIX, intrTypes); |
| numMSI = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_MSI, intrTypes); |
| numFIX = BnxeGetInterruptCount(pDev, DDI_INTR_TYPE_FIXED, intrTypes); |
| |
| if (numFIX <= 0) |
| { |
| BnxeLogWarn(pUM, "Fixed interrupt not supported!"); |
| return B_FALSE; |
| } |
| |
| memset(&pUM->defIntr, 0, sizeof(BnxeIntrBlock)); |
| memset(&pUM->rssIntr, 0, sizeof(BnxeIntrBlock)); |
| memset(&pUM->fcoeIntr, 0, sizeof(BnxeIntrBlock)); |
| |
| if (pUM->devParams.disableMsix) |
| { |
| BnxeLogInfo(pUM, "Forcing fixed level interrupts."); |
| pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA; |
| pUM->intrType = DDI_INTR_TYPE_FIXED; |
| } |
| else if (numMSIX > 0) |
| { |
| pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_MIMD; |
| pUM->intrType = DDI_INTR_TYPE_MSIX; |
| } |
| else /* numFIX */ |
| { |
| pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA; |
| pUM->intrType = DDI_INTR_TYPE_FIXED; |
| } |
| |
| while (1) |
| { |
| /* allocate the default interrupt */ |
| |
| if (!BnxeIntrBlockAlloc(pUM, |
| 0, |
| 1, |
| &pUM->defIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate default %s interrupt!", |
| BnxeIntrTypeName(pUM->intrType)); |
| goto BnxeIntrInit_alloc_fail; |
| } |
| |
| intrTotalAlloc++; |
| |
| if (pUM->intrType == DDI_INTR_TYPE_FIXED) |
| { |
| /* only one interrupt allocated for fixed (default) */ |
| break; |
| } |
| |
| if (BnxeProtoFcoeAfex(pUM)) |
| { |
| pUM->devParams.numRings = 0; |
| } |
| else |
| { |
| /* allocate the RSS interrupts */ |
| |
| while (pUM->devParams.numRings > 0) |
| { |
| if (!BnxeIntrBlockAlloc(pUM, |
| (NDIS_CID(&pUM->lm_dev) + 1), |
| pUM->devParams.numRings, |
| &pUM->rssIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate %d RSS %s interrupts!", |
| pUM->devParams.numRings, |
| BnxeIntrTypeName(pUM->intrType)); |
| pUM->devParams.numRings >>= 1; |
| continue; |
| } |
| |
| break; |
| } |
| |
| if (pUM->devParams.numRings == 0) |
| { |
| BnxeIntrBlockFree(pUM, &pUM->defIntr); |
| goto BnxeIntrInit_alloc_fail; |
| } |
| |
| BnxeLogInfo(pUM, "Allocated %d RSS %s interrupts.", |
| pUM->rssIntr.intrCount, |
| BnxeIntrTypeName(pUM->intrType)); |
| |
| intrTotalAlloc += pUM->rssIntr.intrCount; /* intrCount <= numRings */ |
| } |
| |
| /* |
| * Allocate the FCoE interrupt only if all available status blocks |
| * were not taken up by the RSS chains. If they were then the last |
| * status block (LM_NON_RSS_SB) is overloaded for both RSS and FCoE. |
| */ |
| |
| if (BNXE_FCOE(pUM)) |
| { |
| if (pUM->rssIntr.intrCount < LM_MAX_RSS_CHAINS(&pUM->lm_dev)) |
| { |
| if (!BnxeIntrBlockAlloc(pUM, |
| (LM_NON_RSS_SB(&pUM->lm_dev) + 1), |
| 1, |
| &pUM->fcoeIntr)) |
| { |
| BnxeLogWarn(pUM, "Failed to allocate FCoE %s interrupt!", |
| BnxeIntrTypeName(pUM->intrType)); |
| BnxeIntrBlockFree(pUM, &pUM->defIntr); |
| BnxeIntrBlockFree(pUM, &pUM->rssIntr); |
| goto BnxeIntrInit_alloc_fail; |
| } |
| |
| intrTotalAlloc++; |
| } |
| else |
| { |
| /* to be safe, sets fcoeIntr.intrCount to 0 */ |
| memset(&pUM->fcoeIntr, 0, sizeof(BnxeIntrBlock)); |
| } |
| } |
| |
| break; |
| |
| BnxeIntrInit_alloc_fail: |
| |
| if (pUM->intrType == DDI_INTR_TYPE_FIXED) |
| { |
| return B_FALSE; |
| } |
| |
| /* fall back to fixed a retry allocation */ |
| intrTotalAlloc = 0; |
| pUM->lm_dev.params.interrupt_mode = LM_INT_MODE_INTA; |
| pUM->intrType = DDI_INTR_TYPE_FIXED; |
| } |
| |
| if (pUM->intrType == DDI_INTR_TYPE_MSIX) |
| { |
| pUM->devParams.numRings = pUM->rssIntr.intrCount; |
| pUM->lm_dev.params.rss_chain_cnt = pUM->rssIntr.intrCount; |
| pUM->lm_dev.params.tss_chain_cnt = pUM->rssIntr.intrCount; |
| } |
| else |
| { |
| /* fixed level (no rings)... */ |
| pUM->devParams.numRings = 0; |
| pUM->lm_dev.params.rss_chain_cnt = 1; |
| pUM->lm_dev.params.tss_chain_cnt = 1; |
| |
| BnxeLogWarn(pUM, "Using Fixed Level Interrupts! (set ddi_msix_alloc_limit in /etc/system)"); |
| } |
| |
| BnxeLogInfo(pUM, "Interrupts (Supported - %d Fixed / %d MSI / %d MSIX) (Allocated - %d %s)", |
| numFIX, numMSI, numMSIX, intrTotalAlloc, BnxeIntrTypeName(pUM->intrType)); |
| |
| if (!BnxeIntrAddHandlers(pUM)) |
| { |
| BnxeLogWarn(pUM, "Failed to add interrupts!"); |
| BnxeIntrBlockFree(pUM, &pUM->defIntr); |
| BnxeIntrBlockFree(pUM, &pUM->fcoeIntr); |
| BnxeIntrBlockFree(pUM, &pUM->rssIntr); |
| return B_FALSE; |
| } |
| |
| /* copy default priority and assume rest are the same (for mutex) */ |
| pUM->intrPriority = pUM->defIntr.intrPriority; |
| |
| return B_TRUE; |
| } |
| |
| |
| void BnxeIntrFini(um_device_t * pUM) |
| { |
| int i; |
| |
| BnxeIntrBlockDisable(pUM, &pUM->defIntr); |
| BnxeIntrBlockRemoveHandler(pUM, &pUM->defIntr); |
| BnxeIntrBlockFree(pUM, &pUM->defIntr); |
| |
| BnxeIntrBlockDisable(pUM, &pUM->fcoeIntr); |
| BnxeIntrBlockRemoveHandler(pUM, &pUM->fcoeIntr); |
| BnxeIntrBlockFree(pUM, &pUM->fcoeIntr); |
| |
| BnxeIntrBlockDisable(pUM, &pUM->rssIntr); |
| BnxeIntrBlockRemoveHandler(pUM, &pUM->rssIntr); |
| BnxeIntrBlockFree(pUM, &pUM->rssIntr); |
| } |
| |