/*
* 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.
*/
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kstat.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <sys/dld.h>
#include <sys/ddi.h>
#include <libdllink.h>
#include <libdlflow.h>
#include <libdlstat.h>
#include <libdlaggr.h>
struct flowlist {
char flowname[MAXFLOWNAMELEN];
char linkname[MAXLINKNAMELEN];
datalink_id_t linkid;
int fd;
uint64_t ifspeed;
boolean_t first;
boolean_t display;
pktsum_t prevstats;
pktsum_t diffstats;
};
pktsum_t totalstats;
struct flowlist *stattable = NULL;
#define STATGROWSIZE 16
/* Exported functions */
/*
* dladm_kstat_lookup() is a modified version of kstat_lookup which
* adds the class as a selector.
*/
kstat_t *
dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
const char *name, const char *class)
{
kstat_t *ksp = NULL;
for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
(instance == -1 || ksp->ks_instance == instance) &&
(name == NULL || strcmp(ksp->ks_name, name) == 0) &&
(class == NULL || strcmp(ksp->ks_class, class) == 0))
return (ksp);
}
errno = ENOENT;
return (NULL);
}
/*
* dladm_get_stats() populates the supplied pktsum_t structure with
* the input and output packet and byte kstats from the kstat_t
* found with dladm_kstat_lookup.
*/
void
dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
{
if (kstat_read(kcp, ksp, NULL) == -1)
return;
stats->snaptime = gethrtime();
if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
&stats->ipackets) < 0) {
if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
&stats->ipackets) < 0)
return;
}
if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
&stats->opackets) < 0) {
if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
&stats->opackets) < 0)
return;
}
if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
&stats->rbytes) < 0) {
if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
&stats->rbytes) < 0)
return;
}
if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
&stats->obytes) < 0) {
if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
&stats->obytes) < 0)
return;
}
if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
&stats->ierrors) < 0) {
if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
&stats->ierrors) < 0)
return;
}
if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
&stats->oerrors) < 0) {
if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
&stats->oerrors) < 0)
return;
}
}
int
dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
{
kstat_named_t *knp;
if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
return (-1);
if (knp->data_type != type)
return (-1);
switch (type) {
case KSTAT_DATA_UINT64:
*(uint64_t *)buf = knp->value.ui64;
break;
case KSTAT_DATA_UINT32:
*(uint32_t *)buf = knp->value.ui32;
break;
default:
return (-1);
}
return (0);
}
dladm_status_t
dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
const char *name, uint8_t type, void *val)
{
kstat_ctl_t *kcp;
char module[DLPI_LINKNAME_MAX];
uint_t instance;
char link[DLPI_LINKNAME_MAX];
dladm_status_t status;
uint32_t flags, media;
kstat_t *ksp;
dladm_phys_attr_t dpap;
if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
&media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
return (status);
if (media != DL_ETHER)
return (DLADM_STATUS_LINKINVAL);
status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
if (status != DLADM_STATUS_OK)
return (status);
status = dladm_parselink(dpap.dp_dev, module, &instance);
if (status != DLADM_STATUS_OK)
return (status);
if ((kcp = kstat_open()) == NULL) {
warn("kstat_open operation failed");
return (-1);
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
(ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
goto bail;
if (kstat_read(kcp, ksp, NULL) == -1)
goto bail;
if (dladm_kstat_value(ksp, name, type, val) < 0)
goto bail;
(void) kstat_close(kcp);
return (DLADM_STATUS_OK);
bail:
(void) kstat_close(kcp);
return (dladm_errno2status(errno));
}
/* Compute sum of 2 pktsums (s1 = s2 + s3) */
void
dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
{
s1->rbytes = s2->rbytes + s3->rbytes;
s1->ipackets = s2->ipackets + s3->ipackets;
s1->ierrors = s2->ierrors + s3->ierrors;
s1->obytes = s2->obytes + s3->obytes;
s1->opackets = s2->opackets + s3->opackets;
s1->oerrors = s2->oerrors + s3->oerrors;
s1->snaptime = s2->snaptime;
}
#define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
/* Compute differences between 2 pktsums (s1 = s2 - s3) */
void
dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
{
s1->rbytes = DIFF_STAT(s2->rbytes, s3->rbytes);
s1->ipackets = DIFF_STAT(s2->ipackets, s3->ipackets);
s1->ierrors = DIFF_STAT(s2->ierrors, s3->ierrors);
s1->obytes = DIFF_STAT(s2->obytes, s3->obytes);
s1->opackets = DIFF_STAT(s2->opackets, s3->opackets);
s1->oerrors = DIFF_STAT(s2->oerrors, s3->oerrors);
s1->snaptime = DIFF_STAT(s2->snaptime, s3->snaptime);
}
#define DLSTAT_MAC_RX_SWLANE "mac_rx_swlane"
#define DLSTAT_MAC_RX_HWLANE "mac_rx_hwlane"
#define DLSTAT_MAC_TX_SWLANE "mac_tx_swlane"
#define DLSTAT_MAC_TX_HWLANE "mac_tx_hwlane"
#define DLSTAT_MAC_MISC_STAT "mac_misc_stat"
#define DLSTAT_MAC_RX_RING "mac_rx_ring"
#define DLSTAT_MAC_TX_RING "mac_tx_ring"
#define DLSTAT_MAC_FANOUT "mac_rx_swlane0_fanout"
typedef struct {
const char *si_name;
uint_t si_offset;
} stat_info_t;
#define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
/* Definitions for rx lane stats */
#define RL_OFF(f) (offsetof(rx_lane_stat_t, f))
static stat_info_t rx_hwlane_stats_list[] = {
{"ipackets", RL_OFF(rl_ipackets)},
{"rbytes", RL_OFF(rl_rbytes)},
{"intrs", RL_OFF(rl_intrs)},
{"intrbytes", RL_OFF(rl_intrbytes)},
{"polls", RL_OFF(rl_polls)},
{"pollbytes", RL_OFF(rl_pollbytes)},
{"rxsdrops", RL_OFF(rl_sdrops)},
{"chainunder10", RL_OFF(rl_chl10)},
{"chain10to50", RL_OFF(rl_ch10_50)},
{"chainover50", RL_OFF(rl_chg50)}
};
#define RX_HWLANE_STAT_SIZE A_CNT(rx_hwlane_stats_list)
static stat_info_t rx_swlane_stats_list[] = {
{"ipackets", RL_OFF(rl_ipackets)},
{"rbytes", RL_OFF(rl_rbytes)},
{"local", RL_OFF(rl_lclpackets)},
{"localbytes", RL_OFF(rl_lclbytes)},
{"intrs", RL_OFF(rl_intrs)},
{"intrbytes", RL_OFF(rl_intrbytes)},
{"rxsdrops", RL_OFF(rl_sdrops)}
};
#define RX_SWLANE_STAT_SIZE A_CNT(rx_swlane_stats_list)
static stat_info_t rx_lane_stats_list[] = {
{"ipackets", RL_OFF(rl_ipackets)},
{"rbytes", RL_OFF(rl_rbytes)},
{"local", RL_OFF(rl_lclpackets)},
{"localbytes", RL_OFF(rl_lclbytes)},
{"intrs", RL_OFF(rl_intrs)},
{"intrbytes", RL_OFF(rl_intrbytes)},
{"polls", RL_OFF(rl_polls)},
{"rxsdrops", RL_OFF(rl_sdrops)},
{"pollbytes", RL_OFF(rl_pollbytes)},
{"chainunder10", RL_OFF(rl_chl10)},
{"chain10to50", RL_OFF(rl_ch10_50)},
{"chainover50", RL_OFF(rl_chg50)}
};
#define RX_LANE_STAT_SIZE A_CNT(rx_lane_stats_list)
/* Definitions for tx lane stats */
#define TL_OFF(f) (offsetof(tx_lane_stat_t, f))
static stat_info_t tx_lane_stats_list[] = {
{"opackets", TL_OFF(tl_opackets)},
{"obytes", TL_OFF(tl_obytes)},
{"blockcnt", TL_OFF(tl_blockcnt)},
{"unblockcnt", TL_OFF(tl_unblockcnt)},
{"txsdrops", TL_OFF(tl_sdrops)}
};
#define TX_LANE_STAT_SIZE A_CNT(tx_lane_stats_list)
/* Definitions for tx/rx misc stats */
#define M_OFF(f) (offsetof(misc_stat_t, f))
static stat_info_t misc_stats_list[] = {
{"multircv", M_OFF(ms_multircv)},
{"brdcstrcv", M_OFF(ms_brdcstrcv)},
{"multixmt", M_OFF(ms_multixmt)},
{"brdcstxmt", M_OFF(ms_brdcstxmt)},
{"multircvbytes", M_OFF(ms_multircvbytes)},
{"brdcstrcvbytes", M_OFF(ms_brdcstrcvbytes)},
{"multixmtbytes", M_OFF(ms_multixmtbytes)},
{"brdcstxmtbytes", M_OFF(ms_brdcstxmtbytes)},
{"txerrors", M_OFF(ms_txerrors)},
{"macspoofed", M_OFF(ms_macspoofed)},
{"ipspoofed", M_OFF(ms_ipspoofed)},
{"dhcpspoofed", M_OFF(ms_dhcpspoofed)},
{"restricted", M_OFF(ms_restricted)},
{"ipackets", M_OFF(ms_ipackets)},
{"rbytes", M_OFF(ms_rbytes)},
{"local", M_OFF(ms_local)},
{"localbytes", M_OFF(ms_localbytes)},
{"intrs", M_OFF(ms_intrs)},
{"intrbytes", M_OFF(ms_intrbytes)},
{"polls", M_OFF(ms_polls)},
{"pollbytes", M_OFF(ms_pollbytes)},
{"rxsdrops", M_OFF(ms_rxsdrops)},
{"chainunder10", M_OFF(ms_chainunder10)},
{"chain10to50", M_OFF(ms_chain10to50)},
{"chainover50", M_OFF(ms_chainover50)},
{"obytes", M_OFF(ms_obytes)},
{"opackets", M_OFF(ms_opackets)},
{"blockcnt", M_OFF(ms_blockcnt)},
{"unblockcnt", M_OFF(ms_unblockcnt)},
{"txsdrops", M_OFF(ms_txsdrops)}
};
#define MISC_STAT_SIZE A_CNT(misc_stats_list)
/* Definitions for rx ring stats */
#define R_OFF(f) (offsetof(ring_stat_t, f))
static stat_info_t rx_ring_stats_list[] = {
{"ipackets", R_OFF(r_packets)},
{"rbytes", R_OFF(r_bytes)}
};
#define RX_RING_STAT_SIZE A_CNT(rx_ring_stats_list)
/* Definitions for tx ring stats */
static stat_info_t tx_ring_stats_list[] = {
{"opackets", R_OFF(r_packets)},
{"obytes", R_OFF(r_bytes)}
};
#define TX_RING_STAT_SIZE A_CNT(tx_ring_stats_list)
/* Definitions for fanout stats */
#define F_OFF(f) (offsetof(fanout_stat_t, f))
static stat_info_t fanout_stats_list[] = {
{"ipackets", F_OFF(f_ipackets)},
{"rbytes", F_OFF(f_rbytes)},
};
#define FANOUT_STAT_SIZE A_CNT(fanout_stats_list)
/* Definitions for total stats */
#define T_OFF(f) (offsetof(total_stat_t, f))
static stat_info_t total_stats_list[] = {
{"ipackets", T_OFF(ts_ipackets)},
{"rbytes", T_OFF(ts_rbytes)},
{"opackets", T_OFF(ts_opackets)},
{"obytes", T_OFF(ts_obytes)}
};
#define TOTAL_STAT_SIZE A_CNT(total_stats_list)
/* Definitions for aggr stats */
#define AP_OFF(f) (offsetof(aggr_port_stat_t, f))
static stat_info_t aggr_port_stats_list[] = {
{"ipackets64", AP_OFF(ap_ipackets)},
{"rbytes64", AP_OFF(ap_rbytes)},
{"opackets64", AP_OFF(ap_opackets)},
{"obytes64", AP_OFF(ap_obytes)}
};
#define AGGR_PORT_STAT_SIZE A_CNT(aggr_port_stats_list)
/* Definitions for flow stats */
#define FL_OFF(f) (offsetof(flow_stat_t, f))
static stat_info_t flow_stats_list[] = {
{"ipackets", FL_OFF(fl_ipackets)},
{"rbytes", FL_OFF(fl_rbytes)},
{"opackets", FL_OFF(fl_opackets)},
{"obytes", FL_OFF(fl_obytes)}
};
#define FLOW_STAT_SIZE A_CNT(flow_stats_list)
/* Rx lane specific functions */
void * dlstat_rx_lane_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_rx_lane_match(void *, void *);
static void * i_dlstat_rx_lane_stat_entry_diff(void *, void *);
/* Tx lane specific functions */
void * dlstat_tx_lane_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_tx_lane_match(void *, void *);
static void * i_dlstat_tx_lane_stat_entry_diff(void *, void *);
/* Rx lane total specific functions */
void * dlstat_rx_lane_total_stats(dladm_handle_t,
datalink_id_t);
/* Tx lane total specific functions */
void * dlstat_tx_lane_total_stats(dladm_handle_t,
datalink_id_t);
/* Fanout specific functions */
void * dlstat_fanout_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_fanout_match(void *, void *);
static void * i_dlstat_fanout_stat_entry_diff(void *, void *);
/* Rx ring specific functions */
void * dlstat_rx_ring_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_rx_ring_match(void *, void *);
static void * i_dlstat_rx_ring_stat_entry_diff(void *, void *);
/* Tx ring specific functions */
void * dlstat_tx_ring_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_tx_ring_match(void *, void *);
static void * i_dlstat_tx_ring_stat_entry_diff(void *, void *);
/* Rx ring total specific functions */
void * dlstat_rx_ring_total_stats(dladm_handle_t,
datalink_id_t);
/* Tx ring total specific functions */
void * dlstat_tx_ring_total_stats(dladm_handle_t,
datalink_id_t);
/* Summary specific functions */
void * dlstat_total_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_total_match(void *, void *);
static void * i_dlstat_total_stat_entry_diff(void *, void *);
/* Aggr port specific functions */
void * dlstat_aggr_port_stats(dladm_handle_t, datalink_id_t);
static boolean_t i_dlstat_aggr_port_match(void *, void *);
static void * i_dlstat_aggr_port_stat_entry_diff(void *, void *);
/* Misc stat specific functions */
void * dlstat_misc_stats(dladm_handle_t, datalink_id_t);
typedef void * dladm_stat_query_t(dladm_handle_t, datalink_id_t);
typedef boolean_t dladm_stat_match_t(void *, void *);
typedef void * dladm_stat_diff_t(void *, void *);
typedef struct dladm_stat_desc_s {
dladm_stat_type_t ds_stattype;
dladm_stat_query_t *ds_querystat;
dladm_stat_match_t *ds_matchstat;
dladm_stat_diff_t *ds_diffstat;
uint_t ds_offset;
stat_info_t *ds_statlist;
uint_t ds_statsize;
} dladm_stat_desc_t;
/*
* dladm_stat_table has one entry for each supported stat. ds_querystat returns
* a chain of 'stat entries' for the queried stat.
* Each stat entry has set of identifiers (ids) and an object containing actual
* stat values. These stat entry objects are chained together in a linked list
* of datatype dladm_stat_chain_t. Head of this list is returned to the caller
* of dladm_link_stat_query.
*
* One node in the chain is shown below:
*
* -------------------------
* | dc_statentry |
* | -------------- |
* | | ids | |
* | -------------- |
* | | stat fields | |
* | -------------- |
* -------------------------
* | dc_next ---------|------> to next stat entry
* -------------------------
*
* In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
* object of type rx_lane_stat_entry_t.
*
* dladm_link_stat_query_all returns similar chain. However, instead of storing
* stat fields as raw numbers, it stores those as chain of <name, value> pairs.
* The resulting structure is depicted below:
*
* -------------------------
* | dc_statentry |
* | -------------- | ---------------
* | | nv_header | | | name, val |
* | -------------- | ---------------
* | | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair
* | -------------- | ---------------
* -------------------------
* | dc_next ---------|------> to next stat entry
* -------------------------
*/
static dladm_stat_desc_t dladm_stat_table[] = {
{ DLADM_STAT_RX_LANE, dlstat_rx_lane_stats,
i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
offsetof(rx_lane_stat_entry_t, rle_stats),
rx_lane_stats_list, RX_LANE_STAT_SIZE},
{ DLADM_STAT_TX_LANE, dlstat_tx_lane_stats,
i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
offsetof(tx_lane_stat_entry_t, tle_stats),
tx_lane_stats_list, TX_LANE_STAT_SIZE},
{ DLADM_STAT_RX_LANE_TOTAL, dlstat_rx_lane_total_stats,
i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
offsetof(rx_lane_stat_entry_t, rle_stats),
rx_lane_stats_list, RX_LANE_STAT_SIZE},
{ DLADM_STAT_TX_LANE_TOTAL, dlstat_tx_lane_total_stats,
i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
offsetof(tx_lane_stat_entry_t, tle_stats),
tx_lane_stats_list, TX_LANE_STAT_SIZE},
{ DLADM_STAT_RX_LANE_FOUT, dlstat_fanout_stats,
i_dlstat_fanout_match, i_dlstat_fanout_stat_entry_diff,
offsetof(fanout_stat_entry_t, fe_stats),
fanout_stats_list, FANOUT_STAT_SIZE},
{ DLADM_STAT_RX_RING, dlstat_rx_ring_stats,
i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
offsetof(ring_stat_entry_t, re_stats),
rx_ring_stats_list, RX_RING_STAT_SIZE},
{ DLADM_STAT_TX_RING, dlstat_tx_ring_stats,
i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
offsetof(ring_stat_entry_t, re_stats),
tx_ring_stats_list, TX_RING_STAT_SIZE},
{ DLADM_STAT_RX_RING_TOTAL, dlstat_rx_ring_total_stats,
i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
offsetof(ring_stat_entry_t, re_stats),
rx_ring_stats_list, RX_RING_STAT_SIZE},
{ DLADM_STAT_TX_RING_TOTAL, dlstat_tx_ring_total_stats,
i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
offsetof(ring_stat_entry_t, re_stats),
tx_ring_stats_list, TX_RING_STAT_SIZE},
{ DLADM_STAT_TOTAL, dlstat_total_stats,
i_dlstat_total_match, i_dlstat_total_stat_entry_diff,
offsetof(total_stat_entry_t, tse_stats),
total_stats_list, TOTAL_STAT_SIZE},
{ DLADM_STAT_AGGR_PORT, dlstat_aggr_port_stats,
i_dlstat_aggr_port_match, i_dlstat_aggr_port_stat_entry_diff,
offsetof(aggr_port_stat_entry_t, ape_stats),
aggr_port_stats_list, AGGR_PORT_STAT_SIZE},
/*
* We don't support -i <interval> query with misc stats. Several table fields
* are left uninitialized thus.
*/
{ DLADM_STAT_MISC, dlstat_misc_stats,
NULL, NULL,
0,
misc_stats_list, MISC_STAT_SIZE}
};
/* Internal functions */
static void *
dlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
{
return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2));
}
static boolean_t
dlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
{
return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2));
}
/* Diff between two stats */
static void
i_dlstat_diff_stats(void *diff, void *op1, void *op2,
stat_info_t stats_list[], uint_t size)
{
int i;
for (i = 0; i < size; i++) {
uint64_t *op1_val = (void *)
((uchar_t *)op1 + stats_list[i].si_offset);
uint64_t *op2_val = (void *)
((uchar_t *)op2 + stats_list[i].si_offset);
uint64_t *diff_val = (void *)
((uchar_t *)diff + stats_list[i].si_offset);
*diff_val = DIFF_STAT(*op1_val, *op2_val);
}
}
/*
* Perform diff = s1 - s2, where diff, s1, s2 are structure objects of same
* datatype. slist is list of offsets of the fields within the structure.
*/
#define DLSTAT_DIFF_STAT(s1, s2, diff, f, slist, sz) { \
if (s2 == NULL) { \
bcopy(&s1->f, &diff->f, sizeof (s1->f)); \
} else { \
i_dlstat_diff_stats(&diff->f, &s1->f, \
&s2->f, slist, sz); \
} \
}
/* Sum two stats */
static void
i_dlstat_sum_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 = (void *)
((uchar_t *)op1 + stats_list[i].si_offset);
uint64_t *op2_val = (void *)
((uchar_t *)op2 + stats_list[i].si_offset);
uint64_t *sum_val = (void *)
((uchar_t *)sum + stats_list[i].si_offset);
*sum_val = *op1_val + *op2_val;
}
}
/* Look up kstat value */
static void
i_dlstat_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, void *stats,
stat_info_t stats_list[], uint_t size)
{
int i;
if (kstat_read(kcp, ksp, NULL) == -1)
return;
for (i = 0; i < size; i++) {
uint64_t *val = (void *)
((uchar_t *)stats + stats_list[i].si_offset);
if (dladm_kstat_value(ksp, stats_list[i].si_name,
KSTAT_DATA_UINT64, val) < 0)
return;
}
}
/* Append linked list list1 to linked list list2 and return resulting list */
static dladm_stat_chain_t *
i_dlstat_join_lists(dladm_stat_chain_t *list1, dladm_stat_chain_t *list2)
{
dladm_stat_chain_t *curr;
if (list1 == NULL)
return (list2);
/* list1 has at least one element, find last element in list1 */
curr = list1;
while (curr->dc_next != NULL)
curr = curr->dc_next;
curr->dc_next = list2;
return (list1);
}
uint_t default_idlist[] = {0};
uint_t default_idlist_size = 1;
typedef enum {
DLSTAT_RX_RING_IDLIST,
DLSTAT_TX_RING_IDLIST,
DLSTAT_RX_HWLANE_IDLIST,
DLSTAT_TX_HWLANE_IDLIST,
DLSTAT_FANOUT_IDLIST
} dlstat_idlist_type_t;
void
dladm_sort_index_list(uint_t idlist[], uint_t size)
{
int i, j;
for (j = 1; j < size; j++) {
int key = idlist[j];
for (i = j - 1; (i >= 0) && (idlist[i] > key); i--)
idlist[i + 1] = idlist[i];
idlist[i + 1] = key;
}
}
/* Support for legacy drivers */
void
i_query_legacy_stats(const char *linkname, pktsum_t *stats)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
bzero(stats, sizeof (*stats));
if ((kcp = kstat_open()) == NULL)
return;
ksp = dladm_kstat_lookup(kcp, "link", 0, linkname, NULL);
if (ksp != NULL)
dladm_get_stats(kcp, ksp, stats);
(void) kstat_close(kcp);
}
void *
i_dlstat_legacy_rx_lane_stats(const char *linkname)
{
dladm_stat_chain_t *head = NULL;
pktsum_t stats;
rx_lane_stat_entry_t *rx_lane_stat_entry;
bzero(&stats, sizeof (pktsum_t));
/* Query for dls stats */
i_query_legacy_stats(linkname, &stats);
/* Convert to desired data type */
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
rx_lane_stat_entry->rle_id = L_SWLANE;
rx_lane_stat_entry->rle_stats.rl_ipackets = stats.ipackets;
rx_lane_stat_entry->rle_stats.rl_intrs = stats.ipackets;
rx_lane_stat_entry->rle_stats.rl_rbytes = stats.rbytes;
/* Allocate memory for wrapper */
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(rx_lane_stat_entry);
goto done;
}
head->dc_statentry = rx_lane_stat_entry;
head->dc_next = NULL;
done:
return (head);
}
void *
i_dlstat_legacy_tx_lane_stats(const char *linkname)
{
dladm_stat_chain_t *head = NULL;
pktsum_t stats;
tx_lane_stat_entry_t *tx_lane_stat_entry;
bzero(&stats, sizeof (pktsum_t));
/* Query for dls stats */
i_query_legacy_stats(linkname, &stats);
/* Convert to desired data type */
tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
tx_lane_stat_entry->tle_id = L_SWLANE;
tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets;
tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes;
/* Allocate memory for wrapper */
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(tx_lane_stat_entry);
goto done;
}
head->dc_statentry = tx_lane_stat_entry;
head->dc_next = NULL;
done:
return (head);
}
/*
* Ideally, we would want an ioctl to return list of ring-ids (or lane-ids)
* for a given data-link (or mac client). We could then query for specific
* kstats based on these ring-ids (lane-ids).
* Ring-ids (or lane-ids) could be returned like any other link properties
* queried by dladm show-linkprop. However, non-global zones do not have
* access to this information today.
* We thus opt for an implementation that relies heavily on kstat internals:
* i_dlstat_*search routines and i_dlstat_get_idlist.
*/
/* rx hwlane specific */
static boolean_t
i_dlstat_rx_hwlane_search(kstat_t *ksp)
{
return (ksp->ks_instance == 0 &&
strstr(ksp->ks_name, "mac_rx") != 0 &&
strstr(ksp->ks_name, "hwlane") != 0 &&
strstr(ksp->ks_name, "fanout") == 0 &&
strcmp(ksp->ks_class, "net") == 0);
}
/* tx hwlane specific */
static boolean_t
i_dlstat_tx_hwlane_search(kstat_t *ksp)
{
return (ksp->ks_instance == 0 &&
strstr(ksp->ks_name, "mac_tx") != 0 &&
strstr(ksp->ks_name, "hwlane") != 0 &&
strcmp(ksp->ks_class, "net") == 0);
}
/* rx fanout specific */
static boolean_t
i_dlstat_fanout_search(kstat_t *ksp)
{
return (ksp->ks_instance == 0 &&
strstr(ksp->ks_name, "mac_rx") != 0 &&
strstr(ksp->ks_name, "swlane") != 0 &&
strstr(ksp->ks_name, "fanout") != 0 &&
strcmp(ksp->ks_class, "net") == 0);
}
/* rx ring specific */
static boolean_t
i_dlstat_rx_ring_search(kstat_t *ksp)
{
return (ksp->ks_instance == 0 &&
strstr(ksp->ks_name, "mac_rx") != 0 &&
strstr(ksp->ks_name, "ring") != 0 &&
strcmp(ksp->ks_class, "net") == 0);
}
/* tx ring specific */
static boolean_t
i_dlstat_tx_ring_search(kstat_t *ksp)
{
return (ksp->ks_instance == 0) &&
strstr(ksp->ks_name, "mac_tx") != 0 &&
strstr(ksp->ks_name, "ring") != 0 &&
strcmp(ksp->ks_class, "net") == 0;
}
typedef boolean_t dladm_search_kstat_t(kstat_t *);
typedef struct dladm_extract_idlist_s {
dlstat_idlist_type_t di_type;
char *di_prefix;
dladm_search_kstat_t *di_searchkstat;
} dladm_extract_idlist_t;
static dladm_extract_idlist_t dladm_extract_idlist[] = {
{ DLSTAT_RX_RING_IDLIST, DLSTAT_MAC_RX_RING,
i_dlstat_rx_ring_search},
{ DLSTAT_TX_RING_IDLIST, DLSTAT_MAC_TX_RING,
i_dlstat_tx_ring_search},
{ DLSTAT_RX_HWLANE_IDLIST, DLSTAT_MAC_RX_HWLANE,
i_dlstat_rx_hwlane_search},
{ DLSTAT_TX_HWLANE_IDLIST, DLSTAT_MAC_TX_HWLANE,
i_dlstat_tx_hwlane_search},
{ DLSTAT_FANOUT_IDLIST, DLSTAT_MAC_FANOUT,
i_dlstat_fanout_search}
};
static void
i_dlstat_get_idlist(const char *modname, dlstat_idlist_type_t idlist_type,
uint_t idlist[], uint_t *size)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
char *prefix;
int prefixlen;
boolean_t (*fptr_searchkstat)(kstat_t *);
*size = 0;
if ((kcp = kstat_open()) == NULL) {
warn("kstat_open operation failed");
goto done;
}
prefix = dladm_extract_idlist[idlist_type].di_prefix;
fptr_searchkstat = dladm_extract_idlist[idlist_type].di_searchkstat;
prefixlen = strlen(prefix);
for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
if ((strcmp(ksp->ks_module, modname) == 0) &&
fptr_searchkstat(ksp)) {
idlist[(*size)++] = atoi(&ksp->ks_name[prefixlen]);
}
}
dladm_sort_index_list(idlist, *size);
done:
(void) kstat_close(kcp);
}
static dladm_stat_chain_t *
i_dlstat_query_stats(const char *modname, const char *prefix,
uint_t idlist[], uint_t idlist_size,
void * (*fn)(kstat_ctl_t *, kstat_t *, int))
{
kstat_ctl_t *kcp;
kstat_t *ksp;
char statname[MAXLINKNAMELEN];
int i = 0;
dladm_stat_chain_t *head = NULL, *prev = NULL;
dladm_stat_chain_t *curr;
if ((kcp = kstat_open()) == NULL) {
warn("kstat_open operation failed");
return (NULL);
}
for (i = 0; i < idlist_size; i++) {
uint_t index = idlist[i];
(void) snprintf(statname, sizeof (statname), "%s%d", prefix,
index);
ksp = dladm_kstat_lookup(kcp, modname, 0, statname, NULL);
if (ksp == NULL)
continue;
curr = malloc(sizeof (dladm_stat_chain_t));
if (curr == NULL)
break;
curr->dc_statentry = fn(kcp, ksp, index);
if (curr->dc_statentry == NULL) {
free(curr);
break;
}
(void) strlcpy(curr->dc_statheader, statname,
sizeof (curr->dc_statheader));
curr->dc_next = NULL;
if (head == NULL) /* First node */
head = curr;
else
prev->dc_next = curr;
prev = curr;
}
done:
(void) kstat_close(kcp);
return (head);
}
static misc_stat_entry_t *
i_dlstat_misc_stats(const char *linkname)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
misc_stat_entry_t *misc_stat_entry = NULL;
if ((kcp = kstat_open()) == NULL)
return (NULL);
ksp = dladm_kstat_lookup(kcp, linkname, 0, DLSTAT_MAC_MISC_STAT, NULL);
if (ksp == NULL)
goto done;
misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t));
if (misc_stat_entry == NULL)
goto done;
i_dlstat_get_stats(kcp, ksp, &misc_stat_entry->mse_stats,
misc_stats_list, MISC_STAT_SIZE);
done:
(void) kstat_close(kcp);
return (misc_stat_entry);
}
/* Rx lane statistic specific functions */
static boolean_t
i_dlstat_rx_lane_match(void *arg1, void *arg2)
{
rx_lane_stat_entry_t *s1 = arg1;
rx_lane_stat_entry_t *s2 = arg2;
return (s1->rle_index == s2->rle_index &&
s1->rle_id == s2->rle_id);
}
static void *
i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
{
rx_lane_stat_entry_t *s1 = arg1;
rx_lane_stat_entry_t *s2 = arg2;
rx_lane_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (rx_lane_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->rle_index = s1->rle_index;
diff_entry->rle_id = s1->rle_id;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
RX_LANE_STAT_SIZE);
done:
return (diff_entry);
}
static void *
i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
rx_lane_stat_entry_t *rx_lane_stat_entry;
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = i;
rx_lane_stat_entry->rle_id = L_HWLANE;
i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
done:
return (rx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
rx_lane_stat_entry_t *rx_lane_stat_entry;
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
rx_lane_stat_entry->rle_id = L_SWLANE;
i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
rx_lane_stat_entry->rle_stats.rl_ipackets =
rx_lane_stat_entry->rle_stats.rl_intrs;
rx_lane_stat_entry->rle_stats.rl_rbytes =
rx_lane_stat_entry->rle_stats.rl_intrbytes;
done:
return (rx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
rx_lane_stat_entry_t *local_stat_entry;
rx_lane_stat_entry_t *rx_lane_stat_entry;
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (local_stat_entry == NULL)
goto done;
local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
local_stat_entry->rle_id = L_LOCAL;
i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
local_stat_entry->rle_stats.rl_ipackets =
rx_lane_stat_entry->rle_stats.rl_lclpackets;
local_stat_entry->rle_stats.rl_rbytes =
rx_lane_stat_entry->rle_stats.rl_lclbytes;
done:
free(rx_lane_stat_entry);
return (local_stat_entry);
}
static dladm_stat_chain_t *
i_dlstat_rx_local_stats(const char *linkname)
{
dladm_stat_chain_t *local_stats = NULL;
local_stats = i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE,
default_idlist, default_idlist_size,
i_dlstat_rx_local_retrieve_stat);
if (local_stats != NULL) {
(void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
sizeof (local_stats->dc_statheader));
}
return (local_stats);
}
static dladm_stat_chain_t *
i_dlstat_rx_bcast_stats(const char *linkname)
{
misc_stat_entry_t *misc_stat_entry;
dladm_stat_chain_t *head = NULL;
rx_lane_stat_entry_t *rx_lane_stat_entry;
misc_stat_entry = i_dlstat_misc_stats(linkname);
if (misc_stat_entry == NULL)
goto done;
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
rx_lane_stat_entry->rle_id = L_BCAST;
rx_lane_stat_entry->rle_stats.rl_ipackets =
misc_stat_entry->mse_stats.ms_brdcstrcv +
misc_stat_entry->mse_stats.ms_multircv;
rx_lane_stat_entry->rle_stats.rl_intrs =
misc_stat_entry->mse_stats.ms_brdcstrcv +
misc_stat_entry->mse_stats.ms_multircv;
rx_lane_stat_entry->rle_stats.rl_rbytes =
misc_stat_entry->mse_stats.ms_brdcstrcvbytes +
misc_stat_entry->mse_stats.ms_multircvbytes;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(rx_lane_stat_entry);
goto done;
}
head->dc_statentry = rx_lane_stat_entry;
head->dc_next = NULL;
free(misc_stat_entry);
done:
return (head);
}
static dladm_stat_chain_t *
i_dlstat_rx_defunctlane_stats(const char *linkname)
{
misc_stat_entry_t *misc_stat_entry;
dladm_stat_chain_t *head = NULL;
rx_lane_stat_entry_t *rx_lane_stat_entry;
misc_stat_entry = i_dlstat_misc_stats(linkname);
if (misc_stat_entry == NULL)
goto done;
rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
rx_lane_stat_entry->rle_id = L_DFNCT;
rx_lane_stat_entry->rle_stats.rl_ipackets =
misc_stat_entry->mse_stats.ms_ipackets;
rx_lane_stat_entry->rle_stats.rl_rbytes =
misc_stat_entry->mse_stats.ms_rbytes;
rx_lane_stat_entry->rle_stats.rl_intrs =
misc_stat_entry->mse_stats.ms_intrs;
rx_lane_stat_entry->rle_stats.rl_polls =
misc_stat_entry->mse_stats.ms_polls;
rx_lane_stat_entry->rle_stats.rl_sdrops =
misc_stat_entry->mse_stats.ms_rxsdrops;
rx_lane_stat_entry->rle_stats.rl_chl10 =
misc_stat_entry->mse_stats.ms_chainunder10;
rx_lane_stat_entry->rle_stats.rl_ch10_50 =
misc_stat_entry->mse_stats.ms_chain10to50;
rx_lane_stat_entry->rle_stats.rl_chg50 =
misc_stat_entry->mse_stats.ms_chainover50;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(rx_lane_stat_entry);
goto done;
}
head->dc_statentry = rx_lane_stat_entry;
head->dc_next = NULL;
done:
return (head);
}
static dladm_stat_chain_t *
i_dlstat_rx_hwlane_stats(const char *linkname)
{
uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
uint_t rx_hwlane_idlist_size;
i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
rx_hwlane_idlist, &rx_hwlane_idlist_size);
return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_HWLANE,
rx_hwlane_idlist, rx_hwlane_idlist_size,
i_dlstat_rx_hwlane_retrieve_stat));
}
/*ARGSUSED*/
static dladm_stat_chain_t *
i_dlstat_rx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
const char *linkname)
{
return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE,
default_idlist, default_idlist_size,
i_dlstat_rx_swlane_retrieve_stat));
}
void *
dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *head = NULL;
dladm_stat_chain_t *local_stats = NULL;
dladm_stat_chain_t *bcast_stats = NULL;
dladm_stat_chain_t *defunctlane_stats = NULL;
dladm_stat_chain_t *lane_stats = NULL;
char linkname[MAXLINKNAMELEN];
boolean_t is_legacy_driver;
if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
/* Check if it is legacy driver */
if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
"_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
goto done;
}
if (is_legacy_driver) {
head = i_dlstat_legacy_rx_lane_stats(linkname);
goto done;
}
local_stats = i_dlstat_rx_local_stats(linkname);
bcast_stats = i_dlstat_rx_bcast_stats(linkname);
defunctlane_stats = i_dlstat_rx_defunctlane_stats(linkname);
lane_stats = i_dlstat_rx_hwlane_stats(linkname);
if (lane_stats == NULL)
lane_stats = i_dlstat_rx_swlane_stats(dh, linkid, linkname);
head = i_dlstat_join_lists(local_stats, bcast_stats);
head = i_dlstat_join_lists(head, defunctlane_stats);
head = i_dlstat_join_lists(head, lane_stats);
done:
return (head);
}
/* Tx lane statistic specific functions */
static boolean_t
i_dlstat_tx_lane_match(void *arg1, void *arg2)
{
tx_lane_stat_entry_t *s1 = arg1;
tx_lane_stat_entry_t *s2 = arg2;
return (s1->tle_index == s2->tle_index &&
s1->tle_id == s2->tle_id);
}
static void *
i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
{
tx_lane_stat_entry_t *s1 = arg1;
tx_lane_stat_entry_t *s2 = arg2;
tx_lane_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (tx_lane_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->tle_index = s1->tle_index;
diff_entry->tle_id = s1->tle_id;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
TX_LANE_STAT_SIZE);
done:
return (diff_entry);
}
static void *
i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
tx_lane_stat_entry_t *tx_lane_stat_entry;
tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = i;
tx_lane_stat_entry->tle_id = L_HWLANE;
i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
tx_lane_stats_list, TX_LANE_STAT_SIZE);
done:
return (tx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
tx_lane_stat_entry_t *tx_lane_stat_entry;
tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
tx_lane_stat_entry->tle_id = L_SWLANE;
i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
tx_lane_stats_list, TX_LANE_STAT_SIZE);
done:
return (tx_lane_stat_entry);
}
static dladm_stat_chain_t *
i_dlstat_tx_bcast_stats(const char *linkname)
{
misc_stat_entry_t *misc_stat_entry;
dladm_stat_chain_t *head = NULL;
tx_lane_stat_entry_t *tx_lane_stat_entry;
misc_stat_entry = i_dlstat_misc_stats(linkname);
if (misc_stat_entry == NULL)
goto done;
tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
tx_lane_stat_entry->tle_id = L_BCAST;
tx_lane_stat_entry->tle_stats.tl_opackets =
misc_stat_entry->mse_stats.ms_brdcstxmt +
misc_stat_entry->mse_stats.ms_multixmt;
tx_lane_stat_entry->tle_stats.tl_obytes =
misc_stat_entry->mse_stats.ms_brdcstxmtbytes +
misc_stat_entry->mse_stats.ms_multixmtbytes;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(tx_lane_stat_entry);
goto done;
}
head->dc_statentry = tx_lane_stat_entry;
head->dc_next = NULL;
free(misc_stat_entry);
done:
return (head);
}
static dladm_stat_chain_t *
i_dlstat_tx_defunctlane_stats(const char *linkname)
{
misc_stat_entry_t *misc_stat_entry;
dladm_stat_chain_t *head = NULL;
tx_lane_stat_entry_t *tx_lane_stat_entry;
misc_stat_entry = i_dlstat_misc_stats(linkname);
if (misc_stat_entry == NULL)
goto done;
tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
tx_lane_stat_entry->tle_id = L_DFNCT;
tx_lane_stat_entry->tle_stats.tl_opackets =
misc_stat_entry->mse_stats.ms_opackets;
tx_lane_stat_entry->tle_stats.tl_obytes =
misc_stat_entry->mse_stats.ms_obytes;
tx_lane_stat_entry->tle_stats.tl_sdrops =
misc_stat_entry->mse_stats.ms_txsdrops;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(tx_lane_stat_entry);
goto done;
}
head->dc_statentry = tx_lane_stat_entry;
head->dc_next = NULL;
done:
return (head);
}
static dladm_stat_chain_t *
i_dlstat_tx_hwlane_stats(const char *linkname)
{
uint_t tx_hwlane_idlist[MAX_RINGS_PER_GROUP];
uint_t tx_hwlane_idlist_size;
i_dlstat_get_idlist(linkname, DLSTAT_TX_HWLANE_IDLIST,
tx_hwlane_idlist, &tx_hwlane_idlist_size);
return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_HWLANE,
tx_hwlane_idlist, tx_hwlane_idlist_size,
i_dlstat_tx_hwlane_retrieve_stat));
}
/*ARGSUSED*/
static dladm_stat_chain_t *
i_dlstat_tx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
const char *linkname)
{
return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_SWLANE,
default_idlist, default_idlist_size,
i_dlstat_tx_swlane_retrieve_stat));
}
void *
dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *head = NULL;
dladm_stat_chain_t *bcast_stats = NULL;
dladm_stat_chain_t *defunctlane_stats = NULL;
dladm_stat_chain_t *lane_stats;
char linkname[MAXLINKNAMELEN];
boolean_t is_legacy_driver;
if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
/* Check if it is legacy driver */
if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
"_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
goto done;
}
if (is_legacy_driver) {
head = i_dlstat_legacy_tx_lane_stats(linkname);
goto done;
}
bcast_stats = i_dlstat_tx_bcast_stats(linkname);
defunctlane_stats = i_dlstat_tx_defunctlane_stats(linkname);
lane_stats = i_dlstat_tx_hwlane_stats(linkname);
if (lane_stats == NULL)
lane_stats = i_dlstat_tx_swlane_stats(dh, linkid, linkname);
head = i_dlstat_join_lists(bcast_stats, defunctlane_stats);
head = i_dlstat_join_lists(head, lane_stats);
done:
return (head);
}
/* Rx lane total statistic specific functions */
void *
dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *total_head = NULL;
dladm_stat_chain_t *rx_lane_head, *curr;
rx_lane_stat_entry_t *total_stats;
/* Get per rx lane stats */
rx_lane_head = dlstat_rx_lane_stats(dh, linkid);
if (rx_lane_head == NULL)
goto done;
total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
if (total_stats == NULL)
goto done;
total_stats->rle_index = DLSTAT_INVALID_ENTRY;
total_stats->rle_id = DLSTAT_INVALID_ENTRY;
for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) {
rx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
i_dlstat_sum_stats(&total_stats->rle_stats,
&curr_lane_stats->rle_stats, &total_stats->rle_stats,
rx_lane_stats_list, RX_LANE_STAT_SIZE);
}
total_head = malloc(sizeof (dladm_stat_chain_t));
if (total_head == NULL) {
free(total_stats);
goto done;
}
total_head->dc_statentry = total_stats;
(void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total",
sizeof (total_head->dc_statheader));
total_head->dc_next = NULL;
free(rx_lane_head);
done:
return (total_head);
}
/* Tx lane total statistic specific functions */
void *
dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *total_head = NULL;
dladm_stat_chain_t *tx_lane_head, *curr;
tx_lane_stat_entry_t *total_stats;
/* Get per tx lane stats */
tx_lane_head = dlstat_tx_lane_stats(dh, linkid);
if (tx_lane_head == NULL)
goto done;
total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
if (total_stats == NULL)
goto done;
total_stats->tle_index = DLSTAT_INVALID_ENTRY;
total_stats->tle_id = DLSTAT_INVALID_ENTRY;
for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) {
tx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
i_dlstat_sum_stats(&total_stats->tle_stats,
&curr_lane_stats->tle_stats, &total_stats->tle_stats,
tx_lane_stats_list, TX_LANE_STAT_SIZE);
}
total_head = malloc(sizeof (dladm_stat_chain_t));
if (total_head == NULL) {
free(total_stats);
goto done;
}
total_head->dc_statentry = total_stats;
(void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total",
sizeof (total_head->dc_statheader));
total_head->dc_next = NULL;
free(tx_lane_head);
done:
return (total_head);
}
/* Fanout specific functions */
static boolean_t
i_dlstat_fanout_match(void *arg1, void *arg2)
{
fanout_stat_entry_t *s1 = arg1;
fanout_stat_entry_t *s2 = arg2;
return (s1->fe_index == s2->fe_index &&
s1->fe_id == s2->fe_id &&
s1->fe_foutindex == s2->fe_foutindex);
}
static void *
i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2)
{
fanout_stat_entry_t *s1 = arg1;
fanout_stat_entry_t *s2 = arg2;
fanout_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (fanout_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->fe_index = s1->fe_index;
diff_entry->fe_id = s1->fe_id;
diff_entry->fe_foutindex = s1->fe_foutindex;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list,
FANOUT_STAT_SIZE);
done:
return (diff_entry);
}
static void *
i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
fanout_stat_entry_t *fanout_stat_entry;
fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t));
if (fanout_stat_entry == NULL)
goto done;
/* Set by the caller later */
fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY;
fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY;
fanout_stat_entry->fe_foutindex = i;
i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats,
fanout_stats_list, FANOUT_STAT_SIZE);
done:
return (fanout_stat_entry);
}
static void *
i_dlstat_query_fanout_stats(dladm_handle_t dh, datalink_id_t linkid,
uint_t idlist[], uint_t idlist_size,
const char *modname, const char *prefix)
{
int i;
char statprefix[MAXLINKNAMELEN];
char linkname[MAXLINKNAMELEN];
dladm_stat_chain_t *curr, *curr_head;
dladm_stat_chain_t *head = NULL, *prev = NULL;
uint_t fanout_idlist[MAX_RINGS_PER_GROUP];
uint_t fanout_idlist_size;
if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
i_dlstat_get_idlist(linkname, DLSTAT_FANOUT_IDLIST,
fanout_idlist, &fanout_idlist_size);
for (i = 0; i < idlist_size; i++) {
uint_t index = idlist[i];
(void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout",
prefix, index);
curr_head = i_dlstat_query_stats(modname, statprefix,
fanout_idlist, fanout_idlist_size,
i_dlstat_fanout_retrieve_stat);
if (curr_head == NULL) /* Last lane */
break;
if (head == NULL) /* First lane */
head = curr_head;
else /* Link new lane list to end of previous lane list */
prev->dc_next = curr_head;
/* Walk new lane list and set ids */
for (curr = curr_head; curr != NULL; curr = curr->dc_next) {
fanout_stat_entry_t *curr_stats = curr->dc_statentry;
curr_stats->fe_index = index;
curr_stats->fe_id = L_HWLANE;
/*
* Save last pointer of previous linked list.
* This pointer is used to chain linked lists
* generated in each iteration.
*/
prev = curr;
}
}
return (head);
}
void *
dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid,
const char *linkname)
{
return (i_dlstat_query_fanout_stats(dh, linkid,
default_idlist, default_idlist_size, linkname,
DLSTAT_MAC_RX_SWLANE));
}
void *
dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid,
const char *linkname)
{
uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
uint_t rx_hwlane_idlist_size;
i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
rx_hwlane_idlist, &rx_hwlane_idlist_size);
return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist,
rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE));
}
void *
dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *head = NULL;
dladm_stat_chain_t *fout_hwlane_stats;
dladm_stat_chain_t *fout_swlane_and_local_stats;
fanout_stat_entry_t *fout_stats;
char linkname[MAXLINKNAMELEN];
if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
fout_swlane_and_local_stats =
dlstat_fanout_swlane_and_local_stats(dh, linkid, linkname);
fout_hwlane_stats = dlstat_fanout_hwlane_stats(dh, linkid, linkname);
if (fout_swlane_and_local_stats == NULL) {
head = fout_hwlane_stats;
goto done;
}
fout_stats = fout_swlane_and_local_stats->dc_statentry;
if (fout_hwlane_stats != NULL) { /* hwlane(s), only local traffic */
fout_stats->fe_id = L_LOCAL;
fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
} else { /* no hwlane, mix of local+sw classified */
fout_stats->fe_id = L_LCLSWLANE;
fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
}
fout_swlane_and_local_stats->dc_next = fout_hwlane_stats;
head = fout_swlane_and_local_stats;
done:
return (head);
}
/* Rx ring statistic specific functions */
static boolean_t
i_dlstat_rx_ring_match(void *arg1, void *arg2)
{
rx_lane_stat_entry_t *s1 = arg1;
rx_lane_stat_entry_t *s2 = arg2;
return (s1->rle_index == s2->rle_index);
}
static void *
i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
{
ring_stat_entry_t *s1 = arg1;
ring_stat_entry_t *s2 = arg2;
ring_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (ring_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->re_index = s1->re_index;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
RX_RING_STAT_SIZE);
done:
return (diff_entry);
}
static void *
i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
ring_stat_entry_t *rx_ring_stat_entry;
rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
if (rx_ring_stat_entry == NULL)
goto done;
rx_ring_stat_entry->re_index = i;
i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
rx_ring_stats_list, RX_RING_STAT_SIZE);
done:
return (rx_ring_stat_entry);
}
void *
dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
{
uint_t rx_ring_idlist[MAX_RINGS_PER_GROUP];
uint_t rx_ring_idlist_size;
dladm_phys_attr_t dpa;
char linkname[MAXLINKNAMELEN];
char *modname;
datalink_class_t class;
/*
* kstats corresponding to physical device rings continue to use
* device names even if the link is renamed using dladm rename-link.
* Thus, given a linkid, we lookup the physical device name.
* However, if an aggr is renamed, kstats corresponding to its
* pseudo rings are renamed as well.
*/
if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
if (class != DATALINK_CLASS_AGGR) {
if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
DLADM_STATUS_OK) {
return (NULL);
}
modname = dpa.dp_dev;
} else
modname = linkname;
i_dlstat_get_idlist(modname, DLSTAT_RX_RING_IDLIST,
rx_ring_idlist, &rx_ring_idlist_size);
return (i_dlstat_query_stats(modname, DLSTAT_MAC_RX_RING,
rx_ring_idlist, rx_ring_idlist_size,
i_dlstat_rx_ring_retrieve_stat));
}
/* Tx ring statistic specific functions */
static boolean_t
i_dlstat_tx_ring_match(void *arg1, void *arg2)
{
tx_lane_stat_entry_t *s1 = arg1;
tx_lane_stat_entry_t *s2 = arg2;
return (s1->tle_index == s2->tle_index);
}
static void *
i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
{
ring_stat_entry_t *s1 = arg1;
ring_stat_entry_t *s2 = arg2;
ring_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (ring_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->re_index = s1->re_index;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
TX_RING_STAT_SIZE);
done:
return (diff_entry);
}
static void *
i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
{
ring_stat_entry_t *tx_ring_stat_entry;
tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
if (tx_ring_stat_entry == NULL)
goto done;
tx_ring_stat_entry->re_index = i;
i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
tx_ring_stats_list, TX_RING_STAT_SIZE);
done:
return (tx_ring_stat_entry);
}
void *
dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
{
uint_t tx_ring_idlist[MAX_RINGS_PER_GROUP];
uint_t tx_ring_idlist_size;
dladm_phys_attr_t dpa;
char linkname[MAXLINKNAMELEN];
char *modname;
datalink_class_t class;
/*
* kstats corresponding to physical device rings continue to use
* device names even if the link is renamed using dladm rename-link.
* Thus, given a linkid, we lookup the physical device name.
* However, if an aggr is renamed, kstats corresponding to its
* pseudo rings are renamed as well.
*/
if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
if (class != DATALINK_CLASS_AGGR) {
if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
DLADM_STATUS_OK) {
return (NULL);
}
modname = dpa.dp_dev;
} else
modname = linkname;
i_dlstat_get_idlist(modname, DLSTAT_TX_RING_IDLIST,
tx_ring_idlist, &tx_ring_idlist_size);
return (i_dlstat_query_stats(modname, DLSTAT_MAC_TX_RING,
tx_ring_idlist, tx_ring_idlist_size,
i_dlstat_tx_ring_retrieve_stat));
}
/* Rx ring total statistic specific functions */
void *
dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *total_head = NULL;
dladm_stat_chain_t *rx_ring_head, *curr;
ring_stat_entry_t *total_stats;
/* Get per rx ring stats */
rx_ring_head = dlstat_rx_ring_stats(dh, linkid);
if (rx_ring_head == NULL)
goto done;
total_stats = calloc(1, sizeof (ring_stat_entry_t));
if (total_stats == NULL)
goto done;
total_stats->re_index = DLSTAT_INVALID_ENTRY;
for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) {
ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
i_dlstat_sum_stats(&total_stats->re_stats,
&curr_ring_stats->re_stats, &total_stats->re_stats,
rx_ring_stats_list, RX_RING_STAT_SIZE);
}
total_head = malloc(sizeof (dladm_stat_chain_t));
if (total_head == NULL) {
free(total_stats);
goto done;
}
total_head->dc_statentry = total_stats;
(void) strlcpy(total_head->dc_statheader, "mac_rx_ring_total",
sizeof (total_head->dc_statheader));
total_head->dc_next = NULL;
free(rx_ring_head);
done:
return (total_head);
}
/* Tx ring total statistic specific functions */
void *
dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *total_head = NULL;
dladm_stat_chain_t *tx_ring_head, *curr;
ring_stat_entry_t *total_stats;
/* Get per tx ring stats */
tx_ring_head = dlstat_tx_ring_stats(dh, linkid);
if (tx_ring_head == NULL)
goto done;
total_stats = calloc(1, sizeof (ring_stat_entry_t));
if (total_stats == NULL)
goto done;
total_stats->re_index = DLSTAT_INVALID_ENTRY;
for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) {
ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
i_dlstat_sum_stats(&total_stats->re_stats,
&curr_ring_stats->re_stats, &total_stats->re_stats,
tx_ring_stats_list, TX_RING_STAT_SIZE);
}
total_head = malloc(sizeof (dladm_stat_chain_t));
if (total_head == NULL) {
free(total_stats);
goto done;
}
total_head->dc_statentry = total_stats;
(void) strlcpy(total_head->dc_statheader, "mac_tx_ring_total",
sizeof (total_head->dc_statheader));
total_head->dc_next = NULL;
free(tx_ring_head);
done:
return (total_head);
}
/* Summary statistic specific functions */
/*ARGSUSED*/
static boolean_t
i_dlstat_total_match(void *arg1, void *arg2)
{
/* Always single entry for total */
return (B_TRUE);
}
static void *
i_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
{
total_stat_entry_t *s1 = arg1;
total_stat_entry_t *s2 = arg2;
total_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (total_stat_entry_t));
if (diff_entry == NULL)
goto done;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
TOTAL_STAT_SIZE);
done:
return (diff_entry);
}
void *
dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_stat_chain_t *head = NULL;
dladm_stat_chain_t *rx_total;
dladm_stat_chain_t *tx_total;
total_stat_entry_t *total_stat_entry;
rx_lane_stat_entry_t *rx_lane_stat_entry;
tx_lane_stat_entry_t *tx_lane_stat_entry;
/* Get total rx lane stats */
rx_total = dlstat_rx_lane_total_stats(dh, linkid);
if (rx_total == NULL)
goto done;
/* Get total tx lane stats */
tx_total = dlstat_tx_lane_total_stats(dh, linkid);
if (tx_total == NULL)
goto done;
/* Build total stat */
total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
if (total_stat_entry == NULL)
goto done;
rx_lane_stat_entry = rx_total->dc_statentry;
tx_lane_stat_entry = tx_total->dc_statentry;
/* Extract total rx ipackets, rbytes */
total_stat_entry->tse_stats.ts_ipackets =
rx_lane_stat_entry->rle_stats.rl_ipackets;
total_stat_entry->tse_stats.ts_rbytes =
rx_lane_stat_entry->rle_stats.rl_rbytes;
/* Extract total tx opackets, obytes */
total_stat_entry->tse_stats.ts_opackets =
tx_lane_stat_entry->tle_stats.tl_opackets;
total_stat_entry->tse_stats.ts_obytes =
tx_lane_stat_entry->tle_stats.tl_obytes;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(total_stat_entry);
goto done;
}
head->dc_statentry = total_stat_entry;
(void) strlcpy(head->dc_statheader, "mac_lane_total",
sizeof (head->dc_statheader));
head->dc_next = NULL;
free(rx_total);
free(tx_total);
done:
return (head);
}
/* Aggr total statistic(summed across all component ports) specific functions */
void *
dlstat_aggr_total_stats(dladm_stat_chain_t *head)
{
dladm_stat_chain_t *curr;
dladm_stat_chain_t *total_head;
aggr_port_stat_entry_t *total_stats;
total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
if (total_stats == NULL)
goto done;
total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
for (curr = head; curr != NULL; curr = curr->dc_next) {
aggr_port_stat_entry_t *curr_aggr_port_stats;
curr_aggr_port_stats = curr->dc_statentry;
i_dlstat_sum_stats(&total_stats->ape_stats,
&curr_aggr_port_stats->ape_stats, &total_stats->ape_stats,
aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
}
total_head = malloc(sizeof (dladm_stat_chain_t));
if (total_head == NULL) {
free(total_stats);
goto done;
}
total_head->dc_statentry = total_stats;
total_head->dc_next = NULL;
done:
return (total_head);
}
/* Aggr port statistic specific functions */
static boolean_t
i_dlstat_aggr_port_match(void *arg1, void *arg2)
{
aggr_port_stat_entry_t *s1 = arg1;
aggr_port_stat_entry_t *s2 = arg2;
return (s1->ape_portlinkid == s2->ape_portlinkid);
}
static void *
i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
{
aggr_port_stat_entry_t *s1 = arg1;
aggr_port_stat_entry_t *s2 = arg2;
aggr_port_stat_entry_t *diff_entry;
diff_entry = malloc(sizeof (aggr_port_stat_entry_t));
if (diff_entry == NULL)
goto done;
diff_entry->ape_portlinkid = s1->ape_portlinkid;
DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
AGGR_PORT_STAT_SIZE);
done:
return (diff_entry);
}
/*
* Query dls stats for the aggr port. This results in query for stats into
* the corresponding device driver.
*/
static aggr_port_stat_entry_t *
i_dlstat_single_port_stats(const char *portname, datalink_id_t linkid)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
char module[DLPI_LINKNAME_MAX];
uint_t instance;
aggr_port_stat_entry_t *aggr_port_stat_entry = NULL;
if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK)
goto done;
if ((kcp = kstat_open()) == NULL) {
warn("kstat open operation failed");
return (NULL);
}
ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL);
if (ksp == NULL)
goto done;
aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
if (aggr_port_stat_entry == NULL)
goto done;
/* Save port's linkid */
aggr_port_stat_entry->ape_portlinkid = linkid;
i_dlstat_get_stats(kcp, ksp, &aggr_port_stat_entry->ape_stats,
aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
done:
(void) kstat_close(kcp);
return (aggr_port_stat_entry);
}
void *
dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
{
dladm_aggr_grp_attr_t ginfo;
int i;
dladm_aggr_port_attr_t *portp;
dladm_phys_attr_t dpa;
aggr_port_stat_entry_t *aggr_port_stat_entry;
dladm_stat_chain_t *head = NULL, *prev = NULL, *curr;
dladm_stat_chain_t *total_stats;
/* Get aggr info */
bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE)
!= DLADM_STATUS_OK)
goto done;
/* For every port that is member of this aggr do */
for (i = 0; i < ginfo.lg_nports; i++) {
portp = &(ginfo.lg_ports[i]);
if (dladm_phys_info(dh, portp->lp_linkid, &dpa,
DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
goto done;
}
aggr_port_stat_entry = i_dlstat_single_port_stats(dpa.dp_dev,
portp->lp_linkid);
/* Create dladm_stat_chain_t object for this stat */
curr = malloc(sizeof (dladm_stat_chain_t));
if (curr == NULL) {
free(aggr_port_stat_entry);
goto done;
}
(void) strlcpy(curr->dc_statheader, dpa.dp_dev,
sizeof (curr->dc_statheader));
curr->dc_statentry = aggr_port_stat_entry;
curr->dc_next = NULL;
/* Chain this aggr port stat entry */
/* head of the stat list */
if (prev == NULL)
head = curr;
else
prev->dc_next = curr;
prev = curr;
}
/*
* Prepend the stat list with cumulative aggr stats i.e. summed over all
* component ports
*/
total_stats = dlstat_aggr_total_stats(head);
if (total_stats != NULL) {
total_stats->dc_next = head;
head = total_stats;
}
done:
free(ginfo.lg_ports);
return (head);
}
/* Misc stat specific functions */
void *
dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid)
{
misc_stat_entry_t *misc_stat_entry;
dladm_stat_chain_t *head = NULL;
char linkname[MAXLINKNAMELEN];
if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
misc_stat_entry = i_dlstat_misc_stats(linkname);
if (misc_stat_entry == NULL)
goto done;
head = malloc(sizeof (dladm_stat_chain_t));
if (head == NULL) {
free(misc_stat_entry);
goto done;
}
head->dc_statentry = misc_stat_entry;
(void) strlcpy(head->dc_statheader, "mac_misc_stat",
sizeof (head->dc_statheader));
head->dc_next = NULL;
done:
return (head);
}
/* Exported functions */
dladm_stat_chain_t *
dladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid,
dladm_stat_type_t stattype)
{
return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
}
dladm_stat_chain_t *
dladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2,
dladm_stat_type_t stattype)
{
dladm_stat_chain_t *op1_curr, *op2_curr;
dladm_stat_chain_t *diff_curr;
dladm_stat_chain_t *diff_prev = NULL, *diff_head = NULL;
/* Perform op1 - op2, store result in diff */
for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) {
for (op2_curr = op2; op2_curr != NULL;
op2_curr = op2_curr->dc_next) {
if (dlstat_match_stats(op1_curr->dc_statentry,
op2_curr->dc_statentry, stattype)) {
break;
}
}
diff_curr = malloc(sizeof (dladm_stat_chain_t));
if (diff_curr == NULL)
goto done;
diff_curr->dc_next = NULL;
if (op2_curr == NULL) {
/* prev iteration did not have this stat entry */
diff_curr->dc_statentry =
dlstat_diff_stats(op1_curr->dc_statentry,
NULL, stattype);
} else {
diff_curr->dc_statentry =
dlstat_diff_stats(op1_curr->dc_statentry,
op2_curr->dc_statentry, stattype);
}
if (diff_curr->dc_statentry == NULL) {
free(diff_curr);
goto done;
}
if (diff_prev == NULL) /* head of the diff stat list */
diff_head = diff_curr;
else
diff_prev->dc_next = diff_curr;
diff_prev = diff_curr;
}
done:
return (diff_head);
}
void
dladm_link_stat_free(dladm_stat_chain_t *curr)
{
while (curr != NULL) {
dladm_stat_chain_t *tofree = curr;
curr = curr->dc_next;
free(tofree->dc_statentry);
free(tofree);
}
}
/* Query all link stats */
static name_value_stat_t *
i_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size)
{
int i;
name_value_stat_t *head_stat = NULL, *prev_stat = NULL;
name_value_stat_t *curr_stat;
for (i = 0; i < size; i++) {
uint64_t *val = (void *)
((uchar_t *)stats + stats_list[i].si_offset);
curr_stat = calloc(1, sizeof (name_value_stat_t));
if (curr_stat == NULL)
break;
(void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name,
sizeof (curr_stat->nv_statname));
curr_stat->nv_statval = *val;
curr_stat->nv_nextstat = NULL;
if (head_stat == NULL) /* First node */
head_stat = curr_stat;
else
prev_stat->nv_nextstat = curr_stat;
prev_stat = curr_stat;
}
return (head_stat);
}
void *
build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
{
name_value_stat_entry_t *name_value_stat_entry;
dladm_stat_desc_t *stattbl_ptr;
void *statfields;
stattbl_ptr = &dladm_stat_table[stattype];
/* Allocate memory for query all stat entry */
name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
if (name_value_stat_entry == NULL)
goto done;
/* Header for these stat fields */
(void) strlcpy(name_value_stat_entry->nve_header, statheader,
sizeof (name_value_stat_entry->nve_header));
/* Extract stat fields from the statentry */
statfields = (uchar_t *)statentry +
dladm_stat_table[stattype].ds_offset;
/* Convert curr_stat to <statname, statval> pair */
name_value_stat_entry->nve_stats =
i_dlstat_convert_stats(statfields,
stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize);
done:
return (name_value_stat_entry);
}
void *
i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
{
dladm_stat_chain_t *curr;
dladm_stat_chain_t *nvstat_head = NULL, *nvstat_prev = NULL;
dladm_stat_chain_t *nvstat_curr;
/*
* For every stat in the chain, build header and convert all
* its stat fields
*/
for (curr = stat_head; curr != NULL; curr = curr->dc_next) {
nvstat_curr = malloc(sizeof (dladm_stat_chain_t));
if (nvstat_curr == NULL)
break;
nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
curr->dc_statentry, stattype);
if (nvstat_curr->dc_statentry == NULL) {
free(nvstat_curr);
break;
}
nvstat_curr->dc_next = NULL;
if (nvstat_head == NULL) /* First node */
nvstat_head = nvstat_curr;
else
nvstat_prev->dc_next = nvstat_curr;
nvstat_prev = nvstat_curr;
}
done:
return (nvstat_head);
}
dladm_stat_chain_t *
dladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid,
dladm_stat_type_t stattype)
{
dladm_stat_chain_t *stat_head;
dladm_stat_chain_t *nvstat_head = NULL;
/* Query the requested stat */
stat_head = dladm_link_stat_query(dh, linkid, stattype);
if (stat_head == NULL)
goto done;
/*
* Convert every statfield in every stat-entry of stat chain to
* <statname, statval> pair
*/
nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
/* Free stat_head */
dladm_link_stat_free(stat_head);
done:
return (nvstat_head);
}
void
dladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
{
while (curr != NULL) {
dladm_stat_chain_t *tofree = curr;
name_value_stat_entry_t *nv_entry = curr->dc_statentry;
name_value_stat_t *nv_curr = nv_entry->nve_stats;
while (nv_curr != NULL) {
name_value_stat_t *nv_tofree = nv_curr;
nv_curr = nv_curr->nv_nextstat;
free(nv_tofree);
}
curr = curr->dc_next;
free(nv_entry);
free(tofree);
}
}
/* flow stats specific routines */
flow_stat_t *
dladm_flow_stat_query(const char *flowname)
{
kstat_ctl_t *kcp;
kstat_t *ksp;
flow_stat_t *flow_stat = NULL;
if ((kcp = kstat_open()) == NULL)
return (NULL);
flow_stat = calloc(1, sizeof (flow_stat_t));
if (flow_stat == NULL)
goto done;
ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
if (ksp != NULL) {
i_dlstat_get_stats(kcp, ksp, flow_stat, flow_stats_list,
FLOW_STAT_SIZE);
}
done:
(void) kstat_close(kcp);
return (flow_stat);
}
flow_stat_t *
dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
{
flow_stat_t *diff_stat;
diff_stat = calloc(1, sizeof (flow_stat_t));
if (diff_stat == NULL)
goto done;
if (op2 == NULL) {
bcopy(op1, diff_stat, sizeof (flow_stat_t));
} else {
i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list,
FLOW_STAT_SIZE);
}
done:
return (diff_stat);
}
void
dladm_flow_stat_free(flow_stat_t *curr)
{
free(curr);
}
/* Query all flow stats */
name_value_stat_entry_t *
dladm_flow_stat_query_all(const char *flowname)
{
flow_stat_t *flow_stat;
name_value_stat_entry_t *name_value_stat_entry = NULL;
/* Query flow stats */
flow_stat = dladm_flow_stat_query(flowname);
if (flow_stat == NULL)
goto done;
/* Allocate memory for query all stat entry */
name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
if (name_value_stat_entry == NULL) {
dladm_flow_stat_free(flow_stat);
goto done;
}
/* Header for these stat fields */
(void) strncpy(name_value_stat_entry->nve_header, flowname,
MAXFLOWNAMELEN);
/* Convert every statfield in flow_stat to <statname, statval> pair */
name_value_stat_entry->nve_stats =
i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE);
/* Free flow_stat */
dladm_flow_stat_free(flow_stat);
done:
return (name_value_stat_entry);
}
void
dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
{
name_value_stat_t *nv_curr = curr->nve_stats;
while (nv_curr != NULL) {
name_value_stat_t *nv_tofree = nv_curr;
nv_curr = nv_curr->nv_nextstat;
free(nv_tofree);
}
}