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