root/usr/src/uts/common/io/nge/nge_kstats.c
/*
 * 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 "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_ifHOutOneRetranCount * sizeof (uint32_t);
                hw_stp->s.OutOneRetranCount += nge_reg_get32(ngep, regno);
                *val = hw_stp->s.OutOneRetranCount;
                break;

        case ETHER_STAT_MULTI_COLLISIONS:
                regno = KS_BASE + KS_ifHOutMoreRetranCount * sizeof (uint32_t);
                hw_stp->s.OutMoreRetranCount += nge_reg_get32(ngep, regno);
                *val =  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;

        case ETHER_STAT_CAP_100T4:
        case ETHER_STAT_LP_CAP_100T4:
                *val = 0;
                break;

        default:
                return (ENOTSUP);
        }

        return (0);
}