/* * 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 2009 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include static hxge_fm_ereport_attr_t *hxge_fm_get_ereport_attr(hxge_fm_ereport_id_t ereport_id); static int hxge_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data); hxge_fm_ereport_attr_t hxge_fm_ereport_vmac[] = { {HXGE_FM_EREPORT_VMAC_LINK_DOWN, "10g_link_down", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_LOST} }; hxge_fm_ereport_attr_t hxge_fm_ereport_pfc[] = { /* * The following are part of LDF 0, non-fatal */ {HXGE_FM_EREPORT_PFC_TCAM_PAR_ERR, "classifier_tcam_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_UNAFFECTED}, {HXGE_FM_EREPORT_PFC_VLAN_PAR_ERR, "classifier_vlan_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_UNAFFECTED}, {HXGE_FM_EREPORT_PFC_PKT_DROP, "classifier_pkt_drop_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_UNAFFECTED} }; hxge_fm_ereport_attr_t hxge_fm_ereport_rdmc[] = { /* * The following are part of LDF1, fatal */ {HXGE_FM_EREPORT_RDMC_RBR_CPL_TO, "rxdma_rbr_cpl_to", DDI_FM_DEVICE_NO_RESPONSE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_PEU_RESP_ERR, "rxdma_peu_resp_err", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RCR_SHA_PAR, "rxdma_rcr_sha_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RBR_PRE_PAR, "rxdma_rbr_pre_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RBR_PRE_EMPTY, "rxdma_rbr_pre_empty_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RCR_SHA_FULL, "rxdma_rcr_sha_full", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RCRFULL, "rxdma_rcr_full", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RBR_EMPTY, "rxdma_rbr_empty", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RBRFULL, "rxdma_rbr_full", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_RCR_ERR, "rxdma_completion_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, /* * Control/Data ram received a ecc double bit error. * Fatal error. Part of Device Error 1 */ {HXGE_FM_EREPORT_RDMC_CTRL_FIFO_DED, "rxdma_ctrl_fifo_ded", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_RDMC_DATA_FIFO_DED, "rxdma_data_fifo_ded", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, /* * Control/Data ram received a ecc single bit error. * Non-Fatal error. Part of Device Error 0 */ {HXGE_FM_EREPORT_RDMC_CTRL_FIFO_SEC, "rxdma_ctrl_fifo_sec", DDI_FM_DEVICE_INTERN_CORR, DDI_SERVICE_UNAFFECTED}, {HXGE_FM_EREPORT_RDMC_DATA_FIFO_SEC, "rxdma_data_fifo_sec", DDI_FM_DEVICE_INTERN_CORR, DDI_SERVICE_UNAFFECTED} }; hxge_fm_ereport_attr_t hxge_fm_ereport_tdmc[] = { {HXGE_FM_EREPORT_TDMC_PEU_RESP_ERR, "txdma_peu_resp_err", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_PKT_SIZE_HDR_ERR, "txdma_pkt_size_hdr_err", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_RUNT_PKT_DROP_ERR, "txdma_runt_pkt_drop_err", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_PKT_SIZE_ERR, "txdma_pkt_size_err", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_TX_RNG_OFLOW, "txdma_tx_rng_oflow", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_PREF_PAR_ERR, "txdma_pref_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_TDR_PREF_CPL_TO, "txdma_tdr_pref_cpl_to", DDI_FM_DEVICE_NO_RESPONSE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_PKT_CPL_TO, "txdma_pkt_cpl_to", DDI_FM_DEVICE_NO_RESPONSE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_INVALID_SOP, "txdma_invalid_sop", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_UNEXPECTED_SOP, "txdma_unexpected_sop", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_REORD_TBL_PAR, "txdma_reord_tbl_par_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED}, {HXGE_FM_EREPORT_TDMC_REORD_BUF_DED, "txdma_reord_buf_ded_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_DEGRADED} }; hxge_fm_ereport_attr_t hxge_fm_ereport_peu[] = { {HXGE_FM_EREPORT_PEU_ERR, "peu_peu_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_LOST}, {HXGE_FM_EREPORT_PEU_VNM_PIO_ERR, "peu_vnm_pio_err", DDI_FM_DEVICE_INTERN_UNCORR, DDI_SERVICE_LOST} }; hxge_fm_ereport_attr_t hxge_fm_ereport_sw[] = { {HXGE_FM_EREPORT_SW_INVALID_CHAN_NUM, "invalid_chan_num", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_LOST}, {HXGE_FM_EREPORT_SW_INVALID_PARAM, "invalid_param", DDI_FM_DEVICE_INVAL_STATE, DDI_SERVICE_LOST} }; void hxge_fm_init(p_hxge_t hxgep, ddi_device_acc_attr_t *reg_attr, ddi_device_acc_attr_t *desc_attr, ddi_dma_attr_t *dma_attr) { ddi_iblock_cookie_t iblk; HXGE_DEBUG_MSG((hxgep, DDI_CTL, "==> hxge_fm_init")); /* fm-capable in hxge.conf can be used to set fm_capabilities. */ hxgep->fm_capabilities = ddi_prop_get_int(DDI_DEV_T_ANY, hxgep->dip, DDI_PROP_DONTPASS, "fm-capable", DDI_FM_EREPORT_CAPABLE | DDI_FM_ERRCB_CAPABLE); HXGE_DEBUG_MSG((hxgep, DDI_CTL, "FM capable = %d\n", hxgep->fm_capabilities)); /* * Register capabilities with IO Fault Services. The capabilities * set above may not be supported by the parent nexus, in that case * some capability bits may be cleared. */ if (hxgep->fm_capabilities) ddi_fm_init(hxgep->dip, &hxgep->fm_capabilities, &iblk); /* * Initialize pci ereport capabilities if ereport capable */ if (DDI_FM_EREPORT_CAP(hxgep->fm_capabilities) || DDI_FM_ERRCB_CAP(hxgep->fm_capabilities)) { pci_ereport_setup(hxgep->dip); } /* Register error callback if error callback capable */ if (DDI_FM_ERRCB_CAP(hxgep->fm_capabilities)) { ddi_fm_handler_register(hxgep->dip, hxge_fm_error_cb, (void *) hxgep); } /* * DDI_FLGERR_ACC indicates: * o Driver will check its access handle(s) for faults on * a regular basis by calling ddi_fm_acc_err_get * o Driver is able to cope with incorrect results of I/O * operations resulted from an I/O fault */ if (DDI_FM_ACC_ERR_CAP(hxgep->fm_capabilities)) { reg_attr->devacc_attr_access = DDI_FLAGERR_ACC; desc_attr->devacc_attr_access = DDI_FLAGERR_ACC; } else { reg_attr->devacc_attr_access = DDI_DEFAULT_ACC; desc_attr->devacc_attr_access = DDI_DEFAULT_ACC; } /* * DDI_DMA_FLAGERR indicates: * o Driver will check its DMA handle(s) for faults on a * regular basis using ddi_fm_dma_err_get * o Driver is able to cope with incorrect results of DMA * operations resulted from an I/O fault */ if (DDI_FM_DMA_ERR_CAP(hxgep->fm_capabilities)) dma_attr->dma_attr_flags |= DDI_DMA_FLAGERR; else dma_attr->dma_attr_flags &= ~DDI_DMA_FLAGERR; HXGE_DEBUG_MSG((hxgep, DDI_CTL, "<== hxge_fm_init")); } void hxge_fm_fini(p_hxge_t hxgep) { /* Only unregister FMA capabilities if we registered some */ if (hxgep->fm_capabilities) { /* * Release any resources allocated by pci_ereport_setup() */ if (DDI_FM_EREPORT_CAP(hxgep->fm_capabilities) || DDI_FM_ERRCB_CAP(hxgep->fm_capabilities)) pci_ereport_teardown(hxgep->dip); /* * Un-register error callback if error callback capable */ if (DDI_FM_ERRCB_CAP(hxgep->fm_capabilities)) ddi_fm_handler_unregister(hxgep->dip); /* Unregister from IO Fault Services */ ddi_fm_fini(hxgep->dip); } } /* * Simply call pci_ereport_post which generates ereports for errors * that occur in the PCI local bus configuration status registers. */ /*ARGSUSED*/ static int hxge_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) { pci_ereport_post(dip, err, NULL); return (err->fme_status); } static hxge_fm_ereport_attr_t * hxge_fm_get_ereport_attr(hxge_fm_ereport_id_t ereport_id) { hxge_fm_ereport_attr_t *attr; uint8_t blk_id; uint8_t index; /* Extract the block id and the index within the block */ blk_id = ((ereport_id >> EREPORT_FM_ID_SHIFT) & EREPORT_FM_ID_MASK); index = (ereport_id & EREPORT_INDEX_MASK); /* Return the appropriate structure of type hxge_fm_ereport_attr_t */ switch (blk_id) { case FM_SW_ID: attr = &hxge_fm_ereport_sw[index]; break; case FM_VMAC_ID: attr = &hxge_fm_ereport_vmac[index]; break; case FM_PFC_ID: attr = &hxge_fm_ereport_pfc[index]; break; case FM_RXDMA_ID: attr = &hxge_fm_ereport_rdmc[index]; break; case FM_TXDMA_ID: attr = &hxge_fm_ereport_tdmc[index]; break; case FM_PEU_ID: attr = &hxge_fm_ereport_peu[index]; break; default: attr = NULL; } return (attr); } static void hxge_fm_ereport(p_hxge_t hxgep, uint8_t err_chan, hxge_fm_ereport_attr_t *ereport) { uint64_t ena; char eclass[FM_MAX_CLASS]; char *err_str; p_hxge_stats_t statsp; (void) snprintf(eclass, FM_MAX_CLASS, "%s.%s", DDI_FM_DEVICE, ereport->eclass); err_str = ereport->str; ena = fm_ena_generate(0, FM_ENA_FMT1); statsp = hxgep->statsp; switch (ereport->index) { case HXGE_FM_EREPORT_VMAC_LINK_DOWN: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, NULL); break; case HXGE_FM_EREPORT_PFC_TCAM_PAR_ERR: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_PFC_TCAM_ERR, DATA_TYPE_UINT32, statsp->pfc_stats.tcam_parity_err, NULL); break; case HXGE_FM_EREPORT_PFC_VLAN_PAR_ERR: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_PFC_VLAN_ERR, DATA_TYPE_UINT32, statsp->pfc_stats.vlan_parity_err, NULL); break; case HXGE_FM_EREPORT_PFC_PKT_DROP: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_PFC_PKT_DROP, DATA_TYPE_UINT32, statsp->pfc_stats.pkt_drop, NULL); break; case HXGE_FM_EREPORT_RDMC_RBR_CPL_TO: case HXGE_FM_EREPORT_RDMC_PEU_RESP_ERR: case HXGE_FM_EREPORT_RDMC_RCRFULL: case HXGE_FM_EREPORT_RDMC_RBR_EMPTY: case HXGE_FM_EREPORT_RDMC_RBRFULL: case HXGE_FM_EREPORT_RDMC_RBR_PRE_EMPTY: case HXGE_FM_EREPORT_RDMC_RCR_SHA_FULL: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_ERR_DCHAN, DATA_TYPE_UINT8, err_chan, NULL); break; case HXGE_FM_EREPORT_RDMC_RBR_PRE_PAR: case HXGE_FM_EREPORT_RDMC_RCR_SHA_PAR: { uint32_t err_log; hxge_rx_ring_stats_t *rdc_statsp; rdc_statsp = &statsp->rdc_stats[err_chan]; if (ereport->index == HXGE_FM_EREPORT_RDMC_RBR_PRE_PAR) err_log = (uint32_t) rdc_statsp->errlog.pre_par.value; else err_log = (uint32_t) rdc_statsp->errlog.sha_par.value; ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_ERR_DCHAN, DATA_TYPE_UINT8, err_chan, ERNAME_RDMC_PAR_ERR_LOG, DATA_TYPE_UINT8, err_log, NULL); } break; case HXGE_FM_EREPORT_RDMC_RCR_ERR: { uint8_t err_type; err_type = statsp->rdc_stats[err_chan].errlog.compl_err_type; ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_ERR_DCHAN, DATA_TYPE_UINT8, err_chan, ERNAME_RDC_ERR_TYPE, DATA_TYPE_UINT8, err_type, NULL); } break; case HXGE_FM_EREPORT_RDMC_CTRL_FIFO_SEC: case HXGE_FM_EREPORT_RDMC_CTRL_FIFO_DED: case HXGE_FM_EREPORT_RDMC_DATA_FIFO_SEC: case HXGE_FM_EREPORT_RDMC_DATA_FIFO_DED: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, NULL); break; case HXGE_FM_EREPORT_TDMC_PEU_RESP_ERR: case HXGE_FM_EREPORT_TDMC_TX_RNG_OFLOW: case HXGE_FM_EREPORT_TDMC_PKT_SIZE_HDR_ERR: case HXGE_FM_EREPORT_TDMC_RUNT_PKT_DROP_ERR: case HXGE_FM_EREPORT_TDMC_PKT_SIZE_ERR: case HXGE_FM_EREPORT_TDMC_TDR_PREF_CPL_TO: case HXGE_FM_EREPORT_TDMC_PKT_CPL_TO: case HXGE_FM_EREPORT_TDMC_INVALID_SOP: case HXGE_FM_EREPORT_TDMC_UNEXPECTED_SOP: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_ERR_DCHAN, DATA_TYPE_UINT8, err_chan, NULL); break; case HXGE_FM_EREPORT_TDMC_PREF_PAR_ERR: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, ERNAME_ERR_DCHAN, DATA_TYPE_UINT8, err_chan, ERNAME_TDC_PREF_PAR_LOG, DATA_TYPE_UINT32, statsp->tdc_stats[err_chan].errlog.value, NULL); break; case HXGE_FM_EREPORT_TDMC_REORD_TBL_PAR: case HXGE_FM_EREPORT_TDMC_REORD_BUF_DED: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, NULL); break; case HXGE_FM_EREPORT_PEU_ERR: case HXGE_FM_EREPORT_PEU_VNM_PIO_ERR: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, NULL); break; case HXGE_FM_EREPORT_SW_INVALID_CHAN_NUM: case HXGE_FM_EREPORT_SW_INVALID_PARAM: ddi_fm_ereport_post(hxgep->dip, eclass, ena, DDI_NOSLEEP, FM_VERSION, DATA_TYPE_UINT8, FM_EREPORT_VERS0, ERNAME_DETAILED_ERR_TYPE, DATA_TYPE_STRING, err_str, NULL); break; } } void hxge_fm_report_error(p_hxge_t hxgep, uint8_t err_chan, hxge_fm_ereport_id_t fm_ereport_id) { hxge_fm_ereport_attr_t *fm_ereport_attr; fm_ereport_attr = hxge_fm_get_ereport_attr(fm_ereport_id); if (fm_ereport_attr != NULL && (DDI_FM_EREPORT_CAP(hxgep->fm_capabilities))) { hxge_fm_ereport(hxgep, err_chan, fm_ereport_attr); ddi_fm_service_impact(hxgep->dip, fm_ereport_attr->impact); } } int fm_check_acc_handle(ddi_acc_handle_t handle) { ddi_fm_error_t err; ddi_fm_acc_err_get(handle, &err, DDI_FME_VERSION); ddi_fm_acc_err_clear(handle, DDI_FME_VERSION); return (err.fme_status); } int fm_check_dma_handle(ddi_dma_handle_t handle) { ddi_fm_error_t err; ddi_fm_dma_err_get(handle, &err, DDI_FME_VERSION); return (err.fme_status); }