root/usr/src/uts/common/io/fibre-channel/fca/oce/oce_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 © 2003-2011 Emulex. All rights reserved.  */

/*
 * Source file containing the implementation of the driver statistics
 * and related helper functions
 */

#include <oce_impl.h>
#include <oce_stat.h>
#include <oce_buf.h>

int pow10[5] = {
        0,
        10,
        100,
        1000,
        10000
};

/*
 * function called by kstat to update the stats counters
 *
 * ksp - pointer to the kstats structure
 * rw - flags defining read/write
 *
 * return DDI_SUCCESS => success, failure otherwise
 */
static int
oce_update_stats(kstat_t *ksp, int rw)
{
        struct oce_dev *dev;
        struct oce_stat *stats;
        struct rx_port_stats *port_stats;
        int ret;

        if (rw == KSTAT_WRITE) {
                return (EACCES);
        }

        dev = ksp->ks_private;
        stats = (struct oce_stat *)ksp->ks_data;
        port_stats = &dev->hw_stats->params.rsp.rx.port[dev->port_id];

        mutex_enter(&dev->dev_lock);
        if (dev->suspended) {
                mutex_exit(&dev->dev_lock);
                return (EIO);
        }
        ret = oce_get_hw_stats(dev);
        if (ret != DDI_SUCCESS) {
                oce_log(dev, CE_WARN, MOD_CONFIG,
                    "Failed to get stats:%d", ret);
                mutex_exit(&dev->dev_lock);
                return (EIO);
        }

        /* update the stats */
        stats->rx_bytes_lo.value.ul = port_stats->rx_bytes_lsd;
        stats->rx_bytes_hi.value.ul = port_stats->rx_bytes_msd;

        stats->rx_frames.value.ul = port_stats->rx_total_frames;
        stats->rx_errors.value.ul = port_stats->rx_crc_errors +
            port_stats->rx_alignment_symbol_errors +
            port_stats->rx_in_range_errors +
            port_stats->rx_out_range_errors +
            port_stats->rx_frame_too_long +
            port_stats->rx_ip_checksum_errs +
            port_stats->rx_tcp_checksum_errs +
            port_stats->rx_udp_checksum_errs;

        stats->rx_drops.value.ul = port_stats->rx_dropped_too_small +
            port_stats->rx_dropped_too_short +
            port_stats->rx_dropped_header_too_small +
            port_stats->rx_dropped_tcp_length +
            port_stats->rx_dropped_runt;

        stats->tx_bytes_lo.value.ul = port_stats->tx_bytes_lsd;
        stats->tx_bytes_hi.value.ul = port_stats->tx_bytes_msd;

        stats->tx_frames.value.ul = port_stats->tx_unicast_frames +
            port_stats->tx_multicast_frames +
            port_stats->tx_broadcast_frames +
            port_stats->tx_pause_frames +
            port_stats->tx_control_frames;
        stats->tx_errors.value.ul = dev->tx_errors;

        stats->rx_unicast_frames.value.ul =
            port_stats->rx_unicast_frames;
        stats->rx_multicast_frames.value.ul =
            port_stats->rx_multicast_frames;
        stats->rx_broadcast_frames.value.ul =
            port_stats->rx_broadcast_frames;
        stats->rx_crc_errors.value.ul =
            port_stats->rx_crc_errors;

        stats->rx_alignment_symbol_errors.value.ul =
            port_stats->rx_alignment_symbol_errors;
        stats->rx_in_range_errors.value.ul =
            port_stats->rx_in_range_errors;
        stats->rx_out_range_errors.value.ul =
            port_stats->rx_out_range_errors;
        stats->rx_frame_too_long.value.ul =
            port_stats->rx_frame_too_long;
        stats->rx_address_match_errors.value.ul =
            port_stats->rx_address_match_errors;

        stats->rx_pause_frames.value.ul =
            port_stats->rx_pause_frames;
        stats->rx_control_frames.value.ul =
            port_stats->rx_control_frames;
        stats->rx_ip_checksum_errs.value.ul =
            port_stats->rx_ip_checksum_errs;
        stats->rx_tcp_checksum_errs.value.ul =
            port_stats->rx_tcp_checksum_errs;
        stats->rx_udp_checksum_errs.value.ul =
            port_stats->rx_udp_checksum_errs;
        stats->rx_fifo_overflow.value.ul = port_stats->rx_fifo_overflow;
        stats->rx_input_fifo_overflow.value.ul =
            port_stats->rx_input_fifo_overflow;

        stats->tx_unicast_frames.value.ul =
            port_stats->tx_unicast_frames;
        stats->tx_multicast_frames.value.ul =
            port_stats->tx_multicast_frames;
        stats->tx_broadcast_frames.value.ul =
            port_stats->tx_broadcast_frames;
        stats->tx_pause_frames.value.ul =
            port_stats->tx_pause_frames;
        stats->tx_control_frames.value.ul =
            port_stats->tx_control_frames;


        stats->rx_drops_no_pbuf.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_no_pbuf;
        stats->rx_drops_no_txpb.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_no_txpb;
        stats->rx_drops_no_erx_descr.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_no_erx_descr;
        stats->rx_drops_no_tpre_descr.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_no_tpre_descr;
        stats->rx_drops_too_many_frags.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_too_many_frags;
        stats->rx_drops_invalid_ring.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_invalid_ring;
        stats->rx_drops_mtu.value.ul =
            dev->hw_stats->params.rsp.rx.rx_drops_mtu;

        stats->rx_dropped_too_small.value.ul =
            port_stats->rx_dropped_too_small;
        stats->rx_dropped_too_short.value.ul =
            port_stats->rx_dropped_too_short;
        stats->rx_dropped_header_too_small.value.ul =
            port_stats->rx_dropped_header_too_small;
        stats->rx_dropped_tcp_length.value.ul =
            port_stats->rx_dropped_tcp_length;
        stats->rx_dropped_runt.value.ul =
            port_stats->rx_dropped_runt;

        stats->rx_drops_no_fragments.value.ul =
            dev->hw_stats->params.rsp.err_rx.rx_drops_no_fragments[0];

        mutex_exit(&dev->dev_lock);
        return (DDI_SUCCESS);
} /* oce_update_stats */

/*
 * function to setup the kstat_t structure for the device and install it
 *
 * dev - software handle to the device
 *
 * return DDI_SUCCESS => success, failure otherwise
 */
int
oce_stat_init(struct oce_dev *dev)
{
        struct oce_stat *stats;
        uint32_t num_stats = sizeof (struct oce_stat) /
            sizeof (kstat_named_t);

        /* allocate the kstat */
        dev->oce_kstats = kstat_create(OCE_MOD_NAME, dev->dev_id, "stats",
            "net", KSTAT_TYPE_NAMED,
            num_stats, 0);
        if (dev->oce_kstats == NULL) {
                oce_log(dev, CE_NOTE, MOD_CONFIG,
                    "kstat creation failed: 0x%p",
                    (void *)dev->oce_kstats);
                return (DDI_FAILURE);
        }

        /* allocate the device copy of the stats */
        dev->stats_dbuf = oce_alloc_dma_buffer(dev,
            sizeof (struct mbx_get_nic_stats),
            NULL, DDI_DMA_CONSISTENT);
        if (dev->stats_dbuf == NULL) {
                oce_log(dev, CE_NOTE, MOD_CONFIG,
                    "Could not allocate stats_dbuf: %p",
                    (void *)dev->stats_dbuf);
                kstat_delete(dev->oce_kstats);
                return (DDI_FAILURE);
        }
        dev->hw_stats = (struct mbx_get_nic_stats *)DBUF_VA(dev->stats_dbuf);

        /* initialize the counters */
        stats = (struct oce_stat *)dev->oce_kstats->ks_data;
        kstat_named_init(&stats->rx_bytes_hi, "rx bytes msd", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_bytes_lo, "rx bytes lsd", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_frames, "rx frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_errors, "rx errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops, "rx drops", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->tx_bytes_hi, "tx bytes msd", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_bytes_lo, "tx bytes lsd", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->tx_frames, "tx frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_errors, "tx errors", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_unicast_frames,
            "rx unicast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_multicast_frames,
            "rx multicast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_broadcast_frames,
            "rx broadcast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_crc_errors,
            "rx crc errors", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_alignment_symbol_errors,
            "rx alignment symbol errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_in_range_errors,
            "rx in range errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_out_range_errors,
            "rx out range errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_frame_too_long,
            "rx frame too long", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_address_match_errors,
            "rx address match errors", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_pause_frames,
            "rx pause frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_control_frames,
            "rx control frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_ip_checksum_errs,
            "rx ip checksum errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_tcp_checksum_errs,
            "rx tcp checksum errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_udp_checksum_errs,
            "rx udp checksum errors", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_fifo_overflow,
            "rx fifo overflow", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_input_fifo_overflow,
            "rx input fifo overflow", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->tx_unicast_frames,
            "tx unicast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_multicast_frames,
            "tx multicast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_broadcast_frames,
            "tx broadcast frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_pause_frames,
            "tx pause frames", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->tx_control_frames,
            "tx control frames", KSTAT_DATA_ULONG);


        kstat_named_init(&stats->rx_drops_no_pbuf,
            "rx_drops_no_pbuf", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_no_txpb,
            "rx_drops_no_txpb", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_no_erx_descr,
            "rx_drops_no_erx_descr", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_no_tpre_descr,
            "rx_drops_no_tpre_descr", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_too_many_frags,
            "rx_drops_too_many_frags", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_invalid_ring,
            "rx_drops_invalid_ring", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_drops_mtu,
            "rx_drops_mtu", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_dropped_too_small,
            "rx_dropped_too_small", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_dropped_too_short,
            "rx_dropped_too_short", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_dropped_header_too_small,
            "rx_dropped_header_too_small", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_dropped_tcp_length,
            "rx_dropped_tcp_length", KSTAT_DATA_ULONG);
        kstat_named_init(&stats->rx_dropped_runt,
            "rx_dropped_runt", KSTAT_DATA_ULONG);

        kstat_named_init(&stats->rx_drops_no_fragments,
            "rx_drop_no_frag", KSTAT_DATA_ULONG);


        dev->oce_kstats->ks_update = oce_update_stats;
        dev->oce_kstats->ks_private = (void *)dev;
        kstat_install(dev->oce_kstats);

        return (DDI_SUCCESS);
} /* oce_stat_init */

/*
 * function to undo initialization done in oce_stat_init
 *
 * dev - software handle to the device
 *
 * return none
 */
void
oce_stat_fini(struct oce_dev *dev)
{
        oce_free_dma_buffer(dev, dev->stats_dbuf);
        dev->hw_stats = NULL;
        dev->stats_dbuf = NULL;
        kstat_delete(dev->oce_kstats);
        dev->oce_kstats = NULL;
} /* oce_stat_fini */

/*
 * GLDv3 entry for statistic query
 */
int
oce_m_stat(void *arg, uint_t stat, uint64_t *val)
{
        struct oce_dev *dev = arg;
        struct oce_stat *stats;
        struct rx_port_stats *port_stats;

        stats = (struct oce_stat *)dev->oce_kstats->ks_data;
        port_stats = &dev->hw_stats->params.rsp.rx.port[dev->port_id];

        mutex_enter(&dev->dev_lock);

        if (dev->suspended ||
            (dev->state & STATE_MAC_STOPPING) ||
            !(dev->state & STATE_MAC_STARTED)) {
                mutex_exit(&dev->dev_lock);
                return (EIO);
        }

        switch (stat) {
        case MAC_STAT_IFSPEED: {
                struct link_status link = {0};
                if (dev->link_speed < 0) {
                        (void) oce_get_link_status(dev, &link);
                        dev->link_speed = link.qos_link_speed ?
                            link.qos_link_speed * 10 :
                            pow10[link.mac_speed];
                }
                *val = dev->link_speed * 1000000ull;
        }
        break;

        case MAC_STAT_RBYTES:
                stats->rx_bytes_lo.value.ul = port_stats->rx_bytes_lsd;
                stats->rx_bytes_hi.value.ul = port_stats->rx_bytes_msd;
                *val = (uint64_t)stats->rx_bytes_hi.value.ul << 32 |
                    (uint64_t)stats->rx_bytes_lo.value.ul;
        break;

        case MAC_STAT_IPACKETS:
                stats->rx_frames.value.ul = port_stats->rx_total_frames;
                *val = stats->rx_frames.value.ul;
        break;

        case MAC_STAT_OBYTES:
                stats->tx_bytes_lo.value.ul = port_stats->tx_bytes_lsd;
                stats->tx_bytes_hi.value.ul = port_stats->tx_bytes_msd;
                *val = (uint64_t)stats->tx_bytes_hi.value.ul << 32 |
                    (uint64_t)stats->tx_bytes_lo.value.ul;
        break;

        case MAC_STAT_OPACKETS:
                stats->tx_frames.value.ul = port_stats->tx_unicast_frames +
                    port_stats->tx_multicast_frames +
                    port_stats->tx_broadcast_frames +
                    port_stats->tx_pause_frames +
                    port_stats->tx_control_frames;
                *val = stats->tx_frames.value.ul;
        break;

        case MAC_STAT_BRDCSTRCV:
                stats->rx_broadcast_frames.value.ul =
                    port_stats->rx_broadcast_frames;
                *val = stats->rx_broadcast_frames.value.ul;
        break;

        case MAC_STAT_MULTIRCV:
                stats->rx_multicast_frames.value.ul =
                    port_stats->rx_multicast_frames;
                *val = stats->rx_multicast_frames.value.ul;
        break;

        case MAC_STAT_MULTIXMT:
                stats->tx_multicast_frames.value.ul =
                    port_stats->tx_multicast_frames;
                *val = stats->tx_multicast_frames.value.ul;
        break;

        case MAC_STAT_BRDCSTXMT:
                stats->tx_broadcast_frames.value.ul =
                    port_stats->tx_broadcast_frames;
                *val = stats->tx_broadcast_frames.value.ul;
        break;

        case MAC_STAT_NORCVBUF:
                stats->rx_fifo_overflow.value.ul =
                    port_stats->rx_fifo_overflow;
                *val = stats->rx_fifo_overflow.value.ul;
        break;

        case MAC_STAT_IERRORS:
                stats->rx_errors.value.ul = port_stats->rx_crc_errors +
                    port_stats->rx_alignment_symbol_errors +
                    port_stats->rx_in_range_errors +
                    port_stats->rx_out_range_errors +
                    port_stats->rx_frame_too_long +
                    port_stats->rx_ip_checksum_errs +
                    port_stats->rx_tcp_checksum_errs +
                    port_stats->rx_udp_checksum_errs;
                *val = stats->rx_errors.value.ul;
        break;

        case MAC_STAT_NOXMTBUF:
                *val = dev->tx_noxmtbuf;
        break;

        case MAC_STAT_OERRORS:
                *val = stats->tx_errors.value.ul;
        break;

        case ETHER_STAT_LINK_DUPLEX:
                if (dev->state & STATE_MAC_STARTED)
                        *val = LINK_DUPLEX_FULL;
                else
                        *val = LINK_DUPLEX_UNKNOWN;
        break;

        case ETHER_STAT_ALIGN_ERRORS:
                stats->rx_alignment_symbol_errors.value.ul =
                    port_stats->rx_alignment_symbol_errors;
                *val = port_stats->rx_alignment_symbol_errors;
        break;

        case ETHER_STAT_FCS_ERRORS:
                stats->rx_crc_errors.value.ul =
                    port_stats->rx_crc_errors;
                *val = port_stats->rx_crc_errors;
        break;

        case ETHER_STAT_MACRCV_ERRORS:
                stats->rx_errors.value.ul = port_stats->rx_crc_errors +
                    port_stats->rx_alignment_symbol_errors +
                    port_stats->rx_in_range_errors +
                    port_stats->rx_out_range_errors +
                    port_stats->rx_frame_too_long +
                    port_stats->rx_ip_checksum_errs +
                    port_stats->rx_tcp_checksum_errs +
                    port_stats->rx_udp_checksum_errs;

                *val = stats->rx_errors.value.ul;
        break;

        case ETHER_STAT_MACXMT_ERRORS:
                *val = stats->tx_errors.value.ul;
        break;

        case ETHER_STAT_TOOLONG_ERRORS:
                stats->rx_frame_too_long.value.ul =
                    port_stats->rx_frame_too_long;
                *val = port_stats->rx_frame_too_long;
        break;

        case ETHER_STAT_CAP_PAUSE:
        case ETHER_STAT_LINK_PAUSE:
                if (dev->flow_control & OCE_FC_TX &&
                    dev->flow_control & OCE_FC_RX)
                        *val = LINK_FLOWCTRL_BI;
                else if (dev->flow_control == OCE_FC_TX)
                        *val = LINK_FLOWCTRL_TX;
                else if (dev->flow_control == OCE_FC_RX)
                        *val = LINK_FLOWCTRL_RX;
                else if (dev->flow_control == 0)
                        *val = LINK_FLOWCTRL_NONE;
        break;

        default:
                mutex_exit(&dev->dev_lock);
                return (ENOTSUP);
        }
        mutex_exit(&dev->dev_lock);
        return (0);
} /* oce_m_stat */