1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Copyright 2019 OmniOS Community Edition (OmniOSce) Association.
  28  */
  29 
  30 #include <stddef.h>
  31 #include <stdio.h>
  32 #include <stdlib.h>
  33 #include <strings.h>
  34 #include <err.h>
  35 #include <errno.h>
  36 #include <fcntl.h>
  37 #include <kstat.h>
  38 #include <limits.h>
  39 #include <unistd.h>
  40 #include <signal.h>
  41 #include <sys/dld.h>
  42 #include <sys/ddi.h>
  43 
  44 #include <libdllink.h>
  45 #include <libdlflow.h>
  46 #include <libdlstat.h>
  47 #include <libdlaggr.h>
  48 
  49 struct flowlist {
  50         char            flowname[MAXFLOWNAMELEN];
  51         char            linkname[MAXLINKNAMELEN];
  52         datalink_id_t   linkid;
  53         int             fd;
  54         uint64_t        ifspeed;
  55         boolean_t       first;
  56         boolean_t       display;
  57         pktsum_t        prevstats;
  58         pktsum_t        diffstats;
  59 };
  60 
  61 pktsum_t                totalstats;
  62 struct flowlist         *stattable = NULL;
  63 
  64 #define STATGROWSIZE    16
  65 
  66 /* Exported functions */
  67 
  68 /*
  69  * dladm_kstat_lookup() is a modified version of kstat_lookup which
  70  * adds the class as a selector.
  71  */
  72 kstat_t *
  73 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
  74     const char *name, const char *class)
  75 {
  76         kstat_t *ksp = NULL;
  77 
  78         for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
  79                 if ((module == NULL || strcmp(ksp->ks_module, module) == 0) &&
  80                     (instance == -1 || ksp->ks_instance == instance) &&
  81                     (name == NULL || strcmp(ksp->ks_name, name) == 0) &&
  82                     (class == NULL || strcmp(ksp->ks_class, class) == 0))
  83                         return (ksp);
  84         }
  85 
  86         errno = ENOENT;
  87         return (NULL);
  88 }
  89 
  90 /*
  91  * dladm_get_stats() populates the supplied pktsum_t structure with
  92  * the input and output  packet and byte kstats from the kstat_t
  93  * found with dladm_kstat_lookup.
  94  */
  95 void
  96 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
  97 {
  98 
  99         if (kstat_read(kcp, ksp, NULL) == -1)
 100                 return;
 101 
 102         stats->snaptime = gethrtime();
 103 
 104         if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
 105             &stats->ipackets) < 0) {
 106                 if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
 107                     &stats->ipackets) < 0)
 108                         return;
 109         }
 110 
 111         if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
 112             &stats->opackets) < 0) {
 113                 if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
 114                     &stats->opackets) < 0)
 115                         return;
 116         }
 117 
 118         if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
 119             &stats->rbytes) < 0) {
 120                 if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
 121                     &stats->rbytes) < 0)
 122                         return;
 123         }
 124 
 125         if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
 126             &stats->obytes) < 0) {
 127                 if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
 128                     &stats->obytes) < 0)
 129                         return;
 130         }
 131 
 132         if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
 133             &stats->ierrors) < 0) {
 134                 if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
 135                     &stats->ierrors) < 0)
 136                 return;
 137         }
 138 
 139         if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
 140             &stats->oerrors) < 0) {
 141                 if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
 142                     &stats->oerrors) < 0)
 143                         return;
 144         }
 145 }
 146 
 147 int
 148 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
 149 {
 150         kstat_named_t   *knp;
 151 
 152         if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
 153                 return (-1);
 154 
 155         if (knp->data_type != type)
 156                 return (-1);
 157 
 158         switch (type) {
 159         case KSTAT_DATA_UINT64:
 160                 *(uint64_t *)buf = knp->value.ui64;
 161                 break;
 162         case KSTAT_DATA_UINT32:
 163                 *(uint32_t *)buf = knp->value.ui32;
 164                 break;
 165         default:
 166                 return (-1);
 167         }
 168 
 169         return (0);
 170 }
 171 
 172 dladm_status_t
 173 dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
 174     const char *name, uint8_t type, void *val)
 175 {
 176         char            module[DLPI_LINKNAME_MAX];
 177         uint_t          instance;
 178         char            link[DLPI_LINKNAME_MAX];
 179         dladm_status_t  status;
 180         uint32_t        flags, media;
 181         kstat_t         *ksp;
 182         dladm_phys_attr_t dpap;
 183 
 184         if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
 185             &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
 186                 return (status);
 187 
 188         if (media != DL_ETHER)
 189                 return (DLADM_STATUS_LINKINVAL);
 190 
 191         status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
 192 
 193         if (status != DLADM_STATUS_OK)
 194                 return (status);
 195 
 196         status = dladm_parselink(dpap.dp_dev, module, &instance);
 197 
 198         if (status != DLADM_STATUS_OK)
 199                 return (status);
 200 
 201         /*
 202          * The kstat query could fail if the underlying MAC
 203          * driver was already detached.
 204          */
 205         if (dladm_dld_kcp(handle) == NULL) {
 206                 warn("kstat_open operation failed");
 207                 return (-1);
 208         }
 209 
 210         if ((ksp = kstat_lookup(dladm_dld_kcp(handle), module, instance,
 211             "mac")) == NULL &&
 212             (ksp = kstat_lookup(dladm_dld_kcp(handle), module, instance,
 213             NULL)) == NULL)
 214                 goto bail;
 215 
 216         if (kstat_read(dladm_dld_kcp(handle), ksp, NULL) == -1)
 217                 goto bail;
 218 
 219         if (dladm_kstat_value(ksp, name, type, val) < 0)
 220                 goto bail;
 221 
 222         return (DLADM_STATUS_OK);
 223 
 224 bail:
 225         return (dladm_errno2status(errno));
 226 }
 227 
 228 /* Compute sum of 2 pktsums (s1 = s2 + s3) */
 229 void
 230 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
 231 {
 232         s1->rbytes    = s2->rbytes    + s3->rbytes;
 233         s1->ipackets  = s2->ipackets  + s3->ipackets;
 234         s1->ierrors   = s2->ierrors   + s3->ierrors;
 235         s1->obytes    = s2->obytes    + s3->obytes;
 236         s1->opackets  = s2->opackets  + s3->opackets;
 237         s1->oerrors   = s2->oerrors   + s3->oerrors;
 238         s1->snaptime  = s2->snaptime;
 239 }
 240 
 241 #define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
 242 
 243 
 244 /* Compute differences between 2 pktsums (s1 = s2 - s3) */
 245 void
 246 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
 247 {
 248         s1->rbytes    = DIFF_STAT(s2->rbytes,   s3->rbytes);
 249         s1->ipackets  = DIFF_STAT(s2->ipackets, s3->ipackets);
 250         s1->ierrors   = DIFF_STAT(s2->ierrors,  s3->ierrors);
 251         s1->obytes    = DIFF_STAT(s2->obytes,   s3->obytes);
 252         s1->opackets  = DIFF_STAT(s2->opackets, s3->opackets);
 253         s1->oerrors   = DIFF_STAT(s2->oerrors,  s3->oerrors);
 254         s1->snaptime  = DIFF_STAT(s2->snaptime, s3->snaptime);
 255 }
 256 
 257 #define DLSTAT_MAC_RX_SWLANE    "mac_rx_swlane"
 258 #define DLSTAT_MAC_RX_HWLANE    "mac_rx_hwlane"
 259 #define DLSTAT_MAC_TX_SWLANE    "mac_tx_swlane"
 260 #define DLSTAT_MAC_TX_HWLANE    "mac_tx_hwlane"
 261 #define DLSTAT_MAC_MISC_STAT    "mac_misc_stat"
 262 #define DLSTAT_MAC_RX_RING      "mac_rx_ring"
 263 #define DLSTAT_MAC_TX_RING      "mac_tx_ring"
 264 #define DLSTAT_MAC_FANOUT       "mac_rx_swlane0_fanout"
 265 
 266 typedef struct {
 267         const char      *si_name;
 268         uint_t          si_offset;
 269 } stat_info_t;
 270 
 271 #define A_CNT(arr)      (sizeof (arr) / sizeof (arr[0]))
 272 
 273 /* Definitions for rx lane stats */
 274 #define RL_OFF(f)       (offsetof(rx_lane_stat_t, f))
 275 
 276 static  stat_info_t     rx_hwlane_stats_list[] = {
 277         {"ipackets",            RL_OFF(rl_ipackets)},
 278         {"rbytes",              RL_OFF(rl_rbytes)},
 279         {"intrs",               RL_OFF(rl_intrs)},
 280         {"intrbytes",           RL_OFF(rl_intrbytes)},
 281         {"polls",               RL_OFF(rl_polls)},
 282         {"pollbytes",           RL_OFF(rl_pollbytes)},
 283         {"rxsdrops",            RL_OFF(rl_sdrops)},
 284         {"chainunder10",        RL_OFF(rl_chl10)},
 285         {"chain10to50",         RL_OFF(rl_ch10_50)},
 286         {"chainover50",         RL_OFF(rl_chg50)}
 287 };
 288 #define RX_HWLANE_STAT_SIZE     A_CNT(rx_hwlane_stats_list)
 289 
 290 static  stat_info_t     rx_swlane_stats_list[] = {
 291         {"ipackets",            RL_OFF(rl_ipackets)},
 292         {"rbytes",              RL_OFF(rl_rbytes)},
 293         {"local",               RL_OFF(rl_lclpackets)},
 294         {"localbytes",          RL_OFF(rl_lclbytes)},
 295         {"intrs",               RL_OFF(rl_intrs)},
 296         {"intrbytes",           RL_OFF(rl_intrbytes)},
 297         {"rxsdrops",            RL_OFF(rl_sdrops)}
 298 };
 299 #define RX_SWLANE_STAT_SIZE     A_CNT(rx_swlane_stats_list)
 300 
 301 static  stat_info_t     rx_lane_stats_list[] = {
 302         {"ipackets",            RL_OFF(rl_ipackets)},
 303         {"rbytes",              RL_OFF(rl_rbytes)},
 304         {"local",               RL_OFF(rl_lclpackets)},
 305         {"localbytes",          RL_OFF(rl_lclbytes)},
 306         {"intrs",               RL_OFF(rl_intrs)},
 307         {"intrbytes",           RL_OFF(rl_intrbytes)},
 308         {"polls",               RL_OFF(rl_polls)},
 309         {"rxsdrops",            RL_OFF(rl_sdrops)},
 310         {"pollbytes",           RL_OFF(rl_pollbytes)},
 311         {"chainunder10",        RL_OFF(rl_chl10)},
 312         {"chain10to50",         RL_OFF(rl_ch10_50)},
 313         {"chainover50",         RL_OFF(rl_chg50)}
 314 };
 315 #define RX_LANE_STAT_SIZE       A_CNT(rx_lane_stats_list)
 316 
 317 /* Definitions for tx lane stats */
 318 #define TL_OFF(f)       (offsetof(tx_lane_stat_t, f))
 319 
 320 static  stat_info_t     tx_lane_stats_list[] = {
 321         {"opackets",    TL_OFF(tl_opackets)},
 322         {"obytes",      TL_OFF(tl_obytes)},
 323         {"blockcnt",    TL_OFF(tl_blockcnt)},
 324         {"unblockcnt",  TL_OFF(tl_unblockcnt)},
 325         {"txsdrops",    TL_OFF(tl_sdrops)}
 326 };
 327 #define TX_LANE_STAT_SIZE       A_CNT(tx_lane_stats_list)
 328 
 329 /* Definitions for tx/rx misc stats */
 330 #define M_OFF(f)        (offsetof(misc_stat_t, f))
 331 
 332 static  stat_info_t     misc_stats_list[] = {
 333         {"multircv",            M_OFF(ms_multircv)},
 334         {"brdcstrcv",           M_OFF(ms_brdcstrcv)},
 335         {"multixmt",            M_OFF(ms_multixmt)},
 336         {"brdcstxmt",           M_OFF(ms_brdcstxmt)},
 337         {"multircvbytes",       M_OFF(ms_multircvbytes)},
 338         {"brdcstrcvbytes",      M_OFF(ms_brdcstrcvbytes)},
 339         {"multixmtbytes",       M_OFF(ms_multixmtbytes)},
 340         {"brdcstxmtbytes",      M_OFF(ms_brdcstxmtbytes)},
 341         {"txerrors",            M_OFF(ms_txerrors)},
 342         {"macspoofed",          M_OFF(ms_macspoofed)},
 343         {"ipspoofed",           M_OFF(ms_ipspoofed)},
 344         {"dhcpspoofed",         M_OFF(ms_dhcpspoofed)},
 345         {"restricted",          M_OFF(ms_restricted)},
 346         {"ipackets",            M_OFF(ms_ipackets)},
 347         {"rbytes",              M_OFF(ms_rbytes)},
 348         {"local",               M_OFF(ms_local)},
 349         {"localbytes",          M_OFF(ms_localbytes)},
 350         {"intrs",               M_OFF(ms_intrs)},
 351         {"intrbytes",           M_OFF(ms_intrbytes)},
 352         {"polls",               M_OFF(ms_polls)},
 353         {"pollbytes",           M_OFF(ms_pollbytes)},
 354         {"rxsdrops",            M_OFF(ms_rxsdrops)},
 355         {"chainunder10",        M_OFF(ms_chainunder10)},
 356         {"chain10to50",         M_OFF(ms_chain10to50)},
 357         {"chainover50",         M_OFF(ms_chainover50)},
 358         {"obytes",              M_OFF(ms_obytes)},
 359         {"opackets",            M_OFF(ms_opackets)},
 360         {"blockcnt",            M_OFF(ms_blockcnt)},
 361         {"unblockcnt",          M_OFF(ms_unblockcnt)},
 362         {"txsdrops",            M_OFF(ms_txsdrops)}
 363 };
 364 #define MISC_STAT_SIZE          A_CNT(misc_stats_list)
 365 
 366 /* Definitions for rx ring stats */
 367 #define R_OFF(f)        (offsetof(ring_stat_t, f))
 368 
 369 static  stat_info_t     rx_ring_stats_list[] = {
 370         {"ipackets",    R_OFF(r_packets)},
 371         {"rbytes",      R_OFF(r_bytes)}
 372 };
 373 #define RX_RING_STAT_SIZE       A_CNT(rx_ring_stats_list)
 374 
 375 /* Definitions for tx ring stats */
 376 static  stat_info_t     tx_ring_stats_list[] = {
 377         {"opackets",    R_OFF(r_packets)},
 378         {"obytes",      R_OFF(r_bytes)}
 379 };
 380 #define TX_RING_STAT_SIZE       A_CNT(tx_ring_stats_list)
 381 
 382 /* Definitions for fanout stats */
 383 #define F_OFF(f)        (offsetof(fanout_stat_t, f))
 384 
 385 static  stat_info_t     fanout_stats_list[] = {
 386         {"ipackets",    F_OFF(f_ipackets)},
 387         {"rbytes",      F_OFF(f_rbytes)},
 388 };
 389 #define FANOUT_STAT_SIZE        A_CNT(fanout_stats_list)
 390 
 391 /* Definitions for total stats */
 392 #define T_OFF(f)        (offsetof(total_stat_t, f))
 393 
 394 static  stat_info_t     total_stats_list[] = {
 395         {"ipackets",    T_OFF(ts_ipackets)},
 396         {"rbytes",      T_OFF(ts_rbytes)},
 397         {"opackets",    T_OFF(ts_opackets)},
 398         {"obytes",      T_OFF(ts_obytes)}
 399 };
 400 #define TOTAL_STAT_SIZE         A_CNT(total_stats_list)
 401 
 402 /* Definitions for aggr stats */
 403 #define AP_OFF(f)       (offsetof(aggr_port_stat_t, f))
 404 
 405 static  stat_info_t     aggr_port_stats_list[] = {
 406         {"ipackets64",  AP_OFF(ap_ipackets)},
 407         {"rbytes64",    AP_OFF(ap_rbytes)},
 408         {"opackets64",  AP_OFF(ap_opackets)},
 409         {"obytes64",    AP_OFF(ap_obytes)}
 410 };
 411 #define AGGR_PORT_STAT_SIZE     A_CNT(aggr_port_stats_list)
 412 
 413 /* Definitions for flow stats */
 414 #define FL_OFF(f)       (offsetof(flow_stat_t, f))
 415 
 416 static  stat_info_t     flow_stats_list[] = {
 417         {"ipackets",    FL_OFF(fl_ipackets)},
 418         {"rbytes",      FL_OFF(fl_rbytes)},
 419         {"opackets",    FL_OFF(fl_opackets)},
 420         {"obytes",      FL_OFF(fl_obytes)}
 421 };
 422 #define FLOW_STAT_SIZE          A_CNT(flow_stats_list)
 423 
 424 /* Rx lane specific functions */
 425 void *                  dlstat_rx_lane_stats(dladm_handle_t, datalink_id_t);
 426 static boolean_t        i_dlstat_rx_lane_match(void *, void *);
 427 static void *           i_dlstat_rx_lane_stat_entry_diff(void *, void *);
 428 
 429 /* Tx lane specific functions */
 430 void *                  dlstat_tx_lane_stats(dladm_handle_t, datalink_id_t);
 431 static boolean_t        i_dlstat_tx_lane_match(void *, void *);
 432 static void *           i_dlstat_tx_lane_stat_entry_diff(void *, void *);
 433 
 434 /* Rx lane total specific functions */
 435 void *                  dlstat_rx_lane_total_stats(dladm_handle_t,
 436                             datalink_id_t);
 437 
 438 /* Tx lane total specific functions */
 439 void *                  dlstat_tx_lane_total_stats(dladm_handle_t,
 440                             datalink_id_t);
 441 
 442 /* Fanout specific functions */
 443 void *                  dlstat_fanout_stats(dladm_handle_t, datalink_id_t);
 444 static boolean_t        i_dlstat_fanout_match(void *, void *);
 445 static void *           i_dlstat_fanout_stat_entry_diff(void *, void *);
 446 
 447 /* Rx ring specific functions */
 448 void *                  dlstat_rx_ring_stats(dladm_handle_t, datalink_id_t);
 449 static boolean_t        i_dlstat_rx_ring_match(void *, void *);
 450 static void *           i_dlstat_rx_ring_stat_entry_diff(void *, void *);
 451 
 452 /* Tx ring specific functions */
 453 void *                  dlstat_tx_ring_stats(dladm_handle_t, datalink_id_t);
 454 static boolean_t        i_dlstat_tx_ring_match(void *, void *);
 455 static void *           i_dlstat_tx_ring_stat_entry_diff(void *, void *);
 456 
 457 /* Rx ring total specific functions */
 458 void *                  dlstat_rx_ring_total_stats(dladm_handle_t,
 459                             datalink_id_t);
 460 
 461 /* Tx ring total specific functions */
 462 void *                  dlstat_tx_ring_total_stats(dladm_handle_t,
 463                             datalink_id_t);
 464 
 465 /* Summary specific functions */
 466 void *                  dlstat_total_stats(dladm_handle_t, datalink_id_t);
 467 static boolean_t        i_dlstat_total_match(void *, void *);
 468 static void *           i_dlstat_total_stat_entry_diff(void *, void *);
 469 
 470 /* Aggr port specific functions */
 471 void *                  dlstat_aggr_port_stats(dladm_handle_t, datalink_id_t);
 472 static boolean_t        i_dlstat_aggr_port_match(void *, void *);
 473 static void *           i_dlstat_aggr_port_stat_entry_diff(void *, void *);
 474 
 475 /* Misc stat specific functions */
 476 void *                  dlstat_misc_stats(dladm_handle_t, datalink_id_t);
 477 
 478 typedef void *          dladm_stat_query_t(dladm_handle_t, datalink_id_t);
 479 typedef boolean_t       dladm_stat_match_t(void *, void *);
 480 typedef void *          dladm_stat_diff_t(void *, void *);
 481 
 482 typedef struct dladm_stat_desc_s {
 483         dladm_stat_type_t       ds_stattype;
 484         dladm_stat_query_t      *ds_querystat;
 485         dladm_stat_match_t      *ds_matchstat;
 486         dladm_stat_diff_t       *ds_diffstat;
 487         uint_t                  ds_offset;
 488         stat_info_t             *ds_statlist;
 489         uint_t                  ds_statsize;
 490 } dladm_stat_desc_t;
 491 
 492 /*
 493  * dladm_stat_table has one entry for each supported stat. ds_querystat returns
 494  * a chain of 'stat entries' for the queried stat.
 495  * Each stat entry has set of identifiers (ids) and an object containing actual
 496  * stat values. These stat entry objects are chained together in a linked list
 497  * of datatype dladm_stat_chain_t. Head of this list is returned to the caller
 498  * of dladm_link_stat_query.
 499  *
 500  * One node in the chain is shown below:
 501  *
 502  *      -------------------------
 503  *      | dc_statentry          |
 504  *      |    --------------     |
 505  *      |    |     ids     |    |
 506  *      |    --------------     |
 507  *      |    | stat fields |    |
 508  *      |    --------------     |
 509  *      -------------------------
 510  *      |      dc_next ---------|------> to next stat entry
 511  *      -------------------------
 512  *
 513  * In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
 514  * object of type rx_lane_stat_entry_t.
 515  *
 516  * dladm_link_stat_query_all returns similar chain. However, instead of storing
 517  * stat fields as raw numbers, it stores those as chain of <name, value> pairs.
 518  * The resulting structure is depicted below:
 519  *
 520  *      -------------------------
 521  *      | dc_statentry          |
 522  *      |    --------------     |   ---------------
 523  *      |    |  nv_header  |    |   |   name, val  |
 524  *      |    --------------     |   ---------------
 525  *      |    | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair
 526  *      |    --------------     |   ---------------
 527  *      -------------------------
 528  *      |      dc_next ---------|------> to next stat entry
 529  *      -------------------------
 530  */
 531 static dladm_stat_desc_t  dladm_stat_table[] = {
 532 { DLADM_STAT_RX_LANE,           dlstat_rx_lane_stats,
 533     i_dlstat_rx_lane_match,     i_dlstat_rx_lane_stat_entry_diff,
 534     offsetof(rx_lane_stat_entry_t, rle_stats),
 535     rx_lane_stats_list,         RX_LANE_STAT_SIZE},
 536 
 537 { DLADM_STAT_TX_LANE,           dlstat_tx_lane_stats,
 538     i_dlstat_tx_lane_match,     i_dlstat_tx_lane_stat_entry_diff,
 539     offsetof(tx_lane_stat_entry_t, tle_stats),
 540     tx_lane_stats_list,         TX_LANE_STAT_SIZE},
 541 
 542 { DLADM_STAT_RX_LANE_TOTAL,     dlstat_rx_lane_total_stats,
 543     i_dlstat_rx_lane_match,     i_dlstat_rx_lane_stat_entry_diff,
 544     offsetof(rx_lane_stat_entry_t, rle_stats),
 545     rx_lane_stats_list,         RX_LANE_STAT_SIZE},
 546 
 547 { DLADM_STAT_TX_LANE_TOTAL,     dlstat_tx_lane_total_stats,
 548     i_dlstat_tx_lane_match,     i_dlstat_tx_lane_stat_entry_diff,
 549     offsetof(tx_lane_stat_entry_t, tle_stats),
 550     tx_lane_stats_list,         TX_LANE_STAT_SIZE},
 551 
 552 { DLADM_STAT_RX_LANE_FOUT,      dlstat_fanout_stats,
 553     i_dlstat_fanout_match,      i_dlstat_fanout_stat_entry_diff,
 554     offsetof(fanout_stat_entry_t, fe_stats),
 555     fanout_stats_list,          FANOUT_STAT_SIZE},
 556 
 557 { DLADM_STAT_RX_RING,           dlstat_rx_ring_stats,
 558     i_dlstat_rx_ring_match,     i_dlstat_rx_ring_stat_entry_diff,
 559     offsetof(ring_stat_entry_t, re_stats),
 560     rx_ring_stats_list,         RX_RING_STAT_SIZE},
 561 
 562 { DLADM_STAT_TX_RING,           dlstat_tx_ring_stats,
 563     i_dlstat_tx_ring_match,     i_dlstat_tx_ring_stat_entry_diff,
 564     offsetof(ring_stat_entry_t, re_stats),
 565     tx_ring_stats_list,         TX_RING_STAT_SIZE},
 566 
 567 { DLADM_STAT_RX_RING_TOTAL,     dlstat_rx_ring_total_stats,
 568     i_dlstat_rx_ring_match,     i_dlstat_rx_ring_stat_entry_diff,
 569     offsetof(ring_stat_entry_t, re_stats),
 570     rx_ring_stats_list,         RX_RING_STAT_SIZE},
 571 
 572 { DLADM_STAT_TX_RING_TOTAL,     dlstat_tx_ring_total_stats,
 573     i_dlstat_tx_ring_match,     i_dlstat_tx_ring_stat_entry_diff,
 574     offsetof(ring_stat_entry_t, re_stats),
 575     tx_ring_stats_list,         TX_RING_STAT_SIZE},
 576 
 577 { DLADM_STAT_TOTAL,             dlstat_total_stats,
 578     i_dlstat_total_match,       i_dlstat_total_stat_entry_diff,
 579     offsetof(total_stat_entry_t, tse_stats),
 580     total_stats_list,           TOTAL_STAT_SIZE},
 581 
 582 { DLADM_STAT_AGGR_PORT,         dlstat_aggr_port_stats,
 583     i_dlstat_aggr_port_match,   i_dlstat_aggr_port_stat_entry_diff,
 584     offsetof(aggr_port_stat_entry_t, ape_stats),
 585     aggr_port_stats_list,       AGGR_PORT_STAT_SIZE},
 586 /*
 587  * We don't support -i <interval> query with misc stats. Several table fields
 588  * are left uninitialized thus.
 589  */
 590 { DLADM_STAT_MISC,              dlstat_misc_stats,
 591     NULL,                       NULL,
 592     0,
 593     misc_stats_list,            MISC_STAT_SIZE}
 594 };
 595 
 596 /* Internal functions */
 597 static void *
 598 dlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
 599 {
 600         return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2));
 601 }
 602 
 603 static boolean_t
 604 dlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
 605 {
 606         return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2));
 607 }
 608 
 609 /* Diff between two stats */
 610 static void
 611 i_dlstat_diff_stats(void *diff, void *op1, void *op2,
 612     stat_info_t stats_list[], uint_t size)
 613 {
 614         int     i;
 615 
 616         for (i = 0; i < size; i++) {
 617                 uint64_t *op1_val  = (void *)
 618                     ((uchar_t *)op1 + stats_list[i].si_offset);
 619                 uint64_t *op2_val = (void *)
 620                     ((uchar_t *)op2  + stats_list[i].si_offset);
 621                 uint64_t *diff_val = (void *)
 622                     ((uchar_t *)diff + stats_list[i].si_offset);
 623 
 624                 *diff_val = DIFF_STAT(*op1_val, *op2_val);
 625         }
 626 }
 627 
 628 /*
 629  * Perform diff = s1 - s2,  where diff, s1, s2 are structure objects of same
 630  * datatype. slist is list of offsets of the fields within the structure.
 631  */
 632 #define DLSTAT_DIFF_STAT(s1, s2, diff, f, slist, sz) {                  \
 633         if (s2 == NULL) {                                               \
 634                 bcopy(&s1->f, &diff->f, sizeof (s1->f));               \
 635         } else {                                                        \
 636                 i_dlstat_diff_stats(&diff->f, &s1->f,                     \
 637                     &s2->f, slist, sz);                                  \
 638         }                                                               \
 639 }
 640 
 641 /* Sum two stats */
 642 static void
 643 i_dlstat_sum_stats(void *sum, void *op1, void *op2,
 644     stat_info_t stats_list[], uint_t size)
 645 {
 646         int     i;
 647 
 648         for (i = 0; i < size; i++) {
 649                 uint64_t *op1_val = (void *)
 650                     ((uchar_t *)op1 + stats_list[i].si_offset);
 651                 uint64_t *op2_val = (void *)
 652                     ((uchar_t *)op2 + stats_list[i].si_offset);
 653                 uint64_t *sum_val = (void *)
 654                     ((uchar_t *)sum + stats_list[i].si_offset);
 655 
 656                 *sum_val =  *op1_val + *op2_val;
 657         }
 658 }
 659 
 660 /* Look up kstat value */
 661 static void
 662 i_dlstat_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, void *stats,
 663     stat_info_t stats_list[], uint_t size)
 664 {
 665         int     i;
 666 
 667         if (kstat_read(kcp, ksp, NULL) == -1)
 668                 return;
 669 
 670         for (i = 0; i < size; i++) {
 671                 uint64_t *val = (void *)
 672                     ((uchar_t *)stats + stats_list[i].si_offset);
 673 
 674                 if (dladm_kstat_value(ksp, stats_list[i].si_name,
 675                     KSTAT_DATA_UINT64, val) < 0)
 676                         return;
 677         }
 678 }
 679 
 680 /* Append linked list list1 to linked list list2 and return resulting list */
 681 static dladm_stat_chain_t *
 682 i_dlstat_join_lists(dladm_stat_chain_t *list1, dladm_stat_chain_t *list2)
 683 {
 684         dladm_stat_chain_t      *curr;
 685 
 686         if (list1 == NULL)
 687                 return (list2);
 688 
 689         /* list1 has at least one element, find last element in list1 */
 690         curr = list1;
 691         while (curr->dc_next != NULL)
 692                 curr = curr->dc_next;
 693 
 694         curr->dc_next = list2;
 695         return (list1);
 696 }
 697 
 698 uint_t default_idlist[] = {0};
 699 uint_t default_idlist_size = 1;
 700 
 701 typedef enum {
 702         DLSTAT_RX_RING_IDLIST,
 703         DLSTAT_TX_RING_IDLIST,
 704         DLSTAT_RX_HWLANE_IDLIST,
 705         DLSTAT_TX_HWLANE_IDLIST,
 706         DLSTAT_FANOUT_IDLIST
 707 } dlstat_idlist_type_t;
 708 
 709 void
 710 dladm_sort_index_list(uint_t idlist[], uint_t size)
 711 {
 712         int     i, j;
 713 
 714         for (j = 1; j < size; j++) {
 715                 int key = idlist[j];
 716                 for (i = j - 1; (i >= 0) && (idlist[i] > key); i--)
 717                         idlist[i + 1] = idlist[i];
 718                 idlist[i + 1] = key;
 719         }
 720 }
 721 
 722 /* Support for legacy drivers */
 723 void
 724 i_query_legacy_stats(dladm_handle_t dh, const char *linkname, pktsum_t *stats)
 725 {
 726         kstat_t         *ksp;
 727 
 728         bzero(stats, sizeof (*stats));
 729 
 730         if (dladm_dld_kcp(dh) == NULL)
 731                 return;
 732 
 733         ksp = dladm_kstat_lookup(dladm_dld_kcp(dh), "link", 0, linkname, NULL);
 734 
 735         if (ksp != NULL)
 736                 dladm_get_stats(dladm_dld_kcp(dh), ksp, stats);
 737 }
 738 
 739 void *
 740 i_dlstat_legacy_rx_lane_stats(dladm_handle_t dh, const char *linkname)
 741 {
 742         dladm_stat_chain_t      *head = NULL;
 743         pktsum_t                stats;
 744         rx_lane_stat_entry_t    *rx_lane_stat_entry;
 745 
 746         bzero(&stats, sizeof (pktsum_t));
 747 
 748         /* Query for dls stats */
 749         i_query_legacy_stats(dh, linkname, &stats);
 750 
 751         /* Convert to desired data type */
 752         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
 753         if (rx_lane_stat_entry == NULL)
 754                 goto done;
 755 
 756         rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
 757         rx_lane_stat_entry->rle_id = L_SWLANE;
 758 
 759         rx_lane_stat_entry->rle_stats.rl_ipackets = stats.ipackets;
 760         rx_lane_stat_entry->rle_stats.rl_intrs = stats.ipackets;
 761         rx_lane_stat_entry->rle_stats.rl_rbytes = stats.rbytes;
 762 
 763         /* Allocate memory for wrapper */
 764         head = malloc(sizeof (dladm_stat_chain_t));
 765         if (head == NULL) {
 766                 free(rx_lane_stat_entry);
 767                 goto done;
 768         }
 769 
 770         head->dc_statentry = rx_lane_stat_entry;
 771         head->dc_next = NULL;
 772 done:
 773         return (head);
 774 }
 775 
 776 void *
 777 i_dlstat_legacy_tx_lane_stats(dladm_handle_t dh, const char *linkname)
 778 {
 779         dladm_stat_chain_t      *head = NULL;
 780         pktsum_t                stats;
 781         tx_lane_stat_entry_t    *tx_lane_stat_entry;
 782 
 783         bzero(&stats, sizeof (pktsum_t));
 784 
 785         /* Query for dls stats */
 786         i_query_legacy_stats(dh, linkname, &stats);
 787 
 788         /* Convert to desired data type */
 789         tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
 790         if (tx_lane_stat_entry == NULL)
 791                 goto done;
 792 
 793         tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
 794         tx_lane_stat_entry->tle_id = L_SWLANE;
 795 
 796         tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets;
 797         tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes;
 798 
 799         /* Allocate memory for wrapper */
 800         head = malloc(sizeof (dladm_stat_chain_t));
 801         if (head == NULL) {
 802                 free(tx_lane_stat_entry);
 803                 goto done;
 804         }
 805 
 806         head->dc_statentry = tx_lane_stat_entry;
 807         head->dc_next = NULL;
 808 done:
 809         return (head);
 810 }
 811 
 812 /*
 813  * Ideally, we would want an ioctl to return list of ring-ids (or lane-ids)
 814  * for a given data-link (or mac client). We could then query for specific
 815  * kstats based on these ring-ids (lane-ids).
 816  * Ring-ids (or lane-ids) could be returned like any other link properties
 817  * queried by dladm show-linkprop. However, non-global zones do not have
 818  * access to this information today.
 819  * We thus opt for an implementation that relies heavily on kstat internals:
 820  * i_dlstat_*search routines and i_dlstat_get_idlist.
 821  */
 822 /* rx hwlane specific */
 823 static boolean_t
 824 i_dlstat_rx_hwlane_search(kstat_t *ksp)
 825 {
 826         return (ksp->ks_instance == 0 &&
 827             strstr(ksp->ks_name, "mac_rx") != 0 &&
 828             strstr(ksp->ks_name, "hwlane") != 0 &&
 829             strstr(ksp->ks_name, "fanout") == 0 &&
 830             strcmp(ksp->ks_class, "net") == 0);
 831 }
 832 
 833 /* tx hwlane specific */
 834 static boolean_t
 835 i_dlstat_tx_hwlane_search(kstat_t *ksp)
 836 {
 837         return (ksp->ks_instance == 0 &&
 838             strstr(ksp->ks_name, "mac_tx") != 0 &&
 839             strstr(ksp->ks_name, "hwlane") != 0 &&
 840             strcmp(ksp->ks_class, "net") == 0);
 841 }
 842 
 843 /* rx fanout specific */
 844 static boolean_t
 845 i_dlstat_fanout_search(kstat_t *ksp)
 846 {
 847         return (ksp->ks_instance == 0 &&
 848             strstr(ksp->ks_name, "mac_rx") != 0 &&
 849             strstr(ksp->ks_name, "swlane") != 0 &&
 850             strstr(ksp->ks_name, "fanout") != 0 &&
 851             strcmp(ksp->ks_class, "net") == 0);
 852 }
 853 
 854 /* rx ring specific */
 855 static boolean_t
 856 i_dlstat_rx_ring_search(kstat_t *ksp)
 857 {
 858         return (ksp->ks_instance == 0 &&
 859             strstr(ksp->ks_name, "mac_rx") != 0 &&
 860             strstr(ksp->ks_name, "ring") != 0 &&
 861             strcmp(ksp->ks_class, "net") == 0);
 862 }
 863 
 864 /* tx ring specific */
 865 static boolean_t
 866 i_dlstat_tx_ring_search(kstat_t *ksp)
 867 {
 868         return (ksp->ks_instance == 0) &&
 869             strstr(ksp->ks_name, "mac_tx") != 0 &&
 870             strstr(ksp->ks_name, "ring") != 0 &&
 871             strcmp(ksp->ks_class, "net") == 0;
 872 }
 873 
 874 typedef boolean_t       dladm_search_kstat_t(kstat_t *);
 875 typedef struct dladm_extract_idlist_s {
 876         dlstat_idlist_type_t    di_type;
 877         char                    *di_prefix;
 878         dladm_search_kstat_t    *di_searchkstat;
 879 } dladm_extract_idlist_t;
 880 
 881 static dladm_extract_idlist_t dladm_extract_idlist[] = {
 882 { DLSTAT_RX_RING_IDLIST,        DLSTAT_MAC_RX_RING,
 883     i_dlstat_rx_ring_search},
 884 { DLSTAT_TX_RING_IDLIST,        DLSTAT_MAC_TX_RING,
 885     i_dlstat_tx_ring_search},
 886 { DLSTAT_RX_HWLANE_IDLIST,      DLSTAT_MAC_RX_HWLANE,
 887     i_dlstat_rx_hwlane_search},
 888 { DLSTAT_TX_HWLANE_IDLIST,      DLSTAT_MAC_TX_HWLANE,
 889     i_dlstat_tx_hwlane_search},
 890 { DLSTAT_FANOUT_IDLIST,         DLSTAT_MAC_FANOUT,
 891     i_dlstat_fanout_search}
 892 };
 893 
 894 static void
 895 i_dlstat_get_idlist(dladm_handle_t handle, const char *modname,
 896     dlstat_idlist_type_t idlist_type,
 897     uint_t idlist[], uint_t *size)
 898 {
 899         kstat_ctl_t     *kcp = dladm_dld_kcp(handle);
 900         kstat_t         *ksp;
 901         char            *prefix;
 902         int             prefixlen;
 903         boolean_t       (*fptr_searchkstat)(kstat_t *);
 904 
 905         *size = 0;
 906 
 907         if (kcp == NULL) {
 908                 warn("kstat_open operation failed");
 909                 return;
 910         }
 911 
 912         prefix = dladm_extract_idlist[idlist_type].di_prefix;
 913         fptr_searchkstat = dladm_extract_idlist[idlist_type].di_searchkstat;
 914         prefixlen = strlen(prefix);
 915         for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
 916                 if ((strcmp(ksp->ks_module, modname) == 0) &&
 917                     fptr_searchkstat(ksp)) {
 918                         idlist[(*size)++] = atoi(&ksp->ks_name[prefixlen]);
 919                 }
 920         }
 921         dladm_sort_index_list(idlist, *size);
 922 }
 923 
 924 static dladm_stat_chain_t *
 925 i_dlstat_query_stats(dladm_handle_t handle, const char *modname,
 926     const char *prefix, uint_t idlist[], uint_t idlist_size,
 927     void * (*fn)(kstat_ctl_t *, kstat_t *, int))
 928 {
 929         kstat_t                 *ksp;
 930         char                    statname[MAXLINKNAMELEN];
 931         int                     i = 0;
 932         dladm_stat_chain_t      *head = NULL, *prev = NULL;
 933         dladm_stat_chain_t      *curr;
 934 
 935         if (dladm_dld_kcp(handle) == NULL) {
 936                 warn("kstat_open operation failed");
 937                 return (NULL);
 938         }
 939 
 940         for (i = 0; i < idlist_size; i++) {
 941                 uint_t  index = idlist[i];
 942 
 943                 (void) snprintf(statname, sizeof (statname), "%s%d", prefix,
 944                     index);
 945 
 946                 ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), modname, 0,
 947                     statname, NULL);
 948                 if (ksp == NULL)
 949                         continue;
 950 
 951                 curr = malloc(sizeof (dladm_stat_chain_t));
 952                 if (curr == NULL)
 953                         break;
 954 
 955                 curr->dc_statentry = fn(dladm_dld_kcp(handle), ksp, index);
 956                 if (curr->dc_statentry == NULL) {
 957                         free(curr);
 958                         break;
 959                 }
 960 
 961                 (void) strlcpy(curr->dc_statheader, statname,
 962                     sizeof (curr->dc_statheader));
 963                 curr->dc_next = NULL;
 964 
 965                 if (head == NULL)       /* First node */
 966                         head = curr;
 967                 else
 968                         prev->dc_next = curr;
 969 
 970                 prev = curr;
 971         }
 972 done:
 973         return (head);
 974 }
 975 
 976 static misc_stat_entry_t *
 977 i_dlstat_misc_stats(dladm_handle_t handle, const char *linkname)
 978 {
 979         kstat_t                 *ksp;
 980         misc_stat_entry_t       *misc_stat_entry = NULL;
 981 
 982         if (dladm_dld_kcp(handle) == NULL)
 983                 return (NULL);
 984 
 985         ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), linkname, 0,
 986             DLSTAT_MAC_MISC_STAT, NULL);
 987         if (ksp == NULL)
 988                 goto done;
 989 
 990         misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t));
 991         if (misc_stat_entry == NULL)
 992                 goto done;
 993 
 994         i_dlstat_get_stats(dladm_dld_kcp(handle), ksp,
 995             &misc_stat_entry->mse_stats,
 996             misc_stats_list, MISC_STAT_SIZE);
 997 done:
 998         return (misc_stat_entry);
 999 }
1000 
1001 /* Rx lane statistic specific functions */
1002 static boolean_t
1003 i_dlstat_rx_lane_match(void *arg1, void *arg2)
1004 {
1005         rx_lane_stat_entry_t *s1 = arg1;
1006         rx_lane_stat_entry_t *s2 = arg2;
1007 
1008         return (s1->rle_index == s2->rle_index &&
1009             s1->rle_id == s2->rle_id);
1010 }
1011 
1012 static void *
1013 i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
1014 {
1015         rx_lane_stat_entry_t *s1 = arg1;
1016         rx_lane_stat_entry_t *s2 = arg2;
1017         rx_lane_stat_entry_t *diff_entry;
1018 
1019         diff_entry = malloc(sizeof (rx_lane_stat_entry_t));
1020         if (diff_entry == NULL)
1021                 goto done;
1022 
1023         diff_entry->rle_index = s1->rle_index;
1024         diff_entry->rle_id = s1->rle_id;
1025 
1026         DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
1027             RX_LANE_STAT_SIZE);
1028 
1029 done:
1030         return (diff_entry);
1031 }
1032 
1033 static void *
1034 i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1035 {
1036         rx_lane_stat_entry_t    *rx_lane_stat_entry;
1037 
1038         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1039         if (rx_lane_stat_entry == NULL)
1040                 goto done;
1041 
1042         rx_lane_stat_entry->rle_index = i;
1043         rx_lane_stat_entry->rle_id = L_HWLANE;
1044 
1045         i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1046             rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
1047 
1048 done:
1049         return (rx_lane_stat_entry);
1050 }
1051 
1052 /*ARGSUSED*/
1053 static void *
1054 i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1055 {
1056         rx_lane_stat_entry_t    *rx_lane_stat_entry;
1057 
1058         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1059         if (rx_lane_stat_entry == NULL)
1060                 goto done;
1061 
1062         rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1063         rx_lane_stat_entry->rle_id = L_SWLANE;
1064 
1065         i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1066             rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1067 
1068         rx_lane_stat_entry->rle_stats.rl_ipackets =
1069             rx_lane_stat_entry->rle_stats.rl_intrs;
1070         rx_lane_stat_entry->rle_stats.rl_rbytes =
1071             rx_lane_stat_entry->rle_stats.rl_intrbytes;
1072 done:
1073         return (rx_lane_stat_entry);
1074 }
1075 
1076 /*ARGSUSED*/
1077 static void *
1078 i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1079 {
1080         rx_lane_stat_entry_t    *local_stat_entry;
1081         rx_lane_stat_entry_t    *rx_lane_stat_entry;
1082 
1083         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1084         if (rx_lane_stat_entry == NULL)
1085                 goto done;
1086 
1087         local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1088         if (local_stat_entry == NULL)
1089                 goto done;
1090 
1091         local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1092         local_stat_entry->rle_id = L_LOCAL;
1093 
1094         i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1095             rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1096 
1097         local_stat_entry->rle_stats.rl_ipackets =
1098             rx_lane_stat_entry->rle_stats.rl_lclpackets;
1099         local_stat_entry->rle_stats.rl_rbytes =
1100             rx_lane_stat_entry->rle_stats.rl_lclbytes;
1101 
1102 done:
1103         free(rx_lane_stat_entry);
1104         return (local_stat_entry);
1105 }
1106 
1107 static dladm_stat_chain_t *
1108 i_dlstat_rx_local_stats(dladm_handle_t handle, const char *linkname)
1109 {
1110         dladm_stat_chain_t      *local_stats = NULL;
1111 
1112         local_stats = i_dlstat_query_stats(handle, linkname,
1113             DLSTAT_MAC_RX_SWLANE,
1114             default_idlist, default_idlist_size,
1115             i_dlstat_rx_local_retrieve_stat);
1116 
1117         if (local_stats != NULL) {
1118                 (void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
1119                     sizeof (local_stats->dc_statheader));
1120         }
1121         return (local_stats);
1122 }
1123 
1124 static dladm_stat_chain_t *
1125 i_dlstat_rx_bcast_stats(dladm_handle_t handle, const char *linkname)
1126 {
1127         misc_stat_entry_t       *misc_stat_entry;
1128         dladm_stat_chain_t      *head = NULL;
1129         rx_lane_stat_entry_t    *rx_lane_stat_entry;
1130 
1131         misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1132         if (misc_stat_entry == NULL)
1133                 goto done;
1134 
1135         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1136         if (rx_lane_stat_entry == NULL)
1137                 goto done;
1138 
1139         rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1140         rx_lane_stat_entry->rle_id = L_BCAST;
1141 
1142         rx_lane_stat_entry->rle_stats.rl_ipackets =
1143             misc_stat_entry->mse_stats.ms_brdcstrcv +
1144             misc_stat_entry->mse_stats.ms_multircv;
1145         rx_lane_stat_entry->rle_stats.rl_intrs =
1146             misc_stat_entry->mse_stats.ms_brdcstrcv +
1147             misc_stat_entry->mse_stats.ms_multircv;
1148         rx_lane_stat_entry->rle_stats.rl_rbytes =
1149             misc_stat_entry->mse_stats.ms_brdcstrcvbytes +
1150             misc_stat_entry->mse_stats.ms_multircvbytes;
1151 
1152         head = malloc(sizeof (dladm_stat_chain_t));
1153         if (head == NULL) {
1154                 free(rx_lane_stat_entry);
1155                 goto done;
1156         }
1157 
1158         head->dc_statentry = rx_lane_stat_entry;
1159         head->dc_next = NULL;
1160 
1161         free(misc_stat_entry);
1162 done:
1163         return (head);
1164 }
1165 
1166 static dladm_stat_chain_t *
1167 i_dlstat_rx_defunctlane_stats(dladm_handle_t handle, const char *linkname)
1168 {
1169         misc_stat_entry_t       *misc_stat_entry;
1170         dladm_stat_chain_t      *head = NULL;
1171         rx_lane_stat_entry_t    *rx_lane_stat_entry;
1172 
1173         misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1174         if (misc_stat_entry == NULL)
1175                 goto done;
1176 
1177         rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1178         if (rx_lane_stat_entry == NULL)
1179                 goto done;
1180 
1181         rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1182         rx_lane_stat_entry->rle_id = L_DFNCT;
1183 
1184         rx_lane_stat_entry->rle_stats.rl_ipackets =
1185             misc_stat_entry->mse_stats.ms_ipackets;
1186         rx_lane_stat_entry->rle_stats.rl_rbytes =
1187             misc_stat_entry->mse_stats.ms_rbytes;
1188         rx_lane_stat_entry->rle_stats.rl_intrs =
1189             misc_stat_entry->mse_stats.ms_intrs;
1190         rx_lane_stat_entry->rle_stats.rl_polls =
1191             misc_stat_entry->mse_stats.ms_polls;
1192         rx_lane_stat_entry->rle_stats.rl_sdrops =
1193             misc_stat_entry->mse_stats.ms_rxsdrops;
1194         rx_lane_stat_entry->rle_stats.rl_chl10 =
1195             misc_stat_entry->mse_stats.ms_chainunder10;
1196         rx_lane_stat_entry->rle_stats.rl_ch10_50 =
1197             misc_stat_entry->mse_stats.ms_chain10to50;
1198         rx_lane_stat_entry->rle_stats.rl_chg50 =
1199             misc_stat_entry->mse_stats.ms_chainover50;
1200 
1201         head = malloc(sizeof (dladm_stat_chain_t));
1202         if (head == NULL) {
1203                 free(rx_lane_stat_entry);
1204                 goto done;
1205         }
1206 
1207         head->dc_statentry = rx_lane_stat_entry;
1208         head->dc_next = NULL;
1209 
1210 done:
1211         return (head);
1212 }
1213 
1214 static dladm_stat_chain_t *
1215 i_dlstat_rx_hwlane_stats(dladm_handle_t handle, const char *linkname)
1216 {
1217         uint_t  rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1218         uint_t  rx_hwlane_idlist_size;
1219 
1220         i_dlstat_get_idlist(handle, linkname, DLSTAT_RX_HWLANE_IDLIST,
1221             rx_hwlane_idlist, &rx_hwlane_idlist_size);
1222 
1223         return (i_dlstat_query_stats(handle, linkname, DLSTAT_MAC_RX_HWLANE,
1224             rx_hwlane_idlist, rx_hwlane_idlist_size,
1225             i_dlstat_rx_hwlane_retrieve_stat));
1226 }
1227 
1228 /*ARGSUSED*/
1229 static dladm_stat_chain_t *
1230 i_dlstat_rx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1231     const char *linkname)
1232 {
1233         return (i_dlstat_query_stats(dh, linkname, DLSTAT_MAC_RX_SWLANE,
1234             default_idlist, default_idlist_size,
1235             i_dlstat_rx_swlane_retrieve_stat));
1236 }
1237 
1238 void *
1239 dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1240 {
1241         dladm_stat_chain_t      *head = NULL;
1242         dladm_stat_chain_t      *local_stats = NULL;
1243         dladm_stat_chain_t      *bcast_stats = NULL;
1244         dladm_stat_chain_t      *defunctlane_stats = NULL;
1245         dladm_stat_chain_t      *lane_stats = NULL;
1246         char                    linkname[MAXLINKNAMELEN];
1247         boolean_t               is_legacy_driver;
1248 
1249         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1250             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1251                 goto done;
1252         }
1253 
1254         /* Check if it is legacy driver */
1255         if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1256             "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1257                 goto done;
1258         }
1259 
1260         if (is_legacy_driver) {
1261                 head = i_dlstat_legacy_rx_lane_stats(dh, linkname);
1262                 goto done;
1263         }
1264 
1265         local_stats = i_dlstat_rx_local_stats(dh, linkname);
1266         bcast_stats = i_dlstat_rx_bcast_stats(dh, linkname);
1267         defunctlane_stats = i_dlstat_rx_defunctlane_stats(dh, linkname);
1268         lane_stats = i_dlstat_rx_hwlane_stats(dh, linkname);
1269         if (lane_stats == NULL)
1270                 lane_stats = i_dlstat_rx_swlane_stats(dh, linkid, linkname);
1271 
1272         head = i_dlstat_join_lists(local_stats, bcast_stats);
1273         head = i_dlstat_join_lists(head, defunctlane_stats);
1274         head = i_dlstat_join_lists(head, lane_stats);
1275 done:
1276         return (head);
1277 }
1278 
1279 /* Tx lane statistic specific functions */
1280 static boolean_t
1281 i_dlstat_tx_lane_match(void *arg1, void *arg2)
1282 {
1283         tx_lane_stat_entry_t *s1 = arg1;
1284         tx_lane_stat_entry_t *s2 = arg2;
1285 
1286         return (s1->tle_index == s2->tle_index &&
1287             s1->tle_id == s2->tle_id);
1288 }
1289 
1290 static void *
1291 i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
1292 {
1293         tx_lane_stat_entry_t *s1 = arg1;
1294         tx_lane_stat_entry_t *s2 = arg2;
1295         tx_lane_stat_entry_t *diff_entry;
1296 
1297         diff_entry = malloc(sizeof (tx_lane_stat_entry_t));
1298         if (diff_entry == NULL)
1299                 goto done;
1300 
1301         diff_entry->tle_index = s1->tle_index;
1302         diff_entry->tle_id = s1->tle_id;
1303 
1304         DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
1305             TX_LANE_STAT_SIZE);
1306 
1307 done:
1308         return (diff_entry);
1309 }
1310 
1311 static void *
1312 i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1313 {
1314         tx_lane_stat_entry_t    *tx_lane_stat_entry;
1315 
1316         tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1317         if (tx_lane_stat_entry == NULL)
1318                 goto done;
1319 
1320         tx_lane_stat_entry->tle_index        = i;
1321         tx_lane_stat_entry->tle_id   = L_HWLANE;
1322 
1323         i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1324             tx_lane_stats_list, TX_LANE_STAT_SIZE);
1325 
1326 done:
1327         return (tx_lane_stat_entry);
1328 }
1329 
1330 /*ARGSUSED*/
1331 static void *
1332 i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1333 {
1334         tx_lane_stat_entry_t    *tx_lane_stat_entry;
1335 
1336         tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1337         if (tx_lane_stat_entry == NULL)
1338                 goto done;
1339 
1340         tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1341         tx_lane_stat_entry->tle_id = L_SWLANE;
1342 
1343         i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1344             tx_lane_stats_list, TX_LANE_STAT_SIZE);
1345 
1346 done:
1347         return (tx_lane_stat_entry);
1348 }
1349 
1350 static dladm_stat_chain_t *
1351 i_dlstat_tx_bcast_stats(dladm_handle_t handle, const char *linkname)
1352 {
1353         misc_stat_entry_t       *misc_stat_entry;
1354         dladm_stat_chain_t      *head = NULL;
1355         tx_lane_stat_entry_t    *tx_lane_stat_entry;
1356 
1357         misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1358         if (misc_stat_entry == NULL)
1359                 goto done;
1360 
1361         tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1362         if (tx_lane_stat_entry == NULL)
1363                 goto done;
1364 
1365         tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1366         tx_lane_stat_entry->tle_id = L_BCAST;
1367 
1368         tx_lane_stat_entry->tle_stats.tl_opackets =
1369             misc_stat_entry->mse_stats.ms_brdcstxmt +
1370             misc_stat_entry->mse_stats.ms_multixmt;
1371 
1372         tx_lane_stat_entry->tle_stats.tl_obytes =
1373             misc_stat_entry->mse_stats.ms_brdcstxmtbytes +
1374             misc_stat_entry->mse_stats.ms_multixmtbytes;
1375 
1376         head = malloc(sizeof (dladm_stat_chain_t));
1377         if (head == NULL) {
1378                 free(tx_lane_stat_entry);
1379                 goto done;
1380         }
1381 
1382         head->dc_statentry = tx_lane_stat_entry;
1383         head->dc_next = NULL;
1384 
1385         free(misc_stat_entry);
1386 done:
1387         return (head);
1388 }
1389 
1390 static dladm_stat_chain_t *
1391 i_dlstat_tx_defunctlane_stats(dladm_handle_t handle, const char *linkname)
1392 {
1393         misc_stat_entry_t       *misc_stat_entry;
1394         dladm_stat_chain_t      *head = NULL;
1395         tx_lane_stat_entry_t    *tx_lane_stat_entry;
1396 
1397         misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1398         if (misc_stat_entry == NULL)
1399                 goto done;
1400 
1401         tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1402         if (tx_lane_stat_entry == NULL)
1403                 goto done;
1404 
1405         tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1406         tx_lane_stat_entry->tle_id = L_DFNCT;
1407 
1408         tx_lane_stat_entry->tle_stats.tl_opackets =
1409             misc_stat_entry->mse_stats.ms_opackets;
1410         tx_lane_stat_entry->tle_stats.tl_obytes =
1411             misc_stat_entry->mse_stats.ms_obytes;
1412         tx_lane_stat_entry->tle_stats.tl_sdrops =
1413             misc_stat_entry->mse_stats.ms_txsdrops;
1414 
1415         head = malloc(sizeof (dladm_stat_chain_t));
1416         if (head == NULL) {
1417                 free(tx_lane_stat_entry);
1418                 goto done;
1419         }
1420 
1421         head->dc_statentry = tx_lane_stat_entry;
1422         head->dc_next = NULL;
1423 
1424 done:
1425         return (head);
1426 }
1427 
1428 static dladm_stat_chain_t *
1429 i_dlstat_tx_hwlane_stats(dladm_handle_t handle, const char *linkname)
1430 {
1431         uint_t  tx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1432         uint_t  tx_hwlane_idlist_size;
1433 
1434         i_dlstat_get_idlist(handle, linkname, DLSTAT_TX_HWLANE_IDLIST,
1435             tx_hwlane_idlist, &tx_hwlane_idlist_size);
1436 
1437         return (i_dlstat_query_stats(handle, linkname, DLSTAT_MAC_TX_HWLANE,
1438             tx_hwlane_idlist, tx_hwlane_idlist_size,
1439             i_dlstat_tx_hwlane_retrieve_stat));
1440 }
1441 
1442 /*ARGSUSED*/
1443 static dladm_stat_chain_t *
1444 i_dlstat_tx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1445     const char *linkname)
1446 {
1447         return (i_dlstat_query_stats(dh, linkname, DLSTAT_MAC_TX_SWLANE,
1448             default_idlist, default_idlist_size,
1449             i_dlstat_tx_swlane_retrieve_stat));
1450 }
1451 
1452 void *
1453 dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1454 {
1455         dladm_stat_chain_t      *head = NULL;
1456         dladm_stat_chain_t      *bcast_stats = NULL;
1457         dladm_stat_chain_t      *defunctlane_stats = NULL;
1458         dladm_stat_chain_t      *lane_stats;
1459         char                    linkname[MAXLINKNAMELEN];
1460         boolean_t               is_legacy_driver;
1461 
1462         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1463             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1464                 goto done;
1465         }
1466 
1467         /* Check if it is legacy driver */
1468         if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1469             "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1470                 goto done;
1471         }
1472 
1473         if (is_legacy_driver) {
1474                 head = i_dlstat_legacy_tx_lane_stats(dh, linkname);
1475                 goto done;
1476         }
1477 
1478         bcast_stats = i_dlstat_tx_bcast_stats(dh, linkname);
1479         defunctlane_stats = i_dlstat_tx_defunctlane_stats(dh, linkname);
1480         lane_stats = i_dlstat_tx_hwlane_stats(dh, linkname);
1481         if (lane_stats == NULL)
1482                 lane_stats = i_dlstat_tx_swlane_stats(dh, linkid, linkname);
1483 
1484         head = i_dlstat_join_lists(bcast_stats, defunctlane_stats);
1485         head = i_dlstat_join_lists(head, lane_stats);
1486 
1487 done:
1488         return (head);
1489 }
1490 
1491 /* Rx lane total statistic specific functions */
1492 void *
1493 dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1494 {
1495         dladm_stat_chain_t      *total_head = NULL;
1496         dladm_stat_chain_t      *rx_lane_head, *curr;
1497         rx_lane_stat_entry_t    *total_stats;
1498 
1499         /* Get per rx lane stats */
1500         rx_lane_head = dlstat_rx_lane_stats(dh, linkid);
1501         if (rx_lane_head == NULL)
1502                 goto done;
1503 
1504         total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
1505         if (total_stats == NULL)
1506                 goto done;
1507 
1508         total_stats->rle_index = DLSTAT_INVALID_ENTRY;
1509         total_stats->rle_id = DLSTAT_INVALID_ENTRY;
1510 
1511         for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) {
1512                 rx_lane_stat_entry_t    *curr_lane_stats = curr->dc_statentry;
1513 
1514                 i_dlstat_sum_stats(&total_stats->rle_stats,
1515                     &curr_lane_stats->rle_stats, &total_stats->rle_stats,
1516                     rx_lane_stats_list, RX_LANE_STAT_SIZE);
1517         }
1518 
1519         total_head = malloc(sizeof (dladm_stat_chain_t));
1520         if (total_head == NULL) {
1521                 free(total_stats);
1522                 goto done;
1523         }
1524 
1525         total_head->dc_statentry = total_stats;
1526         (void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total",
1527             sizeof (total_head->dc_statheader));
1528         total_head->dc_next = NULL;
1529         free(rx_lane_head);
1530 
1531 done:
1532         return (total_head);
1533 }
1534 
1535 /* Tx lane total statistic specific functions */
1536 void *
1537 dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1538 {
1539         dladm_stat_chain_t      *total_head = NULL;
1540         dladm_stat_chain_t      *tx_lane_head, *curr;
1541         tx_lane_stat_entry_t    *total_stats;
1542 
1543         /* Get per tx lane stats */
1544         tx_lane_head = dlstat_tx_lane_stats(dh, linkid);
1545         if (tx_lane_head == NULL)
1546                 goto done;
1547 
1548         total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
1549         if (total_stats == NULL)
1550                 goto done;
1551 
1552         total_stats->tle_index = DLSTAT_INVALID_ENTRY;
1553         total_stats->tle_id = DLSTAT_INVALID_ENTRY;
1554 
1555         for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) {
1556                 tx_lane_stat_entry_t    *curr_lane_stats = curr->dc_statentry;
1557 
1558                 i_dlstat_sum_stats(&total_stats->tle_stats,
1559                     &curr_lane_stats->tle_stats, &total_stats->tle_stats,
1560                     tx_lane_stats_list, TX_LANE_STAT_SIZE);
1561         }
1562 
1563         total_head = malloc(sizeof (dladm_stat_chain_t));
1564         if (total_head == NULL) {
1565                 free(total_stats);
1566                 goto done;
1567         }
1568 
1569         total_head->dc_statentry = total_stats;
1570         (void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total",
1571             sizeof (total_head->dc_statheader));
1572         total_head->dc_next = NULL;
1573         free(tx_lane_head);
1574 
1575 done:
1576         return (total_head);
1577 }
1578 
1579 /* Fanout specific functions */
1580 static boolean_t
1581 i_dlstat_fanout_match(void *arg1, void *arg2)
1582 {
1583         fanout_stat_entry_t     *s1 = arg1;
1584         fanout_stat_entry_t     *s2 = arg2;
1585 
1586         return (s1->fe_index == s2->fe_index &&
1587             s1->fe_id == s2->fe_id &&
1588             s1->fe_foutindex == s2->fe_foutindex);
1589 }
1590 
1591 static void *
1592 i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2)
1593 {
1594         fanout_stat_entry_t     *s1 = arg1;
1595         fanout_stat_entry_t     *s2 = arg2;
1596         fanout_stat_entry_t     *diff_entry;
1597 
1598         diff_entry = malloc(sizeof (fanout_stat_entry_t));
1599         if (diff_entry == NULL)
1600                 goto done;
1601 
1602         diff_entry->fe_index = s1->fe_index;
1603         diff_entry->fe_id = s1->fe_id;
1604         diff_entry->fe_foutindex = s1->fe_foutindex;
1605 
1606         DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list,
1607             FANOUT_STAT_SIZE);
1608 
1609 done:
1610         return (diff_entry);
1611 }
1612 
1613 static void *
1614 i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1615 {
1616         fanout_stat_entry_t     *fanout_stat_entry;
1617 
1618         fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t));
1619         if (fanout_stat_entry == NULL)
1620                 goto done;
1621 
1622                                         /* Set by the caller later */
1623         fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY;
1624         fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY;
1625 
1626         fanout_stat_entry->fe_foutindex = i;
1627 
1628         i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats,
1629             fanout_stats_list, FANOUT_STAT_SIZE);
1630 
1631 done:
1632         return (fanout_stat_entry);
1633 }
1634 
1635 static void *
1636 i_dlstat_query_fanout_stats(dladm_handle_t dh, datalink_id_t linkid,
1637     uint_t idlist[], uint_t idlist_size,
1638     const char *modname, const char *prefix)
1639 {
1640         int                     i;
1641         char                    statprefix[MAXLINKNAMELEN];
1642         char                    linkname[MAXLINKNAMELEN];
1643         dladm_stat_chain_t      *curr, *curr_head;
1644         dladm_stat_chain_t      *head = NULL, *prev = NULL;
1645         uint_t                  fanout_idlist[MAX_RINGS_PER_GROUP];
1646         uint_t                  fanout_idlist_size;
1647 
1648         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1649             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1650                 return (NULL);
1651         }
1652 
1653         i_dlstat_get_idlist(dh, linkname, DLSTAT_FANOUT_IDLIST,
1654             fanout_idlist, &fanout_idlist_size);
1655 
1656         for (i = 0; i < idlist_size; i++) {
1657                 uint_t  index = idlist[i];
1658 
1659                 (void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout",
1660                     prefix, index);
1661 
1662                 curr_head = i_dlstat_query_stats(dh, modname, statprefix,
1663                     fanout_idlist, fanout_idlist_size,
1664                     i_dlstat_fanout_retrieve_stat);
1665 
1666                 if (curr_head == NULL)  /* Last lane */
1667                         break;
1668 
1669                 if (head == NULL)       /* First lane */
1670                         head = curr_head;
1671                 else    /* Link new lane list to end of previous lane list */
1672                         prev->dc_next = curr_head;
1673 
1674                 /* Walk new lane list and set ids */
1675                 for (curr = curr_head; curr != NULL; curr = curr->dc_next) {
1676                         fanout_stat_entry_t *curr_stats = curr->dc_statentry;
1677 
1678                         curr_stats->fe_index = index;
1679                         curr_stats->fe_id = L_HWLANE;
1680                         /*
1681                          * Save last pointer of previous linked list.
1682                          * This pointer is used to chain linked lists
1683                          * generated in each iteration.
1684                          */
1685                         prev = curr;
1686                 }
1687         }
1688 
1689         return (head);
1690 }
1691 
1692 void *
1693 dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid,
1694     const char *linkname)
1695 {
1696         return (i_dlstat_query_fanout_stats(dh, linkid,
1697             default_idlist, default_idlist_size, linkname,
1698             DLSTAT_MAC_RX_SWLANE));
1699 }
1700 
1701 void *
1702 dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1703     const char *linkname)
1704 {
1705         uint_t  rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1706         uint_t  rx_hwlane_idlist_size;
1707 
1708         i_dlstat_get_idlist(dh, linkname, DLSTAT_RX_HWLANE_IDLIST,
1709             rx_hwlane_idlist, &rx_hwlane_idlist_size);
1710 
1711         return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist,
1712             rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE));
1713 }
1714 
1715 void *
1716 dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid)
1717 {
1718         dladm_stat_chain_t      *head = NULL;
1719         dladm_stat_chain_t      *fout_hwlane_stats;
1720         dladm_stat_chain_t      *fout_swlane_and_local_stats;
1721         fanout_stat_entry_t     *fout_stats;
1722         char                    linkname[MAXLINKNAMELEN];
1723 
1724         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1725             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1726                 goto done;
1727         }
1728 
1729         fout_swlane_and_local_stats =
1730             dlstat_fanout_swlane_and_local_stats(dh, linkid, linkname);
1731         fout_hwlane_stats = dlstat_fanout_hwlane_stats(dh, linkid, linkname);
1732 
1733         if (fout_swlane_and_local_stats == NULL) {
1734                 head = fout_hwlane_stats;
1735                 goto done;
1736         }
1737 
1738         fout_stats = fout_swlane_and_local_stats->dc_statentry;
1739 
1740         if (fout_hwlane_stats != NULL) { /* hwlane(s), only local traffic */
1741                 fout_stats->fe_id = L_LOCAL;
1742                 fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1743         } else { /* no hwlane, mix of local+sw classified */
1744                 fout_stats->fe_id = L_LCLSWLANE;
1745                 fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1746         }
1747 
1748         fout_swlane_and_local_stats->dc_next = fout_hwlane_stats;
1749         head = fout_swlane_and_local_stats;
1750 
1751 done:
1752         return (head);
1753 }
1754 
1755 /* Rx ring statistic specific functions */
1756 static boolean_t
1757 i_dlstat_rx_ring_match(void *arg1, void *arg2)
1758 {
1759         rx_lane_stat_entry_t    *s1 = arg1;
1760         rx_lane_stat_entry_t    *s2 = arg2;
1761 
1762         return (s1->rle_index == s2->rle_index);
1763 }
1764 
1765 static void *
1766 i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
1767 {
1768         ring_stat_entry_t       *s1 = arg1;
1769         ring_stat_entry_t       *s2 = arg2;
1770         ring_stat_entry_t       *diff_entry;
1771 
1772         diff_entry = malloc(sizeof (ring_stat_entry_t));
1773         if (diff_entry == NULL)
1774                 goto done;
1775 
1776         diff_entry->re_index = s1->re_index;
1777 
1778         DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
1779             RX_RING_STAT_SIZE);
1780 
1781 done:
1782         return (diff_entry);
1783 }
1784 
1785 static void *
1786 i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1787 {
1788         ring_stat_entry_t       *rx_ring_stat_entry;
1789 
1790         rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1791         if (rx_ring_stat_entry == NULL)
1792                 goto done;
1793 
1794         rx_ring_stat_entry->re_index = i;
1795 
1796         i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
1797             rx_ring_stats_list, RX_RING_STAT_SIZE);
1798 
1799 done:
1800         return (rx_ring_stat_entry);
1801 }
1802 
1803 void *
1804 dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1805 {
1806         uint_t                  rx_ring_idlist[MAX_RINGS_PER_GROUP];
1807         uint_t                  rx_ring_idlist_size;
1808         dladm_phys_attr_t       dpa;
1809         char                    linkname[MAXLINKNAMELEN];
1810         char                    *modname;
1811         datalink_class_t        class;
1812 
1813         /*
1814          * kstats corresponding to physical device rings continue to use
1815          * device names even if the link is renamed using dladm rename-link.
1816          * Thus, given a linkid, we lookup the physical device name.
1817          * However, if an aggr is renamed, kstats corresponding to its
1818          * pseudo rings are renamed as well.
1819          */
1820         if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1821             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1822                 return (NULL);
1823         }
1824 
1825         if (class != DATALINK_CLASS_AGGR) {
1826                 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1827                     DLADM_STATUS_OK) {
1828                         return (NULL);
1829                 }
1830                 modname = dpa.dp_dev;
1831         } else
1832                 modname = linkname;
1833 
1834         i_dlstat_get_idlist(dh, modname, DLSTAT_RX_RING_IDLIST,
1835             rx_ring_idlist, &rx_ring_idlist_size);
1836 
1837         return (i_dlstat_query_stats(dh, modname, DLSTAT_MAC_RX_RING,
1838             rx_ring_idlist, rx_ring_idlist_size,
1839             i_dlstat_rx_ring_retrieve_stat));
1840 }
1841 
1842 /* Tx ring statistic specific functions */
1843 static boolean_t
1844 i_dlstat_tx_ring_match(void *arg1, void *arg2)
1845 {
1846         tx_lane_stat_entry_t    *s1 = arg1;
1847         tx_lane_stat_entry_t    *s2 = arg2;
1848 
1849         return (s1->tle_index == s2->tle_index);
1850 }
1851 
1852 static void *
1853 i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
1854 {
1855         ring_stat_entry_t       *s1 = arg1;
1856         ring_stat_entry_t       *s2 = arg2;
1857         ring_stat_entry_t       *diff_entry;
1858 
1859         diff_entry = malloc(sizeof (ring_stat_entry_t));
1860         if (diff_entry == NULL)
1861                 goto done;
1862 
1863         diff_entry->re_index = s1->re_index;
1864 
1865         DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
1866             TX_RING_STAT_SIZE);
1867 
1868 done:
1869         return (diff_entry);
1870 }
1871 
1872 static void *
1873 i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1874 {
1875         ring_stat_entry_t       *tx_ring_stat_entry;
1876 
1877         tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1878         if (tx_ring_stat_entry == NULL)
1879                 goto done;
1880 
1881         tx_ring_stat_entry->re_index = i;
1882 
1883         i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
1884             tx_ring_stats_list, TX_RING_STAT_SIZE);
1885 
1886 done:
1887         return (tx_ring_stat_entry);
1888 }
1889 
1890 void *
1891 dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1892 {
1893         uint_t                  tx_ring_idlist[MAX_RINGS_PER_GROUP];
1894         uint_t                  tx_ring_idlist_size;
1895         dladm_phys_attr_t       dpa;
1896         char                    linkname[MAXLINKNAMELEN];
1897         char                    *modname;
1898         datalink_class_t        class;
1899 
1900         /*
1901          * kstats corresponding to physical device rings continue to use
1902          * device names even if the link is renamed using dladm rename-link.
1903          * Thus, given a linkid, we lookup the physical device name.
1904          * However, if an aggr is renamed, kstats corresponding to its
1905          * pseudo rings are renamed as well.
1906          */
1907         if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1908             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1909                 return (NULL);
1910         }
1911 
1912         if (class != DATALINK_CLASS_AGGR) {
1913                 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1914                     DLADM_STATUS_OK) {
1915                         return (NULL);
1916                 }
1917                 modname = dpa.dp_dev;
1918         } else
1919                 modname = linkname;
1920 
1921         i_dlstat_get_idlist(dh, modname, DLSTAT_TX_RING_IDLIST,
1922             tx_ring_idlist, &tx_ring_idlist_size);
1923 
1924         return (i_dlstat_query_stats(dh, modname, DLSTAT_MAC_TX_RING,
1925             tx_ring_idlist, tx_ring_idlist_size,
1926             i_dlstat_tx_ring_retrieve_stat));
1927 }
1928 
1929 /* Rx ring total statistic specific functions */
1930 void *
1931 dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1932 {
1933         dladm_stat_chain_t      *total_head = NULL;
1934         dladm_stat_chain_t      *rx_ring_head, *curr;
1935         ring_stat_entry_t       *total_stats;
1936 
1937         /* Get per rx ring stats */
1938         rx_ring_head = dlstat_rx_ring_stats(dh, linkid);
1939         if (rx_ring_head == NULL)
1940                 goto done;
1941 
1942         total_stats = calloc(1, sizeof (ring_stat_entry_t));
1943         if (total_stats == NULL)
1944                 goto done;
1945 
1946         total_stats->re_index = DLSTAT_INVALID_ENTRY;
1947 
1948         for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) {
1949                 ring_stat_entry_t       *curr_ring_stats = curr->dc_statentry;
1950 
1951                 i_dlstat_sum_stats(&total_stats->re_stats,
1952                     &curr_ring_stats->re_stats, &total_stats->re_stats,
1953                     rx_ring_stats_list, RX_RING_STAT_SIZE);
1954         }
1955 
1956         total_head = malloc(sizeof (dladm_stat_chain_t));
1957         if (total_head == NULL) {
1958                 free(total_stats);
1959                 goto done;
1960         }
1961 
1962         total_head->dc_statentry = total_stats;
1963         (void) strlcpy(total_head->dc_statheader, "mac_rx_ring_total",
1964             sizeof (total_head->dc_statheader));
1965         total_head->dc_next = NULL;
1966         free(rx_ring_head);
1967 
1968 done:
1969         return (total_head);
1970 }
1971 
1972 /* Tx ring total statistic specific functions */
1973 void *
1974 dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1975 {
1976         dladm_stat_chain_t      *total_head = NULL;
1977         dladm_stat_chain_t      *tx_ring_head, *curr;
1978         ring_stat_entry_t       *total_stats;
1979 
1980         /* Get per tx ring stats */
1981         tx_ring_head = dlstat_tx_ring_stats(dh, linkid);
1982         if (tx_ring_head == NULL)
1983                 goto done;
1984 
1985         total_stats = calloc(1, sizeof (ring_stat_entry_t));
1986         if (total_stats == NULL)
1987                 goto done;
1988 
1989         total_stats->re_index = DLSTAT_INVALID_ENTRY;
1990 
1991         for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) {
1992                 ring_stat_entry_t       *curr_ring_stats = curr->dc_statentry;
1993 
1994                 i_dlstat_sum_stats(&total_stats->re_stats,
1995                     &curr_ring_stats->re_stats, &total_stats->re_stats,
1996                     tx_ring_stats_list, TX_RING_STAT_SIZE);
1997         }
1998 
1999         total_head = malloc(sizeof (dladm_stat_chain_t));
2000         if (total_head == NULL) {
2001                 free(total_stats);
2002                 goto done;
2003         }
2004 
2005         total_head->dc_statentry = total_stats;
2006         (void) strlcpy(total_head->dc_statheader, "mac_tx_ring_total",
2007             sizeof (total_head->dc_statheader));
2008         total_head->dc_next = NULL;
2009         free(tx_ring_head);
2010 
2011 done:
2012         return (total_head);
2013 }
2014 
2015 /* Summary statistic specific functions */
2016 /*ARGSUSED*/
2017 static boolean_t
2018 i_dlstat_total_match(void *arg1, void *arg2)
2019 {
2020         /* Always single entry for total */
2021         return (B_TRUE);
2022 }
2023 
2024 static void *
2025 i_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
2026 {
2027         total_stat_entry_t      *s1 = arg1;
2028         total_stat_entry_t      *s2 = arg2;
2029         total_stat_entry_t      *diff_entry;
2030 
2031         diff_entry = malloc(sizeof (total_stat_entry_t));
2032         if (diff_entry == NULL)
2033                 goto done;
2034 
2035         DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
2036             TOTAL_STAT_SIZE);
2037 
2038 done:
2039         return (diff_entry);
2040 }
2041 
2042 void *
2043 dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2044 {
2045         dladm_stat_chain_t      *head = NULL;
2046         dladm_stat_chain_t      *rx_total;
2047         dladm_stat_chain_t      *tx_total;
2048         total_stat_entry_t      *total_stat_entry;
2049         rx_lane_stat_entry_t    *rx_lane_stat_entry;
2050         tx_lane_stat_entry_t    *tx_lane_stat_entry;
2051 
2052         /* Get total rx lane stats */
2053         rx_total = dlstat_rx_lane_total_stats(dh, linkid);
2054         if (rx_total == NULL)
2055                 goto done;
2056 
2057         /* Get total tx lane stats */
2058         tx_total = dlstat_tx_lane_total_stats(dh, linkid);
2059         if (tx_total == NULL)
2060                 goto done;
2061 
2062         /* Build total stat */
2063         total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2064         if (total_stat_entry == NULL)
2065                 goto done;
2066 
2067         rx_lane_stat_entry = rx_total->dc_statentry;
2068         tx_lane_stat_entry = tx_total->dc_statentry;
2069 
2070         /* Extract total rx ipackets, rbytes */
2071         total_stat_entry->tse_stats.ts_ipackets =
2072             rx_lane_stat_entry->rle_stats.rl_ipackets;
2073         total_stat_entry->tse_stats.ts_rbytes =
2074             rx_lane_stat_entry->rle_stats.rl_rbytes;
2075 
2076         /* Extract total tx opackets, obytes */
2077         total_stat_entry->tse_stats.ts_opackets =
2078             tx_lane_stat_entry->tle_stats.tl_opackets;
2079         total_stat_entry->tse_stats.ts_obytes =
2080             tx_lane_stat_entry->tle_stats.tl_obytes;
2081 
2082         head = malloc(sizeof (dladm_stat_chain_t));
2083         if (head == NULL) {
2084                 free(total_stat_entry);
2085                 goto done;
2086         }
2087 
2088         head->dc_statentry = total_stat_entry;
2089         (void) strlcpy(head->dc_statheader, "mac_lane_total",
2090             sizeof (head->dc_statheader));
2091         head->dc_next = NULL;
2092         free(rx_total);
2093         free(tx_total);
2094 
2095 done:
2096         return (head);
2097 }
2098 
2099 /* Aggr total statistic(summed across all component ports) specific functions */
2100 void *
2101 dlstat_aggr_total_stats(dladm_stat_chain_t *head)
2102 {
2103         dladm_stat_chain_t      *curr;
2104         dladm_stat_chain_t      *total_head;
2105         aggr_port_stat_entry_t  *total_stats;
2106 
2107         total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
2108         if (total_stats == NULL)
2109                 goto done;
2110 
2111         total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
2112 
2113         for (curr = head; curr != NULL; curr = curr->dc_next) {
2114                 aggr_port_stat_entry_t  *curr_aggr_port_stats;
2115 
2116                 curr_aggr_port_stats = curr->dc_statentry;
2117 
2118                 i_dlstat_sum_stats(&total_stats->ape_stats,
2119                     &curr_aggr_port_stats->ape_stats, &total_stats->ape_stats,
2120                     aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2121         }
2122 
2123         total_head = malloc(sizeof (dladm_stat_chain_t));
2124         if (total_head == NULL) {
2125                 free(total_stats);
2126                 goto done;
2127         }
2128 
2129         total_head->dc_statentry = total_stats;
2130         total_head->dc_next = NULL;
2131 
2132 done:
2133         return (total_head);
2134 }
2135 
2136 /* Aggr port statistic specific functions */
2137 static boolean_t
2138 i_dlstat_aggr_port_match(void *arg1, void *arg2)
2139 {
2140         aggr_port_stat_entry_t *s1 = arg1;
2141         aggr_port_stat_entry_t *s2 = arg2;
2142 
2143         return (s1->ape_portlinkid == s2->ape_portlinkid);
2144 }
2145 
2146 static void *
2147 i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
2148 {
2149         aggr_port_stat_entry_t  *s1 = arg1;
2150         aggr_port_stat_entry_t  *s2 = arg2;
2151         aggr_port_stat_entry_t  *diff_entry;
2152 
2153         diff_entry = malloc(sizeof (aggr_port_stat_entry_t));
2154         if (diff_entry == NULL)
2155                 goto done;
2156 
2157         diff_entry->ape_portlinkid = s1->ape_portlinkid;
2158 
2159         DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
2160             AGGR_PORT_STAT_SIZE);
2161 
2162 done:
2163         return (diff_entry);
2164 }
2165 
2166 /*
2167  * Query dls stats for the aggr port. This results in query for stats into
2168  * the corresponding device driver.
2169  */
2170 static aggr_port_stat_entry_t *
2171 i_dlstat_single_port_stats(dladm_handle_t handle, const char *portname,
2172     datalink_id_t linkid)
2173 {
2174         kstat_t                 *ksp;
2175         char                    module[DLPI_LINKNAME_MAX];
2176         uint_t                  instance;
2177         aggr_port_stat_entry_t  *aggr_port_stat_entry = NULL;
2178 
2179         if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK)
2180                 goto done;
2181 
2182         if (dladm_dld_kcp(handle) == NULL) {
2183                 warn("kstat open operation failed");
2184                 return (NULL);
2185         }
2186 
2187         ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), module, instance,
2188         "mac", NULL);
2189         if (ksp == NULL)
2190                 goto done;
2191 
2192         aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2193         if (aggr_port_stat_entry == NULL)
2194                 goto done;
2195 
2196         /* Save port's linkid */
2197         aggr_port_stat_entry->ape_portlinkid = linkid;
2198 
2199         i_dlstat_get_stats(dladm_dld_kcp(handle), ksp,
2200             &aggr_port_stat_entry->ape_stats,
2201             aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2202 done:
2203         return (aggr_port_stat_entry);
2204 }
2205 
2206 void *
2207 dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
2208 {
2209         dladm_aggr_grp_attr_t   ginfo;
2210         int                     i;
2211         dladm_aggr_port_attr_t   *portp;
2212         dladm_phys_attr_t       dpa;
2213         aggr_port_stat_entry_t  *aggr_port_stat_entry;
2214         dladm_stat_chain_t      *head = NULL, *prev = NULL, *curr;
2215         dladm_stat_chain_t      *total_stats;
2216 
2217         /* Get aggr info */
2218         bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
2219         if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE)
2220             != DLADM_STATUS_OK)
2221                 goto done;
2222         /* For every port that is member of this aggr do */
2223         for (i = 0; i < ginfo.lg_nports; i++) {
2224                 portp = &(ginfo.lg_ports[i]);
2225                 if (dladm_phys_info(dh, portp->lp_linkid, &dpa,
2226                     DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
2227                         goto done;
2228                 }
2229 
2230                 aggr_port_stat_entry = i_dlstat_single_port_stats(dh,
2231                     dpa.dp_dev, portp->lp_linkid);
2232 
2233                 /* Create dladm_stat_chain_t object for this stat */
2234                 curr = malloc(sizeof (dladm_stat_chain_t));
2235                 if (curr == NULL) {
2236                         free(aggr_port_stat_entry);
2237                         goto done;
2238                 }
2239                 (void) strlcpy(curr->dc_statheader, dpa.dp_dev,
2240                     sizeof (curr->dc_statheader));
2241                 curr->dc_statentry = aggr_port_stat_entry;
2242                 curr->dc_next = NULL;
2243 
2244                 /* Chain this aggr port stat entry */
2245                 /* head of the stat list */
2246                 if (prev == NULL)
2247                         head = curr;
2248                 else
2249                         prev->dc_next = curr;
2250                 prev = curr;
2251         }
2252 
2253         /*
2254          * Prepend the stat list with cumulative aggr stats i.e. summed over all
2255          * component ports
2256          */
2257         total_stats = dlstat_aggr_total_stats(head);
2258         if (total_stats != NULL) {
2259                 total_stats->dc_next = head;
2260                 head = total_stats;
2261         }
2262 
2263 done:
2264         free(ginfo.lg_ports);
2265         return (head);
2266 }
2267 
2268 /* Misc stat specific functions */
2269 void *
2270 dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid)
2271 {
2272         misc_stat_entry_t       *misc_stat_entry;
2273         dladm_stat_chain_t      *head = NULL;
2274         char                    linkname[MAXLINKNAMELEN];
2275 
2276         if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2277             DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2278                 goto done;
2279         }
2280 
2281         misc_stat_entry = i_dlstat_misc_stats(dh, linkname);
2282         if (misc_stat_entry == NULL)
2283                 goto done;
2284 
2285         head = malloc(sizeof (dladm_stat_chain_t));
2286         if (head == NULL) {
2287                 free(misc_stat_entry);
2288                 goto done;
2289         }
2290 
2291         head->dc_statentry = misc_stat_entry;
2292         (void) strlcpy(head->dc_statheader, "mac_misc_stat",
2293             sizeof (head->dc_statheader));
2294         head->dc_next = NULL;
2295 
2296 done:
2297         return (head);
2298 }
2299 
2300 /* Exported functions */
2301 dladm_stat_chain_t *
2302 dladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid,
2303     dladm_stat_type_t stattype)
2304 {
2305         return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
2306 }
2307 
2308 dladm_stat_chain_t *
2309 dladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2,
2310     dladm_stat_type_t stattype)
2311 {
2312         dladm_stat_chain_t      *op1_curr, *op2_curr;
2313         dladm_stat_chain_t      *diff_curr;
2314         dladm_stat_chain_t      *diff_prev = NULL, *diff_head = NULL;
2315 
2316                                 /* Perform op1 - op2, store result in diff */
2317         for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) {
2318                 for (op2_curr = op2; op2_curr != NULL;
2319                     op2_curr = op2_curr->dc_next) {
2320                         if (dlstat_match_stats(op1_curr->dc_statentry,
2321                             op2_curr->dc_statentry, stattype)) {
2322                                 break;
2323                         }
2324                 }
2325                 diff_curr = malloc(sizeof (dladm_stat_chain_t));
2326                 if (diff_curr == NULL)
2327                         goto done;
2328 
2329                 diff_curr->dc_next = NULL;
2330 
2331                 if (op2_curr == NULL) {
2332                         /* prev iteration did not have this stat entry */
2333                         diff_curr->dc_statentry =
2334                             dlstat_diff_stats(op1_curr->dc_statentry,
2335                             NULL, stattype);
2336                 } else {
2337                         diff_curr->dc_statentry =
2338                             dlstat_diff_stats(op1_curr->dc_statentry,
2339                             op2_curr->dc_statentry, stattype);
2340                 }
2341 
2342                 if (diff_curr->dc_statentry == NULL) {
2343                         free(diff_curr);
2344                         goto done;
2345                 }
2346 
2347                 if (diff_prev == NULL) /* head of the diff stat list */
2348                         diff_head = diff_curr;
2349                 else
2350                         diff_prev->dc_next = diff_curr;
2351                 diff_prev = diff_curr;
2352         }
2353 done:
2354         return (diff_head);
2355 }
2356 
2357 void
2358 dladm_link_stat_free(dladm_stat_chain_t *curr)
2359 {
2360         while (curr != NULL) {
2361                 dladm_stat_chain_t      *tofree = curr;
2362 
2363                 curr = curr->dc_next;
2364                 free(tofree->dc_statentry);
2365                 free(tofree);
2366         }
2367 }
2368 
2369 /* Query all link stats */
2370 static name_value_stat_t *
2371 i_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size)
2372 {
2373         int                     i;
2374         name_value_stat_t       *head_stat = NULL, *prev_stat = NULL;
2375         name_value_stat_t       *curr_stat;
2376 
2377         for (i = 0; i < size; i++) {
2378                 uint64_t *val = (void *)
2379                     ((uchar_t *)stats + stats_list[i].si_offset);
2380 
2381                 curr_stat = calloc(1, sizeof (name_value_stat_t));
2382                 if (curr_stat == NULL)
2383                         break;
2384 
2385                 (void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name,
2386                     sizeof (curr_stat->nv_statname));
2387                 curr_stat->nv_statval = *val;
2388                 curr_stat->nv_nextstat = NULL;
2389 
2390                 if (head_stat == NULL)  /* First node */
2391                         head_stat = curr_stat;
2392                 else
2393                         prev_stat->nv_nextstat = curr_stat;
2394 
2395                 prev_stat = curr_stat;
2396         }
2397         return (head_stat);
2398 }
2399 
2400 void *
2401 build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
2402 {
2403         name_value_stat_entry_t *name_value_stat_entry;
2404         dladm_stat_desc_t       *stattbl_ptr;
2405         void                    *statfields;
2406 
2407         stattbl_ptr = &dladm_stat_table[stattype];
2408 
2409         /* Allocate memory for query all stat entry */
2410         name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2411         if (name_value_stat_entry == NULL)
2412                 goto done;
2413 
2414         /* Header for these stat fields */
2415         (void) strlcpy(name_value_stat_entry->nve_header, statheader,
2416             sizeof (name_value_stat_entry->nve_header));
2417 
2418         /* Extract stat fields from the statentry */
2419         statfields = (uchar_t *)statentry +
2420             dladm_stat_table[stattype].ds_offset;
2421 
2422         /* Convert curr_stat to <statname, statval> pair */
2423         name_value_stat_entry->nve_stats =
2424             i_dlstat_convert_stats(statfields,
2425             stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize);
2426 done:
2427         return (name_value_stat_entry);
2428 }
2429 
2430 void *
2431 i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
2432 {
2433         dladm_stat_chain_t      *curr;
2434         dladm_stat_chain_t      *nvstat_head = NULL, *nvstat_prev = NULL;
2435         dladm_stat_chain_t      *nvstat_curr;
2436 
2437         /*
2438          * For every stat in the chain, build header and convert all
2439          * its stat fields
2440          */
2441         for (curr = stat_head; curr != NULL; curr = curr->dc_next) {
2442                 nvstat_curr = malloc(sizeof (dladm_stat_chain_t));
2443                 if (nvstat_curr == NULL)
2444                         break;
2445 
2446                 nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
2447                     curr->dc_statentry, stattype);
2448 
2449                 if (nvstat_curr->dc_statentry == NULL) {
2450                         free(nvstat_curr);
2451                         break;
2452                 }
2453 
2454                 nvstat_curr->dc_next = NULL;
2455 
2456                 if (nvstat_head == NULL)        /* First node */
2457                         nvstat_head = nvstat_curr;
2458                 else
2459                         nvstat_prev->dc_next = nvstat_curr;
2460 
2461                 nvstat_prev = nvstat_curr;
2462         }
2463 done:
2464         return (nvstat_head);
2465 }
2466 
2467 dladm_stat_chain_t *
2468 dladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid,
2469     dladm_stat_type_t stattype)
2470 {
2471         dladm_stat_chain_t      *stat_head;
2472         dladm_stat_chain_t      *nvstat_head = NULL;
2473 
2474         /* Query the requested stat */
2475         stat_head = dladm_link_stat_query(dh, linkid, stattype);
2476         if (stat_head == NULL)
2477                 goto done;
2478 
2479         /*
2480          * Convert every statfield in every stat-entry of stat chain to
2481          * <statname, statval> pair
2482          */
2483         nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
2484 
2485         /* Free stat_head */
2486         dladm_link_stat_free(stat_head);
2487 
2488 done:
2489         return (nvstat_head);
2490 }
2491 
2492 void
2493 dladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
2494 {
2495         while (curr != NULL) {
2496                 dladm_stat_chain_t      *tofree = curr;
2497                 name_value_stat_entry_t *nv_entry = curr->dc_statentry;
2498                 name_value_stat_t       *nv_curr = nv_entry->nve_stats;
2499 
2500                 while (nv_curr != NULL) {
2501                         name_value_stat_t       *nv_tofree = nv_curr;
2502 
2503                         nv_curr = nv_curr->nv_nextstat;
2504                         free(nv_tofree);
2505                 }
2506 
2507                 curr = curr->dc_next;
2508                 free(nv_entry);
2509                 free(tofree);
2510         }
2511 }
2512 
2513 /* flow stats specific routines */
2514 flow_stat_t *
2515 dladm_flow_stat_query(dladm_handle_t handle, const char *flowname)
2516 {
2517         kstat_t         *ksp;
2518         flow_stat_t     *flow_stat = NULL;
2519 
2520         if (dladm_dld_kcp(handle) == NULL)
2521                 return (NULL);
2522 
2523         flow_stat = calloc(1, sizeof (flow_stat_t));
2524         if (flow_stat == NULL)
2525                 goto done;
2526 
2527         ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), NULL, -1, flowname,
2528         "flow");
2529 
2530         if (ksp != NULL) {
2531                 i_dlstat_get_stats(dladm_dld_kcp(handle), ksp, flow_stat,
2532                     flow_stats_list, FLOW_STAT_SIZE);
2533         }
2534 
2535 done:
2536         return (flow_stat);
2537 }
2538 
2539 flow_stat_t *
2540 dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
2541 {
2542         flow_stat_t     *diff_stat;
2543 
2544         diff_stat = calloc(1, sizeof (flow_stat_t));
2545         if (diff_stat == NULL)
2546                 goto done;
2547 
2548         if (op2 == NULL) {
2549                 bcopy(op1, diff_stat, sizeof (flow_stat_t));
2550         } else {
2551                 i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list,
2552                     FLOW_STAT_SIZE);
2553         }
2554 done:
2555         return (diff_stat);
2556 }
2557 
2558 void
2559 dladm_flow_stat_free(flow_stat_t *curr)
2560 {
2561         free(curr);
2562 }
2563 
2564 /* Query all flow stats */
2565 name_value_stat_entry_t *
2566 dladm_flow_stat_query_all(dladm_handle_t handle, const char *flowname)
2567 {
2568         flow_stat_t             *flow_stat;
2569         name_value_stat_entry_t *name_value_stat_entry = NULL;
2570 
2571         /* Query flow stats */
2572         flow_stat =  dladm_flow_stat_query(handle, flowname);
2573         if (flow_stat == NULL)
2574                 goto done;
2575 
2576         /* Allocate memory for query all stat entry */
2577         name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2578         if (name_value_stat_entry == NULL) {
2579                 dladm_flow_stat_free(flow_stat);
2580                 goto done;
2581         }
2582 
2583         /* Header for these stat fields */
2584         (void) strncpy(name_value_stat_entry->nve_header, flowname,
2585             MAXFLOWNAMELEN);
2586 
2587         /* Convert every statfield in flow_stat to <statname, statval> pair */
2588         name_value_stat_entry->nve_stats =
2589             i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE);
2590 
2591         /* Free flow_stat */
2592         dladm_flow_stat_free(flow_stat);
2593 
2594 done:
2595         return (name_value_stat_entry);
2596 }
2597 
2598 void
2599 dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
2600 {
2601         name_value_stat_t       *nv_curr = curr->nve_stats;
2602 
2603         while (nv_curr != NULL) {
2604                 name_value_stat_t       *nv_tofree = nv_curr;
2605 
2606                 nv_curr = nv_curr->nv_nextstat;
2607                 free(nv_tofree);
2608         }
2609 }