/*
* 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);
}