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