root/usr/src/uts/common/io/mac/mac_stat.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 2010 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright 2018 Joyent, Inc.
 * Copyright 2026 Oxide Computer Company
 */

/*
 * MAC Services Module
 */

#include <sys/types.h>
#include <sys/sysmacros.h>
#include <sys/stream.h>
#include <sys/kstat.h>
#include <sys/mac.h>
#include <sys/mac_impl.h>
#include <sys/mac_client_impl.h>
#include <sys/mac_stat.h>
#include <sys/mac_soft_ring.h>
#include <sys/vlan.h>

#define MAC_KSTAT_NAME  "mac"
#define MAC_KSTAT_CLASS "net"

enum mac_stat {
        MAC_STAT_LCL,
        MAC_STAT_LCLBYTES,
        MAC_STAT_INTRS,
        MAC_STAT_INTRBYTES,
        MAC_STAT_POLLS,
        MAC_STAT_POLLBYTES,
        MAC_STAT_RXSDROPS,
        MAC_STAT_CHU10,
        MAC_STAT_CH10T50,
        MAC_STAT_CHO50,
        MAC_STAT_BLOCK,
        MAC_STAT_UNBLOCK,
        MAC_STAT_TXSDROPS,
        MAC_STAT_TX_ERRORS,
        MAC_STAT_MACSPOOFED,
        MAC_STAT_IPSPOOFED,
        MAC_STAT_DHCPSPOOFED,
        MAC_STAT_RESTRICTED,
        MAC_STAT_DHCPDROPPED,
        MAC_STAT_MULTIRCVBYTES,
        MAC_STAT_BRDCSTRCVBYTES,
        MAC_STAT_MULTIXMTBYTES,
        MAC_STAT_BRDCSTXMTBYTES
};

static mac_stat_info_t  i_mac_si[] = {
        { MAC_STAT_IFSPEED,     "ifspeed",      KSTAT_DATA_UINT64,      0 },
        { MAC_STAT_MULTIRCV,    "multircv",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_BRDCSTRCV,   "brdcstrcv",    KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_MULTIXMT,    "multixmt",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_BRDCSTXMT,   "brdcstxmt",    KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_NORCVBUF,    "norcvbuf",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_IERRORS,     "ierrors",      KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_UNKNOWNS,    "unknowns",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_NOXMTBUF,    "noxmtbuf",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_OERRORS,     "oerrors",      KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_COLLISIONS,  "collisions",   KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_UNDERFLOWS,  "uflo",         KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_OVERFLOWS,   "oflo",         KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_OBYTES,      "obytes",       KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_OPACKETS,    "opackets",     KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_RBYTES,      "rbytes64",     KSTAT_DATA_UINT64,      0 },
        { MAC_STAT_IPACKETS,    "ipackets64",   KSTAT_DATA_UINT64,      0 },
        { MAC_STAT_OBYTES,      "obytes64",     KSTAT_DATA_UINT64,      0 },
        { MAC_STAT_OPACKETS,    "opackets64",   KSTAT_DATA_UINT64,      0 }
};
#define MAC_NKSTAT \
        (sizeof (i_mac_si) / sizeof (mac_stat_info_t))

static mac_stat_info_t  i_mac_mod_si[] = {
        { MAC_STAT_LINK_STATE,  "link_state",   KSTAT_DATA_UINT32,
            (uint64_t)LINK_STATE_UNKNOWN },
        { MAC_STAT_LINK_UP,     "link_up",      KSTAT_DATA_UINT32,      0 },
        { MAC_STAT_PROMISC,     "promisc",      KSTAT_DATA_UINT32,      0 }
};
#define MAC_MOD_NKSTAT \
        (sizeof (i_mac_mod_si) / sizeof (mac_stat_info_t))

#define MAC_MOD_KSTAT_OFFSET    0
#define MAC_KSTAT_OFFSET        MAC_MOD_KSTAT_OFFSET + MAC_MOD_NKSTAT
#define MAC_TYPE_KSTAT_OFFSET   MAC_KSTAT_OFFSET + MAC_NKSTAT

/*
 * Definitions for per rx ring statistics
 */
static mac_stat_info_t  i_mac_rx_ring_si[] = {
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_HDROPS,      "hdrops",       KSTAT_DATA_UINT64,      0}
};
#define MAC_RX_RING_NKSTAT \
        (sizeof (i_mac_rx_ring_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for per tx ring statistics
 */
static mac_stat_info_t  i_mac_tx_ring_si[] = {
        { MAC_STAT_OBYTES,      "obytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OPACKETS,    "opackets",     KSTAT_DATA_UINT64,      0}
};
#define MAC_TX_RING_NKSTAT \
        (sizeof (i_mac_tx_ring_si) / sizeof (mac_stat_info_t))


/*
 * Definitions for per software lane tx statistics
 */
static mac_stat_info_t  i_mac_tx_swlane_si[] = {
        { MAC_STAT_OBYTES,      "obytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OPACKETS,    "opackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OERRORS,     "oerrors",      KSTAT_DATA_UINT64,      0},
        { MAC_STAT_BLOCK,       "blockcnt",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_UNBLOCK,     "unblockcnt",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_TXSDROPS,    "txsdrops",     KSTAT_DATA_UINT64,      0}
};
#define MAC_TX_SWLANE_NKSTAT \
        (sizeof (i_mac_tx_swlane_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for per software lane rx statistics
 */
static mac_stat_info_t  i_mac_rx_swlane_si[] = {
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_LCL,         "local",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_LCLBYTES,    "localbytes",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRS,       "intrs",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRBYTES,   "intrbytes",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RXSDROPS,    "rxsdrops",     KSTAT_DATA_UINT64,      0}
};
#define MAC_RX_SWLANE_NKSTAT \
        (sizeof (i_mac_rx_swlane_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for per hardware lane rx statistics
 */
static mac_stat_info_t  i_mac_rx_hwlane_si[] = {
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRS,       "intrs",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRBYTES,   "intrbytes",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_POLLS,       "polls",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_POLLBYTES,   "pollbytes",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RXSDROPS,    "rxsdrops",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CHU10,       "chainunder10", KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CH10T50,     "chain10to50",  KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CHO50,       "chainover50",  KSTAT_DATA_UINT64,      0}
};
#define MAC_RX_HWLANE_NKSTAT \
        (sizeof (i_mac_rx_hwlane_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for misc statistics
 */
static mac_stat_info_t  i_mac_misc_si[] = {
        { MAC_STAT_MULTIRCV,    "multircv",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_BRDCSTRCV,   "brdcstrcv",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_MULTIXMT,    "multixmt",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_BRDCSTXMT,   "brdcstxmt",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_MULTIRCVBYTES, "multircvbytes",   KSTAT_DATA_UINT64, 0},
        { MAC_STAT_BRDCSTRCVBYTES, "brdcstrcvbytes", KSTAT_DATA_UINT64, 0},
        { MAC_STAT_MULTIXMTBYTES,  "multixmtbytes",  KSTAT_DATA_UINT64, 0},
        { MAC_STAT_BRDCSTXMTBYTES, "brdcstxmtbytes", KSTAT_DATA_UINT64, 0},
        { MAC_STAT_TX_ERRORS,   "txerrors",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_MACSPOOFED,  "macspoofed",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_IPSPOOFED,   "ipspoofed",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_DHCPSPOOFED, "dhcpspoofed",  KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RESTRICTED,  "restricted",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_DHCPDROPPED, "dhcpdropped",  KSTAT_DATA_UINT64,      0},
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_LCL,         "local",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_LCLBYTES,    "localbytes",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRS,       "intrs",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_INTRBYTES,   "intrbytes",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_POLLS,       "polls",        KSTAT_DATA_UINT64,      0},
        { MAC_STAT_POLLBYTES,   "pollbytes",    KSTAT_DATA_UINT64,      0},
        { MAC_STAT_RXSDROPS,    "rxsdrops",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CHU10,       "chainunder10", KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CH10T50,     "chain10to50",  KSTAT_DATA_UINT64,      0},
        { MAC_STAT_CHO50,       "chainover50",  KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OBYTES,      "obytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OPACKETS,    "opackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OERRORS,     "oerrors",      KSTAT_DATA_UINT64,      0},
        { MAC_STAT_BLOCK,       "blockcnt",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_UNBLOCK,     "unblockcnt",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_TXSDROPS,    "txsdrops",     KSTAT_DATA_UINT64,      0}
};
#define MAC_SUMMARY_NKSTAT \
        (sizeof (i_mac_misc_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for per hardware lane tx statistics
 */
static mac_stat_info_t  i_mac_tx_hwlane_si[] = {
        { MAC_STAT_OBYTES,      "obytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OPACKETS,    "opackets",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_OERRORS,     "oerrors",      KSTAT_DATA_UINT64,      0},
        { MAC_STAT_BLOCK,       "blockcnt",     KSTAT_DATA_UINT64,      0},
        { MAC_STAT_UNBLOCK,     "unblockcnt",   KSTAT_DATA_UINT64,      0},
        { MAC_STAT_TXSDROPS,    "txsdrops",     KSTAT_DATA_UINT64,      0}
};
#define MAC_TX_HWLANE_NKSTAT \
        (sizeof (i_mac_tx_hwlane_si) / sizeof (mac_stat_info_t))

/*
 * Definitions for per fanout rx statistics
 */
static mac_stat_info_t  i_mac_rx_fanout_si[] = {
        { MAC_STAT_RBYTES,      "rbytes",       KSTAT_DATA_UINT64,      0},
        { MAC_STAT_IPACKETS,    "ipackets",     KSTAT_DATA_UINT64,      0},
};
#define MAC_RX_FANOUT_NKSTAT \
        (sizeof (i_mac_rx_fanout_si) / sizeof (mac_stat_info_t))

/*
 * Private functions.
 */

typedef struct {
        uint_t  si_offset;
} stat_info_t;

#define RX_SRS_STAT_OFF(f)      (offsetof(mac_rx_stats_t, f))
static stat_info_t rx_srs_stats_list[] = {
        {RX_SRS_STAT_OFF(mrs_lclbytes)},
        {RX_SRS_STAT_OFF(mrs_lclcnt)},
        {RX_SRS_STAT_OFF(mrs_pollcnt)},
        {RX_SRS_STAT_OFF(mrs_pollbytes)},
        {RX_SRS_STAT_OFF(mrs_intrcnt)},
        {RX_SRS_STAT_OFF(mrs_intrbytes)},
        {RX_SRS_STAT_OFF(mrs_sdrops)},
        {RX_SRS_STAT_OFF(mrs_chaincntundr10)},
        {RX_SRS_STAT_OFF(mrs_chaincnt10to50)},
        {RX_SRS_STAT_OFF(mrs_chaincntover50)},
        {RX_SRS_STAT_OFF(mrs_ierrors)}
};
#define RX_SRS_STAT_SIZE                \
        (sizeof (rx_srs_stats_list) / sizeof (stat_info_t))

#define TX_SOFTRING_STAT_OFF(f) (offsetof(mac_tx_stats_t, f))
static stat_info_t tx_softring_stats_list[] = {
        {TX_SOFTRING_STAT_OFF(mts_obytes)},
        {TX_SOFTRING_STAT_OFF(mts_opackets)},
        {TX_SOFTRING_STAT_OFF(mts_oerrors)},
        {TX_SOFTRING_STAT_OFF(mts_blockcnt)},
        {TX_SOFTRING_STAT_OFF(mts_unblockcnt)},
        {TX_SOFTRING_STAT_OFF(mts_sdrops)},
};
#define TX_SOFTRING_STAT_SIZE           \
        (sizeof (tx_softring_stats_list) / sizeof (stat_info_t))

static void
i_mac_add_stats(void *sum, void *op1, void *op2,
    stat_info_t stats_list[], uint_t size)
{
        int     i;

        for (i = 0; i < size; i++) {
                uint64_t *op1_val = (uint64_t *)
                    ((uchar_t *)op1 + stats_list[i].si_offset);
                uint64_t *op2_val = (uint64_t *)
                    ((uchar_t *)op2 + stats_list[i].si_offset);
                uint64_t *sum_val = (uint64_t *)
                    ((uchar_t *)sum + stats_list[i].si_offset);

                *sum_val =  *op1_val + *op2_val;
        }
}

static int
i_mac_driver_stat_update(kstat_t *ksp, int rw)
{
        mac_impl_t      *mip = ksp->ks_private;
        kstat_named_t   *knp = ksp->ks_data;
        uint_t          i;
        uint64_t        val;
        mac_stat_info_t *msi;
        uint_t          msi_index;

        if (rw != KSTAT_READ)
                return (EACCES);

        for (i = 0; i < mip->mi_kstat_count; i++, msi_index++) {
                if (i == MAC_MOD_KSTAT_OFFSET) {
                        msi_index = 0;
                        msi = i_mac_mod_si;
                } else if (i == MAC_KSTAT_OFFSET) {
                        msi_index = 0;
                        msi = i_mac_si;
                } else if (i == MAC_TYPE_KSTAT_OFFSET) {
                        msi_index = 0;
                        msi = mip->mi_type->mt_stats;
                }

                val = mac_stat_get((mac_handle_t)mip, msi[msi_index].msi_stat);
                switch (msi[msi_index].msi_type) {
                case KSTAT_DATA_UINT64:
                        knp->value.ui64 = val;
                        break;
                case KSTAT_DATA_UINT32:
                        knp->value.ui32 = (uint32_t)val;
                        break;
                default:
                        ASSERT(B_FALSE);
                        break;
                }

                knp++;
        }

        return (0);
}

static void
i_mac_kstat_init(kstat_named_t *knp, mac_stat_info_t *si, uint_t count)
{
        int i;
        for (i = 0; i < count; i++) {
                kstat_named_init(knp, si[i].msi_name, si[i].msi_type);
                knp++;
        }
}

static int
i_mac_stat_update(kstat_t *ksp, int rw, uint64_t (*fn)(void *, uint_t),
    mac_stat_info_t *msi, uint_t count)
{
        kstat_named_t   *knp = ksp->ks_data;
        uint_t          i;
        uint64_t        val;

        if (rw != KSTAT_READ)
                return (EACCES);

        for (i = 0; i < count; i++) {
                val = fn(ksp->ks_private, msi[i].msi_stat);

                switch (msi[i].msi_type) {
                case KSTAT_DATA_UINT64:
                        knp->value.ui64 = val;
                        break;
                case KSTAT_DATA_UINT32:
                        knp->value.ui32 = (uint32_t)val;
                        break;
                default:
                        ASSERT(B_FALSE);
                        break;
                }
                knp++;
        }
        return (0);
}

/*
 * Create kstat with given name - statname, update function - fn
 * and initialize it with given names - init_stat_info
 */
static kstat_t *
i_mac_stat_create(void *handle, const char *modname, const char *statname,
    int (*fn) (kstat_t *, int),
    mac_stat_info_t *init_stat_info, uint_t count)
{
        kstat_t         *ksp;
        kstat_named_t   *knp;

        ksp = kstat_create(modname, 0, statname, "net",
            KSTAT_TYPE_NAMED, count, 0);

        if (ksp == NULL)
                return (NULL);

        ksp->ks_update = fn;
        ksp->ks_private = handle;

        knp = (kstat_named_t *)ksp->ks_data;
        i_mac_kstat_init(knp, init_stat_info, count);
        kstat_install(ksp);

        return (ksp);
}

/*
 * Per rx ring statistics
 */
uint64_t
mac_rx_ring_stat_get(void *handle, uint_t stat)
{
        mac_ring_t              *ring = (mac_ring_t *)handle;
        uint64_t                val = 0;

        /*
         * XXX Every ring-capable driver must implement an entry point to
         * query per ring statistics. CR 6893122 tracks this work item.
         * Once this bug is fixed, the framework should fail registration
         * for a driver that does not implement this entry point and
         * assert ring->mr_stat != NULL here.
         */
        if (ring->mr_stat != NULL)
                ring->mr_stat(ring->mr_driver, stat, &val);

        return (val);
}

static int
i_mac_rx_ring_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, mac_rx_ring_stat_get,
            i_mac_rx_ring_si, MAC_RX_RING_NKSTAT));
}

static void
i_mac_rx_ring_stat_create(mac_ring_t *ring, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(ring, modname, statname,
            i_mac_rx_ring_stat_update, i_mac_rx_ring_si, MAC_RX_RING_NKSTAT);

        ring->mr_ksp = ksp;
}

/*
 * Per tx ring statistics
 */
uint64_t
mac_tx_ring_stat_get(void *handle, uint_t stat)
{
        mac_ring_t              *ring = (mac_ring_t *)handle;
        uint64_t                val = 0;

        /*
         * XXX Every ring-capable driver must implement an entry point to
         * query per ring statistics. CR 6893122 tracks this work item.
         * Once this bug is fixed, the framework should fail registration
         * for a driver that does not implement this entry point and
         * assert ring->mr_stat != NULL here.
         */
        if (ring->mr_stat != NULL)
                ring->mr_stat(ring->mr_driver, stat, &val);

        return (val);
}

static int
i_mac_tx_ring_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, mac_tx_ring_stat_get,
            i_mac_tx_ring_si, MAC_TX_RING_NKSTAT));
}

static void
i_mac_tx_ring_stat_create(mac_ring_t *ring, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(ring, modname, statname,
            i_mac_tx_ring_stat_update, i_mac_tx_ring_si, MAC_TX_RING_NKSTAT);

        ring->mr_ksp = ksp;
}

/*
 * Per software lane tx statistics
 */
static uint64_t
i_mac_tx_swlane_stat_get(void *handle, uint_t stat)
{
        mac_soft_ring_set_t *mac_srs = (mac_soft_ring_set_t *)handle;
        mac_tx_stats_t *mac_tx_stat = &mac_srs->srs_tx.st_stat;

        switch (stat) {
        case MAC_STAT_OBYTES:
                return (mac_tx_stat->mts_obytes);

        case MAC_STAT_OPACKETS:
                return (mac_tx_stat->mts_opackets);

        case MAC_STAT_OERRORS:
                return (mac_tx_stat->mts_oerrors);

        case MAC_STAT_BLOCK:
                return (mac_tx_stat->mts_blockcnt);

        case MAC_STAT_UNBLOCK:
                return (mac_tx_stat->mts_unblockcnt);

        case MAC_STAT_TXSDROPS:
                return (mac_tx_stat->mts_sdrops);

        default:
                return (0);
        }
}

static int
i_mac_tx_swlane_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_tx_swlane_stat_get,
            i_mac_tx_swlane_si, MAC_TX_SWLANE_NKSTAT));
}

static void
i_mac_tx_swlane_stat_create(mac_soft_ring_set_t *mac_srs, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(mac_srs, modname, statname,
            i_mac_tx_swlane_stat_update, i_mac_tx_swlane_si,
            MAC_TX_SWLANE_NKSTAT);

        mac_srs->srs_ksp = ksp;
}

/*
 * Per software lane rx statistics
 */
static uint64_t
i_mac_rx_swlane_stat_get(void *handle, uint_t stat)
{
        mac_soft_ring_set_t     *mac_srs = (mac_soft_ring_set_t *)handle;
        mac_rx_stats_t          *mac_rx_stat = &mac_srs->srs_rx.sr_stat;

        switch (stat) {
        case MAC_STAT_IPACKETS:
                return (mac_rx_stat->mrs_intrcnt +
                    mac_rx_stat->mrs_lclcnt);

        case MAC_STAT_RBYTES:
                return (mac_rx_stat->mrs_intrbytes +
                    mac_rx_stat->mrs_lclbytes);

        case MAC_STAT_LCL:
                return (mac_rx_stat->mrs_lclcnt);

        case MAC_STAT_LCLBYTES:
                return (mac_rx_stat->mrs_lclbytes);

        case MAC_STAT_INTRS:
                return (mac_rx_stat->mrs_intrcnt);

        case MAC_STAT_INTRBYTES:
                return (mac_rx_stat->mrs_intrbytes);

        case MAC_STAT_RXSDROPS:
                return (mac_rx_stat->mrs_sdrops);

        default:
                return (0);
        }
}

static int
i_mac_rx_swlane_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_rx_swlane_stat_get,
            i_mac_rx_swlane_si, MAC_RX_SWLANE_NKSTAT));
}

static void
i_mac_rx_swlane_stat_create(mac_soft_ring_set_t *mac_srs, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(mac_srs, modname, statname,
            i_mac_rx_swlane_stat_update, i_mac_rx_swlane_si,
            MAC_RX_SWLANE_NKSTAT);

        mac_srs->srs_ksp = ksp;
}


/*
 * Per hardware lane rx statistics
 */
static uint64_t
i_mac_rx_hwlane_stat_get(void *handle, uint_t stat)
{
        mac_soft_ring_set_t     *mac_srs = (mac_soft_ring_set_t *)handle;
        mac_rx_stats_t          *mac_rx_stat = &mac_srs->srs_rx.sr_stat;

        switch (stat) {
        case MAC_STAT_IPACKETS:
                return (mac_rx_stat->mrs_intrcnt +
                    mac_rx_stat->mrs_pollcnt);

        case MAC_STAT_RBYTES:
                return (mac_rx_stat->mrs_intrbytes +
                    mac_rx_stat->mrs_pollbytes);

        case MAC_STAT_INTRS:
                return (mac_rx_stat->mrs_intrcnt);

        case MAC_STAT_INTRBYTES:
                return (mac_rx_stat->mrs_intrbytes);

        case MAC_STAT_POLLS:
                return (mac_rx_stat->mrs_pollcnt);

        case MAC_STAT_POLLBYTES:
                return (mac_rx_stat->mrs_pollbytes);

        case MAC_STAT_RXSDROPS:
                return (mac_rx_stat->mrs_sdrops);

        case MAC_STAT_CHU10:
                return (mac_rx_stat->mrs_chaincntundr10);

        case MAC_STAT_CH10T50:
                return (mac_rx_stat->mrs_chaincnt10to50);

        case MAC_STAT_CHO50:
                return (mac_rx_stat->mrs_chaincntover50);

        default:
                return (0);
        }
}

static int
i_mac_rx_hwlane_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_rx_hwlane_stat_get,
            i_mac_rx_hwlane_si, MAC_RX_HWLANE_NKSTAT));
}

static void
i_mac_rx_hwlane_stat_create(mac_soft_ring_set_t *mac_srs, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(mac_srs, modname, statname,
            i_mac_rx_hwlane_stat_update, i_mac_rx_hwlane_si,
            MAC_RX_HWLANE_NKSTAT);

        mac_srs->srs_ksp = ksp;
}


/*
 * Misc statistics
 *
 * Counts for
 *      - Multicast/broadcast Rx/Tx counts
 *      - Tx errors
 */
static uint64_t
i_mac_misc_stat_get(void *handle, uint_t stat)
{
        flow_entry_t            *flent = handle;
        mac_client_impl_t       *mcip = flent->fe_mcip;
        mac_misc_stats_t        *mac_misc_stat = &mcip->mci_misc_stat;
        mac_rx_stats_t          *mac_rx_stat;
        mac_tx_stats_t          *mac_tx_stat;

        mac_rx_stat = &mac_misc_stat->mms_defunctrxlanestats;
        mac_tx_stat = &mac_misc_stat->mms_defuncttxlanestats;

        switch (stat) {
        case MAC_STAT_MULTIRCV:
                return (mac_misc_stat->mms_multircv);

        case MAC_STAT_BRDCSTRCV:
                return (mac_misc_stat->mms_brdcstrcv);

        case MAC_STAT_MULTIXMT:
                return (mac_misc_stat->mms_multixmt);

        case MAC_STAT_BRDCSTXMT:
                return (mac_misc_stat->mms_brdcstxmt);

        case MAC_STAT_MULTIRCVBYTES:
                return (mac_misc_stat->mms_multircvbytes);

        case MAC_STAT_BRDCSTRCVBYTES:
                return (mac_misc_stat->mms_brdcstrcvbytes);

        case MAC_STAT_MULTIXMTBYTES:
                return (mac_misc_stat->mms_multixmtbytes);

        case MAC_STAT_BRDCSTXMTBYTES:
                return (mac_misc_stat->mms_brdcstxmtbytes);

        case MAC_STAT_TX_ERRORS:
                return (mac_misc_stat->mms_txerrors);

        case MAC_STAT_MACSPOOFED:
                return (mac_misc_stat->mms_macspoofed);

        case MAC_STAT_IPSPOOFED:
                return (mac_misc_stat->mms_ipspoofed);

        case MAC_STAT_DHCPSPOOFED:
                return (mac_misc_stat->mms_dhcpspoofed);

        case MAC_STAT_RESTRICTED:
                return (mac_misc_stat->mms_restricted);

        case MAC_STAT_DHCPDROPPED:
                return (mac_misc_stat->mms_dhcpdropped);

        case MAC_STAT_IPACKETS:
                return (mac_rx_stat->mrs_intrcnt +
                    mac_rx_stat->mrs_pollcnt);

        case MAC_STAT_RBYTES:
                return (mac_rx_stat->mrs_intrbytes +
                    mac_rx_stat->mrs_pollbytes);

        case MAC_STAT_LCL:
                return (mac_rx_stat->mrs_lclcnt);

        case MAC_STAT_LCLBYTES:
                return (mac_rx_stat->mrs_lclbytes);

        case MAC_STAT_INTRS:
                return (mac_rx_stat->mrs_intrcnt);

        case MAC_STAT_INTRBYTES:
                return (mac_rx_stat->mrs_intrbytes);

        case MAC_STAT_POLLS:
                return (mac_rx_stat->mrs_pollcnt);

        case MAC_STAT_POLLBYTES:
                return (mac_rx_stat->mrs_pollbytes);

        case MAC_STAT_RXSDROPS:
                return (mac_rx_stat->mrs_sdrops);

        case MAC_STAT_CHU10:
                return (mac_rx_stat->mrs_chaincntundr10);

        case MAC_STAT_CH10T50:
                return (mac_rx_stat->mrs_chaincnt10to50);

        case MAC_STAT_CHO50:
                return (mac_rx_stat->mrs_chaincntover50);

        case MAC_STAT_OBYTES:
                return (mac_tx_stat->mts_obytes);

        case MAC_STAT_OPACKETS:
                return (mac_tx_stat->mts_opackets);

        case MAC_STAT_OERRORS:
                return (mac_tx_stat->mts_oerrors);

        case MAC_STAT_BLOCK:
                return (mac_tx_stat->mts_blockcnt);

        case MAC_STAT_UNBLOCK:
                return (mac_tx_stat->mts_unblockcnt);

        case MAC_STAT_TXSDROPS:
                return (mac_tx_stat->mts_sdrops);

        default:
                return (0);
        }
}

static int
i_mac_misc_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_misc_stat_get,
            i_mac_misc_si, MAC_SUMMARY_NKSTAT));
}

static void
i_mac_misc_stat_create(flow_entry_t *flent, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(flent, modname, statname,
            i_mac_misc_stat_update, i_mac_misc_si,
            MAC_SUMMARY_NKSTAT);

        flent->fe_misc_stat_ksp = ksp;
}

/*
 * Per hardware lane tx statistics
 */
static uint64_t
i_mac_tx_hwlane_stat_get(void *handle, uint_t stat)
{
        mac_soft_ring_t *ringp = (mac_soft_ring_t *)handle;
        mac_tx_stats_t  *mac_tx_stat = &ringp->s_st_stat;

        switch (stat) {
        case MAC_STAT_OBYTES:
                return (mac_tx_stat->mts_obytes);

        case MAC_STAT_OPACKETS:
                return (mac_tx_stat->mts_opackets);

        case MAC_STAT_OERRORS:
                return (mac_tx_stat->mts_oerrors);

        case MAC_STAT_BLOCK:
                return (mac_tx_stat->mts_blockcnt);

        case MAC_STAT_UNBLOCK:
                return (mac_tx_stat->mts_unblockcnt);

        case MAC_STAT_TXSDROPS:
                return (mac_tx_stat->mts_sdrops);

        default:
                return (0);
        }
}

static int
i_mac_tx_hwlane_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_tx_hwlane_stat_get,
            i_mac_tx_hwlane_si, MAC_TX_HWLANE_NKSTAT));
}

static void
i_mac_tx_hwlane_stat_create(mac_soft_ring_t *ringp, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(ringp, modname, statname,
            i_mac_tx_hwlane_stat_update, i_mac_tx_hwlane_si,
            MAC_TX_HWLANE_NKSTAT);

        ringp->s_ring_ksp = ksp;
}

/*
 * Per fanout rx statistics
 */
static uint64_t
i_mac_rx_fanout_stat_get(void *handle, uint_t stat)
{
        mac_soft_ring_t         *tcp_ringp = (mac_soft_ring_t *)handle;
        mac_soft_ring_t         *tcp6_ringp = NULL, *udp_ringp = NULL;
        mac_soft_ring_t         *udp6_ringp = NULL, *oth_ringp = NULL;
        mac_soft_ring_set_t     *mac_srs = tcp_ringp->s_ring_set;
        int                     index;
        uint64_t                val;

        mutex_enter(&mac_srs->srs_lock);
        /* Extract corresponding udp and oth ring pointers */
        for (index = 0; mac_srs->srs_tcp_soft_rings[index] != NULL; index++) {
                if (mac_srs->srs_tcp_soft_rings[index] == tcp_ringp) {
                        tcp6_ringp = mac_srs->srs_tcp6_soft_rings[index];
                        udp_ringp = mac_srs->srs_udp_soft_rings[index];
                        udp6_ringp = mac_srs->srs_udp6_soft_rings[index];
                        oth_ringp = mac_srs->srs_oth_soft_rings[index];
                        break;
                }
        }

        ASSERT((tcp6_ringp != NULL) && (udp_ringp != NULL) &&
            (udp6_ringp != NULL) && (oth_ringp != NULL));

        switch (stat) {
        case MAC_STAT_RBYTES:
                val = (tcp_ringp->s_ring_total_rbytes) +
                    (tcp6_ringp->s_ring_total_rbytes) +
                    (udp_ringp->s_ring_total_rbytes) +
                    (udp6_ringp->s_ring_total_rbytes) +
                    (oth_ringp->s_ring_total_rbytes);
                break;

        case MAC_STAT_IPACKETS:
                val = (tcp_ringp->s_ring_total_inpkt) +
                    (tcp6_ringp->s_ring_total_inpkt) +
                    (udp_ringp->s_ring_total_inpkt) +
                    (udp6_ringp->s_ring_total_inpkt) +
                    (oth_ringp->s_ring_total_inpkt);
                break;

        default:
                val = 0;
                break;
        }
        mutex_exit(&mac_srs->srs_lock);
        return (val);
}

static int
i_mac_rx_fanout_stat_update(kstat_t *ksp, int rw)
{
        return (i_mac_stat_update(ksp, rw, i_mac_rx_fanout_stat_get,
            i_mac_rx_fanout_si, MAC_RX_FANOUT_NKSTAT));
}

static void
i_mac_rx_fanout_stat_create(mac_soft_ring_t *ringp, const char *modname,
    const char *statname)
{
        kstat_t         *ksp;

        ksp = i_mac_stat_create(ringp, modname, statname,
            i_mac_rx_fanout_stat_update, i_mac_rx_fanout_si,
            MAC_RX_FANOUT_NKSTAT);

        ringp->s_ring_ksp = ksp;
}

/*
 * Exported functions.
 */

/*
 * Create the "mac" kstat.  The "mac" kstat is comprised of three kinds of
 * statistics: statistics maintained by the mac module itself, generic mac
 * statistics maintained by the driver, and MAC-type specific statistics
 * also maintained by the driver.
 */
void
mac_driver_stat_create(mac_impl_t *mip)
{
        kstat_t         *ksp;
        kstat_named_t   *knp;
        uint_t          count;
        major_t         major = getmajor(mip->mi_phy_dev);

        count = MAC_MOD_NKSTAT + MAC_NKSTAT + mip->mi_type->mt_statcount;
        ksp = kstat_create((const char *)ddi_major_to_name(major),
            getminor(mip->mi_phy_dev) - 1, MAC_KSTAT_NAME,
            MAC_KSTAT_CLASS, KSTAT_TYPE_NAMED, count, 0);
        if (ksp == NULL)
                return;

        ksp->ks_update = i_mac_driver_stat_update;
        ksp->ks_private = mip;
        mip->mi_ksp = ksp;
        mip->mi_kstat_count = count;

        knp = (kstat_named_t *)ksp->ks_data;
        i_mac_kstat_init(knp, i_mac_mod_si, MAC_MOD_NKSTAT);
        knp += MAC_MOD_NKSTAT;
        i_mac_kstat_init(knp, i_mac_si, MAC_NKSTAT);
        if (mip->mi_type->mt_statcount > 0) {
                knp += MAC_NKSTAT;
                i_mac_kstat_init(knp, mip->mi_type->mt_stats,
                    mip->mi_type->mt_statcount);
        }

        kstat_install(ksp);
}

/*ARGSUSED*/
void
mac_driver_stat_delete(mac_impl_t *mip)
{
        if (mip->mi_ksp != NULL) {
                kstat_delete(mip->mi_ksp);
                mip->mi_ksp = NULL;
                mip->mi_kstat_count = 0;
        }
}

uint64_t
mac_driver_stat_default(mac_impl_t *mip, uint_t stat)
{
        uint_t  stat_index;

        if (IS_MAC_STAT(stat)) {
                stat_index = stat - MAC_STAT_MIN;
                ASSERT(stat_index < MAC_NKSTAT);
                return (i_mac_si[stat_index].msi_default);
        }
        ASSERT(IS_MACTYPE_STAT(stat));
        stat_index = stat - MACTYPE_STAT_MIN;
        ASSERT(stat_index < mip->mi_type->mt_statcount);
        return (mip->mi_type->mt_stats[stat_index].msi_default);
}

void
mac_ring_stat_create(mac_ring_t *ring)
{
        mac_impl_t      *mip = ring->mr_mip;
        mac_group_t     *grp = (mac_group_t *)ring->mr_gh;
        char            statname[MAXNAMELEN];
        char            modname[MAXNAMELEN];

        if (mip->mi_state_flags & MIS_IS_AGGR) {
                (void) strlcpy(modname, mip->mi_clients_list->mci_name,
                    MAXNAMELEN);
        } else
                (void) strlcpy(modname, mip->mi_name, MAXNAMELEN);

        switch (ring->mr_type) {
        case MAC_RING_TYPE_RX:
                (void) snprintf(statname, sizeof (statname),
                    "mac_rx_ring_%d_%d", grp->mrg_index, ring->mr_index);
                i_mac_rx_ring_stat_create(ring, modname, statname);
                break;

        case MAC_RING_TYPE_TX:
                (void) snprintf(statname, sizeof (statname), "mac_tx_ring%d",
                    ring->mr_index);
                i_mac_tx_ring_stat_create(ring, modname, statname);
                break;

        default:
                ASSERT(B_FALSE);
                break;
        }
}

void
mac_srs_stat_create(mac_soft_ring_set_t *mac_srs)
{
        flow_entry_t    *flent = mac_srs->srs_flent;
        char            statname[MAXNAMELEN];
        boolean_t       is_tx_srs;

        /* No hardware/software lanes for user defined flows */
        if ((flent->fe_type & FLOW_USER) != 0)
                return;

        is_tx_srs = ((mac_srs->srs_type & SRST_TX) != 0);

        if (is_tx_srs) {
                mac_srs_tx_t    *srs_tx = &mac_srs->srs_tx;
                mac_ring_t      *ring = srs_tx->st_arg2;

                if (ring != NULL) {
                        (void) snprintf(statname, sizeof (statname),
                            "mac_tx_hwlane%d", ring->mr_index);
                } else {
                        (void) snprintf(statname, sizeof (statname),
                            "mac_tx_swlane0");
                }
                i_mac_tx_swlane_stat_create(mac_srs, flent->fe_flow_name,
                    statname);
        } else {
                mac_ring_t      *ring = mac_srs->srs_ring;

                if (ring == NULL) {
                        (void) snprintf(statname, sizeof (statname),
                            "mac_rx_swlane0");
                        i_mac_rx_swlane_stat_create(mac_srs,
                            flent->fe_flow_name, statname);
                } else {
                        (void) snprintf(statname, sizeof (statname),
                            "mac_rx_hwlane%d", ring->mr_index);
                        i_mac_rx_hwlane_stat_create(mac_srs,
                            flent->fe_flow_name, statname);
                }
        }
}

void
mac_misc_stat_create(flow_entry_t *flent)
{
        char    statname[MAXNAMELEN];

        /* No misc stats for user defined or mcast/bcast flows */
        if (((flent->fe_type & FLOW_USER) != 0) ||
            ((flent->fe_type & FLOW_MCAST) != 0))
                return;

        (void) snprintf(statname, sizeof (statname), "mac_misc_stat");
        i_mac_misc_stat_create(flent, flent->fe_flow_name, statname);
}

void
mac_soft_ring_stat_create(mac_soft_ring_t *ringp)
{
        mac_soft_ring_set_t     *mac_srs = ringp->s_ring_set;
        flow_entry_t            *flent = ringp->s_ring_mcip->mci_flent;
        mac_ring_t              *ring = ringp->s_ring_tx_arg2;
        boolean_t               is_tx_srs;
        char                    statname[MAXNAMELEN];

        /* No hardware/software lanes for user defined flows */
        if ((flent->fe_type & FLOW_USER) != 0)
                return;

        is_tx_srs = ((mac_srs->srs_type & SRST_TX) != 0);
        if (is_tx_srs) {
                ASSERT(ring != NULL);
                (void) snprintf(statname, sizeof (statname), "mac_tx_hwlane%d",
                    ring->mr_index);
                i_mac_tx_hwlane_stat_create(ringp, flent->fe_flow_name,
                    statname);
        } else {
                /*
                 * We maintain a single "fanout lane" stat per set of rx
                 * protocol softrings. That is, each set of (TCP/TCP6, UDP/UDP6,
                 * OTH) softrings counts as a single lane.
                 */
                if (ringp->s_ring_state & ST_RING_TCP) {
                        int                     index;
                        int                     fanout_lane;
                        mac_soft_ring_t         *softring;

                        for (index = 0, softring = mac_srs->srs_soft_ring_head;
                            softring != NULL;
                            index++, softring = softring->s_ring_next) {
                                if (softring == ringp)
                                        break;
                        }

                        fanout_lane = index / ST_RING_NUM_PROTO;

                        if (mac_srs->srs_ring == NULL) {
                                (void) snprintf(statname, sizeof (statname),
                                    "mac_rx_swlane0_fanout%d", fanout_lane);
                        } else {
                                (void) snprintf(statname, sizeof (statname),
                                    "mac_rx_hwlane%d_fanout%d",
                                    mac_srs->srs_ring->mr_index, fanout_lane);
                        }
                        i_mac_rx_fanout_stat_create(ringp, flent->fe_flow_name,
                            statname);
                }
        }
}

void
mac_ring_stat_delete(mac_ring_t *ring)
{
        if (ring->mr_ksp != NULL) {
                kstat_delete(ring->mr_ksp);
                ring->mr_ksp = NULL;
        }
}

void
mac_srs_stat_delete(mac_soft_ring_set_t *mac_srs)
{
        boolean_t       is_tx_srs;

        is_tx_srs = ((mac_srs->srs_type & SRST_TX) != 0);
        if (!is_tx_srs) {
                /*
                 * Rx ring has been taken away. Before destroying corresponding
                 * SRS, save the stats recorded by that SRS.
                 */
                mac_client_impl_t       *mcip = mac_srs->srs_mcip;
                mac_misc_stats_t        *mac_misc_stat = &mcip->mci_misc_stat;
                mac_rx_stats_t          *mac_rx_stat = &mac_srs->srs_rx.sr_stat;

                i_mac_add_stats(&mac_misc_stat->mms_defunctrxlanestats,
                    mac_rx_stat, &mac_misc_stat->mms_defunctrxlanestats,
                    rx_srs_stats_list, RX_SRS_STAT_SIZE);
        }

        if (mac_srs->srs_ksp != NULL) {
                kstat_delete(mac_srs->srs_ksp);
                mac_srs->srs_ksp = NULL;
        }
}

void
mac_misc_stat_delete(flow_entry_t *flent)
{
        if (flent->fe_misc_stat_ksp != NULL) {
                kstat_delete(flent->fe_misc_stat_ksp);
                flent->fe_misc_stat_ksp = NULL;
        }
}

void
mac_soft_ring_stat_delete(mac_soft_ring_t *ringp)
{
        mac_soft_ring_set_t     *mac_srs = ringp->s_ring_set;
        boolean_t               is_tx_srs;

        is_tx_srs = ((mac_srs->srs_type & SRST_TX) != 0);
        if (is_tx_srs) {
                /*
                 * Tx ring has been taken away. Before destroying corresponding
                 * soft ring, save the stats recorded by that soft ring.
                 */
                mac_client_impl_t       *mcip = mac_srs->srs_mcip;
                mac_misc_stats_t        *mac_misc_stat = &mcip->mci_misc_stat;
                mac_tx_stats_t          *mac_tx_stat = &ringp->s_st_stat;

                i_mac_add_stats(&mac_misc_stat->mms_defuncttxlanestats,
                    mac_tx_stat, &mac_misc_stat->mms_defuncttxlanestats,
                    tx_softring_stats_list, TX_SOFTRING_STAT_SIZE);
        }

        if (ringp->s_ring_ksp) {
                kstat_delete(ringp->s_ring_ksp);
                ringp->s_ring_ksp = NULL;
        }
}

void
mac_pseudo_ring_stat_rename(mac_impl_t *mip)
{
        mac_group_t     *group;
        mac_ring_t      *ring;

        /* Recreate pseudo rx ring kstats */
        for (group = mip->mi_rx_groups; group != NULL;
            group = group->mrg_next) {
                for (ring = group->mrg_rings; ring != NULL;
                    ring = ring->mr_next) {
                        mac_ring_stat_delete(ring);
                        mac_ring_stat_create(ring);
                }
        }

        /* Recreate pseudo tx ring kstats */
        for (group = mip->mi_tx_groups; group != NULL;
            group = group->mrg_next) {
                for (ring = group->mrg_rings; ring != NULL;
                    ring = ring->mr_next) {
                        mac_ring_stat_delete(ring);
                        mac_ring_stat_create(ring);
                }
        }
}

void
mac_stat_rename(mac_client_impl_t *mcip)
{
        flow_entry_t            *flent = mcip->mci_flent;
        mac_soft_ring_set_t     *mac_srs;
        mac_soft_ring_t         *ringp;
        int                     i, j;

        ASSERT(flent != NULL);

        /* Recreate rx SRSes kstats */
        for (i = 0; i < flent->fe_rx_srs_cnt; i++) {
                mac_srs = (mac_soft_ring_set_t *)flent->fe_rx_srs[i];
                mac_srs_stat_delete(mac_srs);
                mac_srs_stat_create(mac_srs);

                /* Recreate rx fanout kstats */
                for (j = 0; j < mac_srs->srs_tcp_ring_count; j++) {
                        ringp = mac_srs->srs_tcp_soft_rings[j];
                        mac_soft_ring_stat_delete(ringp);
                        mac_soft_ring_stat_create(ringp);
                }
        }

        /* Recreate tx SRS kstats */
        mac_srs = (mac_soft_ring_set_t *)flent->fe_tx_srs;
        mac_srs_stat_delete(mac_srs);
        mac_srs_stat_create(mac_srs);

        /* Recreate tx sofring kstats */
        for (ringp = mac_srs->srs_soft_ring_head; ringp;
            ringp = ringp->s_ring_next) {
                mac_soft_ring_stat_delete(ringp);
                mac_soft_ring_stat_create(ringp);
        }

        /* Recreate misc kstats */
        mac_misc_stat_delete(flent);
        mac_misc_stat_create(flent);
}

void
mac_tx_srs_stat_recreate(mac_soft_ring_set_t *tx_srs, boolean_t add_stats)
{
        mac_client_impl_t       *mcip = tx_srs->srs_mcip;
        mac_misc_stats_t        *mac_misc_stat = &mcip->mci_misc_stat;
        mac_tx_stats_t          *mac_tx_stat = &tx_srs->srs_tx.st_stat;

        if (add_stats) {
                /* Add the stats to cumulative stats */
                i_mac_add_stats(&mac_misc_stat->mms_defuncttxlanestats,
                    mac_tx_stat, &mac_misc_stat->mms_defuncttxlanestats,
                    tx_softring_stats_list, TX_SOFTRING_STAT_SIZE);
        }

        bzero(mac_tx_stat, sizeof (mac_tx_stats_t));
        mac_srs_stat_delete(tx_srs);
        mac_srs_stat_create(tx_srs);
}