6659 nvlist_free(NULL) is a no-op
[illumos-gate.git] / usr / src / lib / libdladm / common / libdlstat.c
bloba4f9307bd7c7e044665dbcfd7e06bb56c0cffea7
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
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>
40 #include <libdllink.h>
41 #include <libdlflow.h>
42 #include <libdlstat.h>
43 #include <libdlaggr.h>
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;
57 pktsum_t totalstats;
58 struct flowlist *stattable = NULL;
60 #define STATGROWSIZE 16
62 /* Exported functions */
65 * dladm_kstat_lookup() is a modified version of kstat_lookup which
66 * adds the class as a selector.
68 kstat_t *
69 dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
70 const char *name, const char *class)
72 kstat_t *ksp = NULL;
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);
82 errno = ENOENT;
83 return (NULL);
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.
91 void
92 dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
95 if (kstat_read(kcp, ksp, NULL) == -1)
96 return;
98 stats->snaptime = gethrtime();
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;
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;
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;
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;
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;
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;
144 dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
146 kstat_named_t *knp;
148 if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
149 return (-1);
151 if (knp->data_type != type)
152 return (-1);
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);
165 return (0);
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)
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;
181 if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
182 &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
183 return (status);
185 if (media != DL_ETHER)
186 return (DLADM_STATUS_LINKINVAL);
188 status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
190 if (status != DLADM_STATUS_OK)
191 return (status);
193 status = dladm_parselink(dpap.dp_dev, module, &instance);
195 if (status != DLADM_STATUS_OK)
196 return (status);
198 if ((kcp = kstat_open()) == NULL) {
199 warn("kstat_open operation failed");
200 return (-1);
204 * The kstat query could fail if the underlying MAC
205 * driver was already detached.
207 if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL &&
208 (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL)
209 goto bail;
211 if (kstat_read(kcp, ksp, NULL) == -1)
212 goto bail;
214 if (dladm_kstat_value(ksp, name, type, val) < 0)
215 goto bail;
217 (void) kstat_close(kcp);
218 return (DLADM_STATUS_OK);
220 bail:
221 (void) kstat_close(kcp);
222 return (dladm_errno2status(errno));
225 /* Compute sum of 2 pktsums (s1 = s2 + s3) */
226 void
227 dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
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;
238 #define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
241 /* Compute differences between 2 pktsums (s1 = s2 - s3) */
242 void
243 dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
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);
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"
263 typedef struct {
264 const char *si_name;
265 uint_t si_offset;
266 } stat_info_t;
268 #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
270 /* Definitions for rx lane stats */
271 #define RL_OFF(f) (offsetof(rx_lane_stat_t, f))
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)}
285 #define RX_HWLANE_STAT_SIZE A_CNT(rx_hwlane_stats_list)
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)}
296 #define RX_SWLANE_STAT_SIZE A_CNT(rx_swlane_stats_list)
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)}
312 #define RX_LANE_STAT_SIZE A_CNT(rx_lane_stats_list)
314 /* Definitions for tx lane stats */
315 #define TL_OFF(f) (offsetof(tx_lane_stat_t, f))
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)}
324 #define TX_LANE_STAT_SIZE A_CNT(tx_lane_stats_list)
326 /* Definitions for tx/rx misc stats */
327 #define M_OFF(f) (offsetof(misc_stat_t, f))
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)}
361 #define MISC_STAT_SIZE A_CNT(misc_stats_list)
363 /* Definitions for rx ring stats */
364 #define R_OFF(f) (offsetof(ring_stat_t, f))
366 static stat_info_t rx_ring_stats_list[] = {
367 {"ipackets", R_OFF(r_packets)},
368 {"rbytes", R_OFF(r_bytes)}
370 #define RX_RING_STAT_SIZE A_CNT(rx_ring_stats_list)
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)}
377 #define TX_RING_STAT_SIZE A_CNT(tx_ring_stats_list)
379 /* Definitions for fanout stats */
380 #define F_OFF(f) (offsetof(fanout_stat_t, f))
382 static stat_info_t fanout_stats_list[] = {
383 {"ipackets", F_OFF(f_ipackets)},
384 {"rbytes", F_OFF(f_rbytes)},
386 #define FANOUT_STAT_SIZE A_CNT(fanout_stats_list)
388 /* Definitions for total stats */
389 #define T_OFF(f) (offsetof(total_stat_t, f))
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)}
397 #define TOTAL_STAT_SIZE A_CNT(total_stats_list)
399 /* Definitions for aggr stats */
400 #define AP_OFF(f) (offsetof(aggr_port_stat_t, f))
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)}
408 #define AGGR_PORT_STAT_SIZE A_CNT(aggr_port_stats_list)
410 /* Definitions for flow stats */
411 #define FL_OFF(f) (offsetof(flow_stat_t, f))
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)}
419 #define FLOW_STAT_SIZE A_CNT(flow_stats_list)
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 *);
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 *);
431 /* Rx lane total specific functions */
432 void * dlstat_rx_lane_total_stats(dladm_handle_t,
433 datalink_id_t);
435 /* Tx lane total specific functions */
436 void * dlstat_tx_lane_total_stats(dladm_handle_t,
437 datalink_id_t);
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 *);
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 *);
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 *);
454 /* Rx ring total specific functions */
455 void * dlstat_rx_ring_total_stats(dladm_handle_t,
456 datalink_id_t);
458 /* Tx ring total specific functions */
459 void * dlstat_tx_ring_total_stats(dladm_handle_t,
460 datalink_id_t);
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 *);
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 *);
472 /* Misc stat specific functions */
473 void * dlstat_misc_stats(dladm_handle_t, datalink_id_t);
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 *);
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;
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.
497 * One node in the chain is shown below:
499 * -------------------------
500 * | dc_statentry |
501 * | -------------- |
502 * | | ids | |
503 * | -------------- |
504 * | | stat fields | |
505 * | -------------- |
506 * -------------------------
507 * | dc_next ---------|------> to next stat entry
508 * -------------------------
510 * In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
511 * object of type rx_lane_stat_entry_t.
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:
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 * -------------------------
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},
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},
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},
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},
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},
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},
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},
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},
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},
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},
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},
584 * We don't support -i <interval> query with misc stats. Several table fields
585 * are left uninitialized thus.
587 { DLADM_STAT_MISC, dlstat_misc_stats,
588 NULL, NULL,
590 misc_stats_list, MISC_STAT_SIZE}
593 /* Internal functions */
594 static void *
595 dlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
597 return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2));
600 static boolean_t
601 dlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
603 return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2));
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)
611 int i;
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);
621 *diff_val = DIFF_STAT(*op1_val, *op2_val);
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.
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); \
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)
643 int i;
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);
653 *sum_val = *op1_val + *op2_val;
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)
662 int i;
664 if (kstat_read(kcp, ksp, NULL) == -1)
665 return;
667 for (i = 0; i < size; i++) {
668 uint64_t *val = (void *)
669 ((uchar_t *)stats + stats_list[i].si_offset);
671 if (dladm_kstat_value(ksp, stats_list[i].si_name,
672 KSTAT_DATA_UINT64, val) < 0)
673 return;
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)
681 dladm_stat_chain_t *curr;
683 if (list1 == NULL)
684 return (list2);
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;
691 curr->dc_next = list2;
692 return (list1);
695 uint_t default_idlist[] = {0};
696 uint_t default_idlist_size = 1;
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;
706 void
707 dladm_sort_index_list(uint_t idlist[], uint_t size)
709 int i, j;
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;
719 /* Support for legacy drivers */
720 void
721 i_query_legacy_stats(const char *linkname, pktsum_t *stats)
723 kstat_ctl_t *kcp;
724 kstat_t *ksp;
726 bzero(stats, sizeof (*stats));
728 if ((kcp = kstat_open()) == NULL)
729 return;
731 ksp = dladm_kstat_lookup(kcp, "link", 0, linkname, NULL);
733 if (ksp != NULL)
734 dladm_get_stats(kcp, ksp, stats);
736 (void) kstat_close(kcp);
739 void *
740 i_dlstat_legacy_rx_lane_stats(const char *linkname)
742 dladm_stat_chain_t *head = NULL;
743 pktsum_t stats;
744 rx_lane_stat_entry_t *rx_lane_stat_entry;
746 bzero(&stats, sizeof (pktsum_t));
748 /* Query for dls stats */
749 i_query_legacy_stats(linkname, &stats);
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;
756 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
757 rx_lane_stat_entry->rle_id = L_SWLANE;
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;
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;
770 head->dc_statentry = rx_lane_stat_entry;
771 head->dc_next = NULL;
772 done:
773 return (head);
776 void *
777 i_dlstat_legacy_tx_lane_stats(const char *linkname)
779 dladm_stat_chain_t *head = NULL;
780 pktsum_t stats;
781 tx_lane_stat_entry_t *tx_lane_stat_entry;
783 bzero(&stats, sizeof (pktsum_t));
785 /* Query for dls stats */
786 i_query_legacy_stats(linkname, &stats);
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;
793 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
794 tx_lane_stat_entry->tle_id = L_SWLANE;
796 tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets;
797 tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes;
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;
806 head->dc_statentry = tx_lane_stat_entry;
807 head->dc_next = NULL;
808 done:
809 return (head);
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.
822 /* rx hwlane specific */
823 static boolean_t
824 i_dlstat_rx_hwlane_search(kstat_t *ksp)
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);
833 /* tx hwlane specific */
834 static boolean_t
835 i_dlstat_tx_hwlane_search(kstat_t *ksp)
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);
843 /* rx fanout specific */
844 static boolean_t
845 i_dlstat_fanout_search(kstat_t *ksp)
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);
854 /* rx ring specific */
855 static boolean_t
856 i_dlstat_rx_ring_search(kstat_t *ksp)
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);
864 /* tx ring specific */
865 static boolean_t
866 i_dlstat_tx_ring_search(kstat_t *ksp)
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;
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;
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}
894 static void
895 i_dlstat_get_idlist(const char *modname, dlstat_idlist_type_t idlist_type,
896 uint_t idlist[], uint_t *size)
898 kstat_ctl_t *kcp;
899 kstat_t *ksp;
900 char *prefix;
901 int prefixlen;
902 boolean_t (*fptr_searchkstat)(kstat_t *);
904 *size = 0;
906 if ((kcp = kstat_open()) == NULL) {
907 warn("kstat_open operation failed");
908 goto done;
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]);
920 dladm_sort_index_list(idlist, *size);
922 done:
923 (void) kstat_close(kcp);
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))
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;
938 if ((kcp = kstat_open()) == NULL) {
939 warn("kstat_open operation failed");
940 return (NULL);
943 for (i = 0; i < idlist_size; i++) {
944 uint_t index = idlist[i];
946 (void) snprintf(statname, sizeof (statname), "%s%d", prefix,
947 index);
949 ksp = dladm_kstat_lookup(kcp, modname, 0, statname, NULL);
950 if (ksp == NULL)
951 continue;
953 curr = malloc(sizeof (dladm_stat_chain_t));
954 if (curr == NULL)
955 break;
957 curr->dc_statentry = fn(kcp, ksp, index);
958 if (curr->dc_statentry == NULL) {
959 free(curr);
960 break;
963 (void) strlcpy(curr->dc_statheader, statname,
964 sizeof (curr->dc_statheader));
965 curr->dc_next = NULL;
967 if (head == NULL) /* First node */
968 head = curr;
969 else
970 prev->dc_next = curr;
972 prev = curr;
974 done:
975 (void) kstat_close(kcp);
976 return (head);
979 static misc_stat_entry_t *
980 i_dlstat_misc_stats(const char *linkname)
982 kstat_ctl_t *kcp;
983 kstat_t *ksp;
984 misc_stat_entry_t *misc_stat_entry = NULL;
986 if ((kcp = kstat_open()) == NULL)
987 return (NULL);
989 ksp = dladm_kstat_lookup(kcp, linkname, 0, DLSTAT_MAC_MISC_STAT, NULL);
990 if (ksp == NULL)
991 goto done;
993 misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t));
994 if (misc_stat_entry == NULL)
995 goto done;
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);
1004 /* Rx lane statistic specific functions */
1005 static boolean_t
1006 i_dlstat_rx_lane_match(void *arg1, void *arg2)
1008 rx_lane_stat_entry_t *s1 = arg1;
1009 rx_lane_stat_entry_t *s2 = arg2;
1011 return (s1->rle_index == s2->rle_index &&
1012 s1->rle_id == s2->rle_id);
1015 static void *
1016 i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
1018 rx_lane_stat_entry_t *s1 = arg1;
1019 rx_lane_stat_entry_t *s2 = arg2;
1020 rx_lane_stat_entry_t *diff_entry;
1022 diff_entry = malloc(sizeof (rx_lane_stat_entry_t));
1023 if (diff_entry == NULL)
1024 goto done;
1026 diff_entry->rle_index = s1->rle_index;
1027 diff_entry->rle_id = s1->rle_id;
1029 DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
1030 RX_LANE_STAT_SIZE);
1032 done:
1033 return (diff_entry);
1036 static void *
1037 i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1039 rx_lane_stat_entry_t *rx_lane_stat_entry;
1041 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1042 if (rx_lane_stat_entry == NULL)
1043 goto done;
1045 rx_lane_stat_entry->rle_index = i;
1046 rx_lane_stat_entry->rle_id = L_HWLANE;
1048 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1049 rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
1051 done:
1052 return (rx_lane_stat_entry);
1055 /*ARGSUSED*/
1056 static void *
1057 i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1059 rx_lane_stat_entry_t *rx_lane_stat_entry;
1061 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1062 if (rx_lane_stat_entry == NULL)
1063 goto done;
1065 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1066 rx_lane_stat_entry->rle_id = L_SWLANE;
1068 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1069 rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
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);
1079 /*ARGSUSED*/
1080 static void *
1081 i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1083 rx_lane_stat_entry_t *local_stat_entry;
1084 rx_lane_stat_entry_t *rx_lane_stat_entry;
1086 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1087 if (rx_lane_stat_entry == NULL)
1088 goto done;
1090 local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1091 if (local_stat_entry == NULL)
1092 goto done;
1094 local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1095 local_stat_entry->rle_id = L_LOCAL;
1097 i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1098 rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
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;
1105 done:
1106 free(rx_lane_stat_entry);
1107 return (local_stat_entry);
1110 static dladm_stat_chain_t *
1111 i_dlstat_rx_local_stats(const char *linkname)
1113 dladm_stat_chain_t *local_stats = NULL;
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);
1119 if (local_stats != NULL) {
1120 (void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
1121 sizeof (local_stats->dc_statheader));
1123 return (local_stats);
1126 static dladm_stat_chain_t *
1127 i_dlstat_rx_bcast_stats(const char *linkname)
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;
1133 misc_stat_entry = i_dlstat_misc_stats(linkname);
1134 if (misc_stat_entry == NULL)
1135 goto done;
1137 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1138 if (rx_lane_stat_entry == NULL)
1139 goto done;
1141 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1142 rx_lane_stat_entry->rle_id = L_BCAST;
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;
1154 head = malloc(sizeof (dladm_stat_chain_t));
1155 if (head == NULL) {
1156 free(rx_lane_stat_entry);
1157 goto done;
1160 head->dc_statentry = rx_lane_stat_entry;
1161 head->dc_next = NULL;
1163 free(misc_stat_entry);
1164 done:
1165 return (head);
1168 static dladm_stat_chain_t *
1169 i_dlstat_rx_defunctlane_stats(const char *linkname)
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;
1175 misc_stat_entry = i_dlstat_misc_stats(linkname);
1176 if (misc_stat_entry == NULL)
1177 goto done;
1179 rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1180 if (rx_lane_stat_entry == NULL)
1181 goto done;
1183 rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1184 rx_lane_stat_entry->rle_id = L_DFNCT;
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;
1203 head = malloc(sizeof (dladm_stat_chain_t));
1204 if (head == NULL) {
1205 free(rx_lane_stat_entry);
1206 goto done;
1209 head->dc_statentry = rx_lane_stat_entry;
1210 head->dc_next = NULL;
1212 done:
1213 return (head);
1216 static dladm_stat_chain_t *
1217 i_dlstat_rx_hwlane_stats(const char *linkname)
1219 uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1220 uint_t rx_hwlane_idlist_size;
1222 i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
1223 rx_hwlane_idlist, &rx_hwlane_idlist_size);
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));
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)
1235 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE,
1236 default_idlist, default_idlist_size,
1237 i_dlstat_rx_swlane_retrieve_stat));
1240 void *
1241 dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
1251 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1252 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1253 goto done;
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;
1262 if (is_legacy_driver) {
1263 head = i_dlstat_legacy_rx_lane_stats(linkname);
1264 goto done;
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);
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);
1281 /* Tx lane statistic specific functions */
1282 static boolean_t
1283 i_dlstat_tx_lane_match(void *arg1, void *arg2)
1285 tx_lane_stat_entry_t *s1 = arg1;
1286 tx_lane_stat_entry_t *s2 = arg2;
1288 return (s1->tle_index == s2->tle_index &&
1289 s1->tle_id == s2->tle_id);
1292 static void *
1293 i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
1295 tx_lane_stat_entry_t *s1 = arg1;
1296 tx_lane_stat_entry_t *s2 = arg2;
1297 tx_lane_stat_entry_t *diff_entry;
1299 diff_entry = malloc(sizeof (tx_lane_stat_entry_t));
1300 if (diff_entry == NULL)
1301 goto done;
1303 diff_entry->tle_index = s1->tle_index;
1304 diff_entry->tle_id = s1->tle_id;
1306 DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
1307 TX_LANE_STAT_SIZE);
1309 done:
1310 return (diff_entry);
1313 static void *
1314 i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1316 tx_lane_stat_entry_t *tx_lane_stat_entry;
1318 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1319 if (tx_lane_stat_entry == NULL)
1320 goto done;
1322 tx_lane_stat_entry->tle_index = i;
1323 tx_lane_stat_entry->tle_id = L_HWLANE;
1325 i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1326 tx_lane_stats_list, TX_LANE_STAT_SIZE);
1328 done:
1329 return (tx_lane_stat_entry);
1332 /*ARGSUSED*/
1333 static void *
1334 i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1336 tx_lane_stat_entry_t *tx_lane_stat_entry;
1338 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1339 if (tx_lane_stat_entry == NULL)
1340 goto done;
1342 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1343 tx_lane_stat_entry->tle_id = L_SWLANE;
1345 i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1346 tx_lane_stats_list, TX_LANE_STAT_SIZE);
1348 done:
1349 return (tx_lane_stat_entry);
1352 static dladm_stat_chain_t *
1353 i_dlstat_tx_bcast_stats(const char *linkname)
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;
1359 misc_stat_entry = i_dlstat_misc_stats(linkname);
1360 if (misc_stat_entry == NULL)
1361 goto done;
1363 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1364 if (tx_lane_stat_entry == NULL)
1365 goto done;
1367 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1368 tx_lane_stat_entry->tle_id = L_BCAST;
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;
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;
1378 head = malloc(sizeof (dladm_stat_chain_t));
1379 if (head == NULL) {
1380 free(tx_lane_stat_entry);
1381 goto done;
1384 head->dc_statentry = tx_lane_stat_entry;
1385 head->dc_next = NULL;
1387 free(misc_stat_entry);
1388 done:
1389 return (head);
1392 static dladm_stat_chain_t *
1393 i_dlstat_tx_defunctlane_stats(const char *linkname)
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;
1399 misc_stat_entry = i_dlstat_misc_stats(linkname);
1400 if (misc_stat_entry == NULL)
1401 goto done;
1403 tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1404 if (tx_lane_stat_entry == NULL)
1405 goto done;
1407 tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1408 tx_lane_stat_entry->tle_id = L_DFNCT;
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;
1417 head = malloc(sizeof (dladm_stat_chain_t));
1418 if (head == NULL) {
1419 free(tx_lane_stat_entry);
1420 goto done;
1423 head->dc_statentry = tx_lane_stat_entry;
1424 head->dc_next = NULL;
1426 done:
1427 return (head);
1430 static dladm_stat_chain_t *
1431 i_dlstat_tx_hwlane_stats(const char *linkname)
1433 uint_t tx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1434 uint_t tx_hwlane_idlist_size;
1436 i_dlstat_get_idlist(linkname, DLSTAT_TX_HWLANE_IDLIST,
1437 tx_hwlane_idlist, &tx_hwlane_idlist_size);
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));
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)
1449 return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_SWLANE,
1450 default_idlist, default_idlist_size,
1451 i_dlstat_tx_swlane_retrieve_stat));
1454 void *
1455 dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
1464 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1465 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1466 goto done;
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;
1475 if (is_legacy_driver) {
1476 head = i_dlstat_legacy_tx_lane_stats(linkname);
1477 goto done;
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);
1486 head = i_dlstat_join_lists(bcast_stats, defunctlane_stats);
1487 head = i_dlstat_join_lists(head, lane_stats);
1489 done:
1490 return (head);
1493 /* Rx lane total statistic specific functions */
1494 void *
1495 dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
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;
1506 total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
1507 if (total_stats == NULL)
1508 goto done;
1510 total_stats->rle_index = DLSTAT_INVALID_ENTRY;
1511 total_stats->rle_id = DLSTAT_INVALID_ENTRY;
1513 for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) {
1514 rx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
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);
1521 total_head = malloc(sizeof (dladm_stat_chain_t));
1522 if (total_head == NULL) {
1523 free(total_stats);
1524 goto done;
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);
1533 done:
1534 return (total_head);
1537 /* Tx lane total statistic specific functions */
1538 void *
1539 dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
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;
1550 total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
1551 if (total_stats == NULL)
1552 goto done;
1554 total_stats->tle_index = DLSTAT_INVALID_ENTRY;
1555 total_stats->tle_id = DLSTAT_INVALID_ENTRY;
1557 for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) {
1558 tx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry;
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);
1565 total_head = malloc(sizeof (dladm_stat_chain_t));
1566 if (total_head == NULL) {
1567 free(total_stats);
1568 goto done;
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);
1577 done:
1578 return (total_head);
1581 /* Fanout specific functions */
1582 static boolean_t
1583 i_dlstat_fanout_match(void *arg1, void *arg2)
1585 fanout_stat_entry_t *s1 = arg1;
1586 fanout_stat_entry_t *s2 = arg2;
1588 return (s1->fe_index == s2->fe_index &&
1589 s1->fe_id == s2->fe_id &&
1590 s1->fe_foutindex == s2->fe_foutindex);
1593 static void *
1594 i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2)
1596 fanout_stat_entry_t *s1 = arg1;
1597 fanout_stat_entry_t *s2 = arg2;
1598 fanout_stat_entry_t *diff_entry;
1600 diff_entry = malloc(sizeof (fanout_stat_entry_t));
1601 if (diff_entry == NULL)
1602 goto done;
1604 diff_entry->fe_index = s1->fe_index;
1605 diff_entry->fe_id = s1->fe_id;
1606 diff_entry->fe_foutindex = s1->fe_foutindex;
1608 DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list,
1609 FANOUT_STAT_SIZE);
1611 done:
1612 return (diff_entry);
1615 static void *
1616 i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1618 fanout_stat_entry_t *fanout_stat_entry;
1620 fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t));
1621 if (fanout_stat_entry == NULL)
1622 goto done;
1624 /* Set by the caller later */
1625 fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY;
1626 fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY;
1628 fanout_stat_entry->fe_foutindex = i;
1630 i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats,
1631 fanout_stats_list, FANOUT_STAT_SIZE);
1633 done:
1634 return (fanout_stat_entry);
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)
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;
1650 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1651 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1652 return (NULL);
1655 i_dlstat_get_idlist(linkname, DLSTAT_FANOUT_IDLIST,
1656 fanout_idlist, &fanout_idlist_size);
1658 for (i = 0; i < idlist_size; i++) {
1659 uint_t index = idlist[i];
1661 (void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout",
1662 prefix, index);
1664 curr_head = i_dlstat_query_stats(modname, statprefix,
1665 fanout_idlist, fanout_idlist_size,
1666 i_dlstat_fanout_retrieve_stat);
1668 if (curr_head == NULL) /* Last lane */
1669 break;
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;
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;
1680 curr_stats->fe_index = index;
1681 curr_stats->fe_id = L_HWLANE;
1683 * Save last pointer of previous linked list.
1684 * This pointer is used to chain linked lists
1685 * generated in each iteration.
1687 prev = curr;
1691 return (head);
1694 void *
1695 dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid,
1696 const char *linkname)
1698 return (i_dlstat_query_fanout_stats(dh, linkid,
1699 default_idlist, default_idlist_size, linkname,
1700 DLSTAT_MAC_RX_SWLANE));
1703 void *
1704 dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1705 const char *linkname)
1707 uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1708 uint_t rx_hwlane_idlist_size;
1710 i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST,
1711 rx_hwlane_idlist, &rx_hwlane_idlist_size);
1713 return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist,
1714 rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE));
1717 void *
1718 dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid)
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];
1726 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1727 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1728 goto done;
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);
1735 if (fout_swlane_and_local_stats == NULL) {
1736 head = fout_hwlane_stats;
1737 goto done;
1740 fout_stats = fout_swlane_and_local_stats->dc_statentry;
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;
1750 fout_swlane_and_local_stats->dc_next = fout_hwlane_stats;
1751 head = fout_swlane_and_local_stats;
1753 done:
1754 return (head);
1757 /* Rx ring statistic specific functions */
1758 static boolean_t
1759 i_dlstat_rx_ring_match(void *arg1, void *arg2)
1761 rx_lane_stat_entry_t *s1 = arg1;
1762 rx_lane_stat_entry_t *s2 = arg2;
1764 return (s1->rle_index == s2->rle_index);
1767 static void *
1768 i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
1770 ring_stat_entry_t *s1 = arg1;
1771 ring_stat_entry_t *s2 = arg2;
1772 ring_stat_entry_t *diff_entry;
1774 diff_entry = malloc(sizeof (ring_stat_entry_t));
1775 if (diff_entry == NULL)
1776 goto done;
1778 diff_entry->re_index = s1->re_index;
1780 DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
1781 RX_RING_STAT_SIZE);
1783 done:
1784 return (diff_entry);
1787 static void *
1788 i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1790 ring_stat_entry_t *rx_ring_stat_entry;
1792 rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1793 if (rx_ring_stat_entry == NULL)
1794 goto done;
1796 rx_ring_stat_entry->re_index = i;
1798 i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
1799 rx_ring_stats_list, RX_RING_STAT_SIZE);
1801 done:
1802 return (rx_ring_stat_entry);
1805 void *
1806 dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
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.
1822 if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1823 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1824 return (NULL);
1827 if (class != DATALINK_CLASS_AGGR) {
1828 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1829 DLADM_STATUS_OK) {
1830 return (NULL);
1832 modname = dpa.dp_dev;
1833 } else
1834 modname = linkname;
1836 i_dlstat_get_idlist(modname, DLSTAT_RX_RING_IDLIST,
1837 rx_ring_idlist, &rx_ring_idlist_size);
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));
1844 /* Tx ring statistic specific functions */
1845 static boolean_t
1846 i_dlstat_tx_ring_match(void *arg1, void *arg2)
1848 tx_lane_stat_entry_t *s1 = arg1;
1849 tx_lane_stat_entry_t *s2 = arg2;
1851 return (s1->tle_index == s2->tle_index);
1854 static void *
1855 i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
1857 ring_stat_entry_t *s1 = arg1;
1858 ring_stat_entry_t *s2 = arg2;
1859 ring_stat_entry_t *diff_entry;
1861 diff_entry = malloc(sizeof (ring_stat_entry_t));
1862 if (diff_entry == NULL)
1863 goto done;
1865 diff_entry->re_index = s1->re_index;
1867 DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
1868 TX_RING_STAT_SIZE);
1870 done:
1871 return (diff_entry);
1874 static void *
1875 i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1877 ring_stat_entry_t *tx_ring_stat_entry;
1879 tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1880 if (tx_ring_stat_entry == NULL)
1881 goto done;
1883 tx_ring_stat_entry->re_index = i;
1885 i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
1886 tx_ring_stats_list, TX_RING_STAT_SIZE);
1888 done:
1889 return (tx_ring_stat_entry);
1892 void *
1893 dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
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.
1909 if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1910 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1911 return (NULL);
1914 if (class != DATALINK_CLASS_AGGR) {
1915 if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1916 DLADM_STATUS_OK) {
1917 return (NULL);
1919 modname = dpa.dp_dev;
1920 } else
1921 modname = linkname;
1923 i_dlstat_get_idlist(modname, DLSTAT_TX_RING_IDLIST,
1924 tx_ring_idlist, &tx_ring_idlist_size);
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));
1931 /* Rx ring total statistic specific functions */
1932 void *
1933 dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1935 dladm_stat_chain_t *total_head = NULL;
1936 dladm_stat_chain_t *rx_ring_head, *curr;
1937 ring_stat_entry_t *total_stats;
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;
1944 total_stats = calloc(1, sizeof (ring_stat_entry_t));
1945 if (total_stats == NULL)
1946 goto done;
1948 total_stats->re_index = DLSTAT_INVALID_ENTRY;
1950 for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) {
1951 ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
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);
1958 total_head = malloc(sizeof (dladm_stat_chain_t));
1959 if (total_head == NULL) {
1960 free(total_stats);
1961 goto done;
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);
1970 done:
1971 return (total_head);
1974 /* Tx ring total statistic specific functions */
1975 void *
1976 dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1978 dladm_stat_chain_t *total_head = NULL;
1979 dladm_stat_chain_t *tx_ring_head, *curr;
1980 ring_stat_entry_t *total_stats;
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;
1987 total_stats = calloc(1, sizeof (ring_stat_entry_t));
1988 if (total_stats == NULL)
1989 goto done;
1991 total_stats->re_index = DLSTAT_INVALID_ENTRY;
1993 for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) {
1994 ring_stat_entry_t *curr_ring_stats = curr->dc_statentry;
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);
2001 total_head = malloc(sizeof (dladm_stat_chain_t));
2002 if (total_head == NULL) {
2003 free(total_stats);
2004 goto done;
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);
2013 done:
2014 return (total_head);
2017 /* Summary statistic specific functions */
2018 /*ARGSUSED*/
2019 static boolean_t
2020 i_dlstat_total_match(void *arg1, void *arg2)
2022 /* Always single entry for total */
2023 return (B_TRUE);
2026 static void *
2027 i_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
2029 total_stat_entry_t *s1 = arg1;
2030 total_stat_entry_t *s2 = arg2;
2031 total_stat_entry_t *diff_entry;
2033 diff_entry = malloc(sizeof (total_stat_entry_t));
2034 if (diff_entry == NULL)
2035 goto done;
2037 DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
2038 TOTAL_STAT_SIZE);
2040 done:
2041 return (diff_entry);
2044 void *
2045 dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
2054 /* Get total rx lane stats */
2055 rx_total = dlstat_rx_lane_total_stats(dh, linkid);
2056 if (rx_total == NULL)
2057 goto done;
2059 /* Get total tx lane stats */
2060 tx_total = dlstat_tx_lane_total_stats(dh, linkid);
2061 if (tx_total == NULL)
2062 goto done;
2064 /* Build total stat */
2065 total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2066 if (total_stat_entry == NULL)
2067 goto done;
2069 rx_lane_stat_entry = rx_total->dc_statentry;
2070 tx_lane_stat_entry = tx_total->dc_statentry;
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;
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;
2084 head = malloc(sizeof (dladm_stat_chain_t));
2085 if (head == NULL) {
2086 free(total_stat_entry);
2087 goto done;
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);
2097 done:
2098 return (head);
2101 /* Aggr total statistic(summed across all component ports) specific functions */
2102 void *
2103 dlstat_aggr_total_stats(dladm_stat_chain_t *head)
2105 dladm_stat_chain_t *curr;
2106 dladm_stat_chain_t *total_head;
2107 aggr_port_stat_entry_t *total_stats;
2109 total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
2110 if (total_stats == NULL)
2111 goto done;
2113 total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
2115 for (curr = head; curr != NULL; curr = curr->dc_next) {
2116 aggr_port_stat_entry_t *curr_aggr_port_stats;
2118 curr_aggr_port_stats = curr->dc_statentry;
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);
2125 total_head = malloc(sizeof (dladm_stat_chain_t));
2126 if (total_head == NULL) {
2127 free(total_stats);
2128 goto done;
2131 total_head->dc_statentry = total_stats;
2132 total_head->dc_next = NULL;
2134 done:
2135 return (total_head);
2138 /* Aggr port statistic specific functions */
2139 static boolean_t
2140 i_dlstat_aggr_port_match(void *arg1, void *arg2)
2142 aggr_port_stat_entry_t *s1 = arg1;
2143 aggr_port_stat_entry_t *s2 = arg2;
2145 return (s1->ape_portlinkid == s2->ape_portlinkid);
2148 static void *
2149 i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
2151 aggr_port_stat_entry_t *s1 = arg1;
2152 aggr_port_stat_entry_t *s2 = arg2;
2153 aggr_port_stat_entry_t *diff_entry;
2155 diff_entry = malloc(sizeof (aggr_port_stat_entry_t));
2156 if (diff_entry == NULL)
2157 goto done;
2159 diff_entry->ape_portlinkid = s1->ape_portlinkid;
2161 DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
2162 AGGR_PORT_STAT_SIZE);
2164 done:
2165 return (diff_entry);
2169 * Query dls stats for the aggr port. This results in query for stats into
2170 * the corresponding device driver.
2172 static aggr_port_stat_entry_t *
2173 i_dlstat_single_port_stats(const char *portname, datalink_id_t linkid)
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;
2181 if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK)
2182 goto done;
2184 if ((kcp = kstat_open()) == NULL) {
2185 warn("kstat open operation failed");
2186 return (NULL);
2189 ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL);
2190 if (ksp == NULL)
2191 goto done;
2193 aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2194 if (aggr_port_stat_entry == NULL)
2195 goto done;
2197 /* Save port's linkid */
2198 aggr_port_stat_entry->ape_portlinkid = linkid;
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);
2207 void *
2208 dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
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;
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;
2231 aggr_port_stat_entry = i_dlstat_single_port_stats(dpa.dp_dev,
2232 portp->lp_linkid);
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;
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;
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;
2255 * Prepend the stat list with cumulative aggr stats i.e. summed over all
2256 * component ports
2258 total_stats = dlstat_aggr_total_stats(head);
2259 if (total_stats != NULL) {
2260 total_stats->dc_next = head;
2261 head = total_stats;
2264 done:
2265 free(ginfo.lg_ports);
2266 return (head);
2269 /* Misc stat specific functions */
2270 void *
2271 dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid)
2273 misc_stat_entry_t *misc_stat_entry;
2274 dladm_stat_chain_t *head = NULL;
2275 char linkname[MAXLINKNAMELEN];
2277 if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2278 DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2279 goto done;
2282 misc_stat_entry = i_dlstat_misc_stats(linkname);
2283 if (misc_stat_entry == NULL)
2284 goto done;
2286 head = malloc(sizeof (dladm_stat_chain_t));
2287 if (head == NULL) {
2288 free(misc_stat_entry);
2289 goto done;
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;
2297 done:
2298 return (head);
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)
2306 return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
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)
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;
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;
2326 diff_curr = malloc(sizeof (dladm_stat_chain_t));
2327 if (diff_curr == NULL)
2328 goto done;
2330 diff_curr->dc_next = NULL;
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);
2343 if (diff_curr->dc_statentry == NULL) {
2344 free(diff_curr);
2345 goto done;
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;
2354 done:
2355 return (diff_head);
2358 void
2359 dladm_link_stat_free(dladm_stat_chain_t *curr)
2361 while (curr != NULL) {
2362 dladm_stat_chain_t *tofree = curr;
2364 curr = curr->dc_next;
2365 free(tofree->dc_statentry);
2366 free(tofree);
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)
2374 int i;
2375 name_value_stat_t *head_stat = NULL, *prev_stat = NULL;
2376 name_value_stat_t *curr_stat;
2378 for (i = 0; i < size; i++) {
2379 uint64_t *val = (void *)
2380 ((uchar_t *)stats + stats_list[i].si_offset);
2382 curr_stat = calloc(1, sizeof (name_value_stat_t));
2383 if (curr_stat == NULL)
2384 break;
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;
2391 if (head_stat == NULL) /* First node */
2392 head_stat = curr_stat;
2393 else
2394 prev_stat->nv_nextstat = curr_stat;
2396 prev_stat = curr_stat;
2398 return (head_stat);
2401 void *
2402 build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
2404 name_value_stat_entry_t *name_value_stat_entry;
2405 dladm_stat_desc_t *stattbl_ptr;
2406 void *statfields;
2408 stattbl_ptr = &dladm_stat_table[stattype];
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;
2415 /* Header for these stat fields */
2416 (void) strlcpy(name_value_stat_entry->nve_header, statheader,
2417 sizeof (name_value_stat_entry->nve_header));
2419 /* Extract stat fields from the statentry */
2420 statfields = (uchar_t *)statentry +
2421 dladm_stat_table[stattype].ds_offset;
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);
2431 void *
2432 i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
2434 dladm_stat_chain_t *curr;
2435 dladm_stat_chain_t *nvstat_head = NULL, *nvstat_prev = NULL;
2436 dladm_stat_chain_t *nvstat_curr;
2439 * For every stat in the chain, build header and convert all
2440 * its stat fields
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;
2447 nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
2448 curr->dc_statentry, stattype);
2450 if (nvstat_curr->dc_statentry == NULL) {
2451 free(nvstat_curr);
2452 break;
2455 nvstat_curr->dc_next = NULL;
2457 if (nvstat_head == NULL) /* First node */
2458 nvstat_head = nvstat_curr;
2459 else
2460 nvstat_prev->dc_next = nvstat_curr;
2462 nvstat_prev = nvstat_curr;
2464 done:
2465 return (nvstat_head);
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)
2472 dladm_stat_chain_t *stat_head;
2473 dladm_stat_chain_t *nvstat_head = NULL;
2475 /* Query the requested stat */
2476 stat_head = dladm_link_stat_query(dh, linkid, stattype);
2477 if (stat_head == NULL)
2478 goto done;
2481 * Convert every statfield in every stat-entry of stat chain to
2482 * <statname, statval> pair
2484 nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
2486 /* Free stat_head */
2487 dladm_link_stat_free(stat_head);
2489 done:
2490 return (nvstat_head);
2493 void
2494 dladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
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;
2501 while (nv_curr != NULL) {
2502 name_value_stat_t *nv_tofree = nv_curr;
2504 nv_curr = nv_curr->nv_nextstat;
2505 free(nv_tofree);
2508 curr = curr->dc_next;
2509 free(nv_entry);
2510 free(tofree);
2514 /* flow stats specific routines */
2515 flow_stat_t *
2516 dladm_flow_stat_query(const char *flowname)
2518 kstat_ctl_t *kcp;
2519 kstat_t *ksp;
2520 flow_stat_t *flow_stat = NULL;
2522 if ((kcp = kstat_open()) == NULL)
2523 return (NULL);
2525 flow_stat = calloc(1, sizeof (flow_stat_t));
2526 if (flow_stat == NULL)
2527 goto done;
2529 ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
2531 if (ksp != NULL) {
2532 i_dlstat_get_stats(kcp, ksp, flow_stat, flow_stats_list,
2533 FLOW_STAT_SIZE);
2536 done:
2537 (void) kstat_close(kcp);
2538 return (flow_stat);
2541 flow_stat_t *
2542 dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
2544 flow_stat_t *diff_stat;
2546 diff_stat = calloc(1, sizeof (flow_stat_t));
2547 if (diff_stat == NULL)
2548 goto done;
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);
2556 done:
2557 return (diff_stat);
2560 void
2561 dladm_flow_stat_free(flow_stat_t *curr)
2563 free(curr);
2566 /* Query all flow stats */
2567 name_value_stat_entry_t *
2568 dladm_flow_stat_query_all(const char *flowname)
2570 flow_stat_t *flow_stat;
2571 name_value_stat_entry_t *name_value_stat_entry = NULL;
2573 /* Query flow stats */
2574 flow_stat = dladm_flow_stat_query(flowname);
2575 if (flow_stat == NULL)
2576 goto done;
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;
2585 /* Header for these stat fields */
2586 (void) strncpy(name_value_stat_entry->nve_header, flowname,
2587 MAXFLOWNAMELEN);
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);
2593 /* Free flow_stat */
2594 dladm_flow_stat_free(flow_stat);
2596 done:
2597 return (name_value_stat_entry);
2600 void
2601 dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
2603 name_value_stat_t *nv_curr = curr->nve_stats;
2605 while (nv_curr != NULL) {
2606 name_value_stat_t *nv_tofree = nv_curr;
2608 nv_curr = nv_curr->nv_nextstat;
2609 free(nv_tofree);