/* * 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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" #include "nge.h" #undef NGE_DBG #define NGE_DBG NGE_DBG_STATS /* debug flag for this code */ /* * Table of Hardware-defined Statistics Block Offsets and Names */ #define KS_NAME(s) { KS_ ## s, #s } const nge_ksindex_t nge_statistics[] = { KS_NAME(ifHOutOctets), KS_NAME(ifHOutZeroRetranCount), KS_NAME(ifHOutOneRetranCount), KS_NAME(ifHOutMoreRetranCount), KS_NAME(ifHOutColCount), KS_NAME(ifHOutFifoovCount), KS_NAME(ifHOutLOCCount), KS_NAME(ifHOutExDecCount), KS_NAME(ifHOutRetryCount), KS_NAME(ifHInFrameErrCount), KS_NAME(ifHInExtraOctErrCount), KS_NAME(ifHInLColErrCount), KS_NAME(ifHInOversizeErrCount), KS_NAME(ifHInFovErrCount), KS_NAME(ifHInFCSErrCount), KS_NAME(ifHInAlignErrCount), KS_NAME(ifHInLenErrCount), KS_NAME(ifHInUniPktsCount), KS_NAME(ifHInBroadPksCount), KS_NAME(ifHInMulPksCount), { KS_STATS_SIZE, NULL } }; /* * Local datatype for defining tables of (Offset, Name) pairs */ static int nge_statistics_update(kstat_t *ksp, int flag) { uint32_t regno; nge_t *ngep; nge_statistics_t *istp; nge_hw_statistics_t *hw_stp; kstat_named_t *knp; const nge_ksindex_t *ksip; if (flag != KSTAT_READ) return (EACCES); ngep = ksp->ks_private; istp = &ngep->statistics; hw_stp = &istp->hw_statistics; knp = ksp->ks_data; /* * Transfer the statistics values from the hardware statistics regs */ for (ksip = nge_statistics; ksip->name != NULL; ++knp, ++ksip) { regno = KS_BASE + ksip->index * sizeof (uint32_t); hw_stp->a[ksip->index] += nge_reg_get32(ngep, regno); knp->value.ui64 = hw_stp->a[ksip->index]; } return (0); } static const nge_ksindex_t nge_chipinfo[] = { { 0, "businfo" }, { 1, "command" }, { 2, "vendor_id" }, { 3, "device_id" }, { 4, "subsystem_vendor_id" }, { 5, "subsystem_device_id" }, { 6, "revision_id" }, { 7, "cache_line_size" }, { 8, "latency_timer" }, { 9, "phy_mode" }, { 10, "phy_id" }, { 11, "hw_mac_addr" }, { 12, "&bus_type" }, { 13, "&bus_speed" }, { 14, "&bus_size" }, { -1, NULL } }; static const nge_ksindex_t nge_debuginfo[] = { { 0, "rx_realloc" }, { 1, "rx_realloc_fails" }, { 2, "rx_realloc_DMA_fails" }, { 3, "rx_realloc_MP_fails" }, { 4, "rx_rcfree" }, { 5, "context_switch" }, { 6, "ip_hsum_err" }, { 7, "tcp_hsum_err" }, { 8, "tc_next" }, { 9, "tx_next" }, { 10, "tx_free" }, { 11, "tx_flow" }, { 12, "rx_prod" }, { 13, "rx_hold" }, { 14, "rx_nobuf" }, { 15, "rx_err" }, {16, "tx_err" }, {17, "tx_stall" }, { -1, NULL } }; static int nge_chipinfo_update(kstat_t *ksp, int flag) { nge_t *ngep; kstat_named_t *knp; chip_info_t *infop; if (flag != KSTAT_READ) return (EACCES); ngep = ksp->ks_private; infop = &ngep->chipinfo; knp = ksp->ks_data; (knp++)->value.ui64 = infop->businfo; (knp++)->value.ui64 = infop->command; (knp++)->value.ui64 = infop->vendor; (knp++)->value.ui64 = infop->device; (knp++)->value.ui64 = infop->subven; (knp++)->value.ui64 = infop->subdev; (knp++)->value.ui64 = infop->revision; (knp++)->value.ui64 = infop->clsize; (knp++)->value.ui64 = infop->latency; (knp++)->value.ui64 = ngep->phy_mode; (knp++)->value.ui64 = ngep->phy_id; (knp++)->value.ui64 = infop->hw_mac_addr; return (0); } static int nge_debuginfo_update(kstat_t *ksp, int flag) { nge_t *ngep; kstat_named_t *knp; nge_sw_statistics_t *sw_stp; if (flag != KSTAT_READ) return (EACCES); ngep = ksp->ks_private; sw_stp = &ngep->statistics.sw_statistics; knp = ksp->ks_data; (knp++)->value.ui64 = sw_stp->recv_realloc; (knp++)->value.ui64 = sw_stp->kmem_alloc_err; (knp++)->value.ui64 = sw_stp->dma_alloc_err; (knp++)->value.ui64 = sw_stp->mp_alloc_err; (knp++)->value.ui64 = sw_stp->recy_free; (knp++)->value.ui64 = sw_stp->load_context; (knp++)->value.ui64 = sw_stp->ip_hwsum_err; (knp++)->value.ui64 = sw_stp->tcp_hwsum_err; (knp++)->value.ui64 = ngep->send->tc_next; (knp++)->value.ui64 = ngep->send->tx_next; (knp++)->value.ui64 = ngep->send->tx_free; (knp++)->value.ui64 = ngep->send->tx_flow; (knp++)->value.ui64 = ngep->recv->prod_index; (knp++)->value.ui64 = ngep->buff->rx_hold; (knp++)->value.ui64 = sw_stp->rx_nobuffer; (knp++)->value.ui64 = sw_stp->rx_err; (knp++)->value.ui64 = sw_stp->tx_stop_err; (knp++)->value.ui64 = sw_stp->tx_stall; return (0); } static kstat_t * nge_setup_named_kstat(nge_t *ngep, int instance, char *name, const nge_ksindex_t *ksip, size_t size, int (*update)(kstat_t *, int)) { kstat_t *ksp; kstat_named_t *knp; char *np; int type; size /= sizeof (nge_ksindex_t); ksp = kstat_create(NGE_DRIVER_NAME, instance, name, "net", KSTAT_TYPE_NAMED, size-1, KSTAT_FLAG_PERSISTENT); if (ksp == NULL) return (NULL); ksp->ks_private = ngep; ksp->ks_update = update; for (knp = ksp->ks_data; (np = ksip->name) != NULL; ++knp, ++ksip) { switch (*np) { default: type = KSTAT_DATA_UINT64; break; case '%': np += 1; type = KSTAT_DATA_UINT32; break; case '$': np ++; type = KSTAT_DATA_STRING; break; case '&': np ++; type = KSTAT_DATA_CHAR; break; } kstat_named_init(knp, np, type); } kstat_install(ksp); return (ksp); } void nge_init_kstats(nge_t *ngep, int instance) { NGE_TRACE(("nge_init_kstats($%p, %d)", (void *)ngep, instance)); ngep->nge_kstats[NGE_KSTAT_STATS] = nge_setup_named_kstat(ngep, instance, "statistics", nge_statistics, sizeof (nge_statistics), nge_statistics_update); ngep->nge_kstats[NGE_KSTAT_CHIPID] = nge_setup_named_kstat(ngep, instance, "chipinfo", nge_chipinfo, sizeof (nge_chipinfo), nge_chipinfo_update); ngep->nge_kstats[NGE_KSTAT_DEBUG] = nge_setup_named_kstat(ngep, instance, "driver-debug", nge_debuginfo, sizeof (nge_debuginfo), nge_debuginfo_update); } void nge_fini_kstats(nge_t *ngep) { int i; NGE_TRACE(("nge_fini_kstats($%p)", (void *)ngep)); for (i = NGE_KSTAT_COUNT; --i >= 0; ) if (ngep->nge_kstats[i] != NULL) kstat_delete(ngep->nge_kstats[i]); } int nge_m_stat(void *arg, uint_t stat, uint64_t *val) { nge_t *ngep = arg; uint32_t regno; nge_statistics_t *nstp = &ngep->statistics; nge_hw_statistics_t *hw_stp = &nstp->hw_statistics; nge_sw_statistics_t *sw_stp = &nstp->sw_statistics; switch (stat) { case MAC_STAT_IFSPEED: *val = ngep->param_link_speed * 1000000ull; break; case MAC_STAT_MULTIRCV: regno = KS_BASE + KS_ifHInMulPksCount * sizeof (uint32_t); hw_stp->s.InMulPksCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InMulPksCount; break; case MAC_STAT_BRDCSTRCV: regno = KS_BASE + KS_ifHInBroadPksCount * sizeof (uint32_t); hw_stp->s.InBroadPksCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InBroadPksCount; break; case MAC_STAT_NORCVBUF: *val = sw_stp->rx_nobuffer; break; case MAC_STAT_IERRORS: regno = KS_BASE + KS_ifHInFrameErrCount * sizeof (uint32_t); hw_stp->s.InFrameErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInExtraOctErrCount * sizeof (uint32_t); hw_stp->s.InExtraOctErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInLColErrCount * sizeof (uint32_t); hw_stp->s.InLColErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInOversizeErrCount * sizeof (uint32_t); hw_stp->s.InOversizeErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInFovErrCount * sizeof (uint32_t); hw_stp->s.InFovErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInFCSErrCount * sizeof (uint32_t); hw_stp->s.InFCSErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInAlignErrCount * sizeof (uint32_t); hw_stp->s.InAlignErrCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHInLenErrCount * sizeof (uint32_t); hw_stp->s.InLenErrCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InFrameErrCount + hw_stp->s.InExtraOctErrCount + hw_stp->s.InLColErrCount + hw_stp->s.InOversizeErrCount + hw_stp->s.InFovErrCount + hw_stp->s.InFCSErrCount + hw_stp->s.InAlignErrCount + hw_stp->s.InLenErrCount; break; case MAC_STAT_OERRORS: regno = KS_BASE + KS_ifHOutFifoovCount * sizeof (uint32_t); hw_stp->s.OutFifoovCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHOutLOCCount * sizeof (uint32_t); hw_stp->s.OutLOCCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHOutExDecCount * sizeof (uint32_t); hw_stp->s.OutExDecCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHOutRetryCount * sizeof (uint32_t); hw_stp->s.OutRetryCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutFifoovCount + hw_stp->s.OutLOCCount + hw_stp->s.OutExDecCount + hw_stp->s.OutRetryCount; break; case MAC_STAT_COLLISIONS: regno = KS_BASE + KS_ifHOutColCount * sizeof (uint32_t); hw_stp->s.OutColCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutColCount; break; case MAC_STAT_RBYTES: *val = sw_stp->rbytes; break; case MAC_STAT_IPACKETS: *val = sw_stp->recv_count; break; case MAC_STAT_OBYTES: *val = sw_stp->obytes; break; case MAC_STAT_OPACKETS: *val = sw_stp->xmit_count; break; case ETHER_STAT_ALIGN_ERRORS: regno = KS_BASE + KS_ifHInAlignErrCount * sizeof (uint32_t); hw_stp->s.InAlignErrCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InAlignErrCount; break; case ETHER_STAT_FCS_ERRORS: regno = KS_BASE + KS_ifHInFCSErrCount * sizeof (uint32_t); hw_stp->s.InFCSErrCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InFCSErrCount; break; case ETHER_STAT_FIRST_COLLISIONS: regno = KS_BASE + KS_ifHOutZeroRetranCount * sizeof (uint32_t); hw_stp->s.OutZeroRetranCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutZeroRetranCount; break; case ETHER_STAT_MULTI_COLLISIONS: regno = KS_BASE + KS_ifHOutOneRetranCount * sizeof (uint32_t); hw_stp->s.OutOneRetranCount += nge_reg_get32(ngep, regno); regno = KS_BASE + KS_ifHOutMoreRetranCount * sizeof (uint32_t); hw_stp->s.OutMoreRetranCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutOneRetranCount + hw_stp->s.OutMoreRetranCount; break; case ETHER_STAT_DEFER_XMTS: regno = KS_BASE + KS_ifHOutExDecCount * sizeof (uint32_t); hw_stp->s.OutExDecCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutExDecCount; break; case ETHER_STAT_TX_LATE_COLLISIONS: regno = KS_BASE + KS_ifHOutColCount * sizeof (uint32_t); hw_stp->s.OutColCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutColCount; break; case ETHER_STAT_EX_COLLISIONS: regno = KS_BASE + KS_ifHOutOneRetranCount * sizeof (uint32_t); hw_stp->s.OutOneRetranCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutOneRetranCount; break; case ETHER_STAT_CARRIER_ERRORS: regno = KS_BASE + KS_ifHOutLOCCount * sizeof (uint32_t); hw_stp->s.OutLOCCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.OutLOCCount; break; case ETHER_STAT_TOOLONG_ERRORS: regno = KS_BASE + KS_ifHInOversizeErrCount * sizeof (uint32_t); hw_stp->s.InOversizeErrCount += nge_reg_get32(ngep, regno); *val = hw_stp->s.InOversizeErrCount; break; case ETHER_STAT_XCVR_ADDR: *val = ngep->phy_xmii_addr; break; case ETHER_STAT_XCVR_ID: *val = ngep->phy_id; break; case ETHER_STAT_XCVR_INUSE: *val = XCVR_1000T; break; case ETHER_STAT_CAP_1000FDX: *val = 1; break; case ETHER_STAT_CAP_1000HDX: *val = 0; break; case ETHER_STAT_CAP_100FDX: *val = 1; break; case ETHER_STAT_CAP_100HDX: *val = 1; break; case ETHER_STAT_CAP_10FDX: *val = 1; break; case ETHER_STAT_CAP_10HDX: *val = 1; break; case ETHER_STAT_CAP_ASMPAUSE: *val = 1; break; case ETHER_STAT_CAP_PAUSE: *val = 1; break; case ETHER_STAT_CAP_AUTONEG: *val = 1; break; case ETHER_STAT_ADV_CAP_1000FDX: *val = ngep->param_adv_1000fdx; break; case ETHER_STAT_ADV_CAP_1000HDX: *val = ngep->param_adv_1000hdx; break; case ETHER_STAT_ADV_CAP_100FDX: *val = ngep->param_adv_100fdx; break; case ETHER_STAT_ADV_CAP_100HDX: *val = ngep->param_adv_100hdx; break; case ETHER_STAT_ADV_CAP_10FDX: *val = ngep->param_adv_10fdx; break; case ETHER_STAT_ADV_CAP_10HDX: *val = ngep->param_adv_10hdx; break; case ETHER_STAT_ADV_CAP_ASMPAUSE: *val = ngep->param_adv_asym_pause; break; case ETHER_STAT_ADV_CAP_PAUSE: *val = ngep->param_adv_pause; break; case ETHER_STAT_ADV_CAP_AUTONEG: *val = ngep->param_adv_autoneg; break; case ETHER_STAT_LP_CAP_1000FDX: *val = ngep->param_lp_1000fdx; break; case ETHER_STAT_LP_CAP_1000HDX: *val = ngep->param_lp_1000hdx; break; case ETHER_STAT_LP_CAP_100FDX: *val = ngep->param_lp_100fdx; break; case ETHER_STAT_LP_CAP_100HDX: *val = ngep->param_lp_100hdx; break; case ETHER_STAT_LP_CAP_10FDX: *val = ngep->param_lp_10fdx; break; case ETHER_STAT_LP_CAP_10HDX: *val = ngep->param_lp_10hdx; break; case ETHER_STAT_LP_CAP_ASMPAUSE: *val = ngep->param_lp_asym_pause; break; case ETHER_STAT_LP_CAP_PAUSE: *val = ngep->param_lp_pause; break; case ETHER_STAT_LP_CAP_AUTONEG: *val = ngep->param_lp_autoneg; break; case ETHER_STAT_LINK_ASMPAUSE: *val = ngep->param_adv_asym_pause && ngep->param_lp_asym_pause && ngep->param_adv_pause != ngep->param_lp_pause; break; case ETHER_STAT_LINK_PAUSE: *val = ngep->param_link_rx_pause; break; case ETHER_STAT_LINK_AUTONEG: *val = ngep->param_link_autoneg; break; case ETHER_STAT_LINK_DUPLEX: *val = ngep->param_link_duplex; break; default: return (ENOTSUP); } return (0); }