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 done:
974 	return (head);
975 }
976 
977 static misc_stat_entry_t *
978 i_dlstat_misc_stats(dladm_handle_t handle, const char *linkname)
979 {
980 	kstat_t			*ksp;
981 	misc_stat_entry_t 	*misc_stat_entry = NULL;
982 
983 	if (dladm_dld_kcp(handle) == NULL)
984 		return (NULL);
985 
986 	ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), linkname, 0,
987 	    DLSTAT_MAC_MISC_STAT, NULL);
988 	if (ksp == NULL)
989 		goto done;
990 
991 	misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t));
992 	if (misc_stat_entry == NULL)
993 		goto done;
994 
995 	i_dlstat_get_stats(dladm_dld_kcp(handle), ksp,
996 	    &misc_stat_entry->mse_stats,
997 	    misc_stats_list, MISC_STAT_SIZE);
998 done:
999 	return (misc_stat_entry);
1000 }
1001 
1002 /* Rx lane statistic specific functions */
1003 static boolean_t
1004 i_dlstat_rx_lane_match(void *arg1, void *arg2)
1005 {
1006 	rx_lane_stat_entry_t *s1 = arg1;
1007 	rx_lane_stat_entry_t *s2 = arg2;
1008 
1009 	return (s1->rle_index == s2->rle_index &&
1010 	    s1->rle_id == s2->rle_id);
1011 }
1012 
1013 static void *
1014 i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
1015 {
1016 	rx_lane_stat_entry_t *s1 = arg1;
1017 	rx_lane_stat_entry_t *s2 = arg2;
1018 	rx_lane_stat_entry_t *diff_entry;
1019 
1020 	diff_entry = malloc(sizeof (rx_lane_stat_entry_t));
1021 	if (diff_entry == NULL)
1022 		goto done;
1023 
1024 	diff_entry->rle_index = s1->rle_index;
1025 	diff_entry->rle_id = s1->rle_id;
1026 
1027 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
1028 	    RX_LANE_STAT_SIZE);
1029 
1030 done:
1031 	return (diff_entry);
1032 }
1033 
1034 static void *
1035 i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1036 {
1037 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
1038 
1039 	rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1040 	if (rx_lane_stat_entry == NULL)
1041 		goto done;
1042 
1043 	rx_lane_stat_entry->rle_index = i;
1044 	rx_lane_stat_entry->rle_id = L_HWLANE;
1045 
1046 	i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1047 	    rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
1048 
1049 done:
1050 	return (rx_lane_stat_entry);
1051 }
1052 
1053 /*ARGSUSED*/
1054 static void *
1055 i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1056 {
1057 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
1058 
1059 	rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1060 	if (rx_lane_stat_entry == NULL)
1061 		goto done;
1062 
1063 	rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1064 	rx_lane_stat_entry->rle_id = L_SWLANE;
1065 
1066 	i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1067 	    rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1068 
1069 	rx_lane_stat_entry->rle_stats.rl_ipackets =
1070 	    rx_lane_stat_entry->rle_stats.rl_intrs;
1071 	rx_lane_stat_entry->rle_stats.rl_rbytes =
1072 	    rx_lane_stat_entry->rle_stats.rl_intrbytes;
1073 done:
1074 	return (rx_lane_stat_entry);
1075 }
1076 
1077 /*ARGSUSED*/
1078 static void *
1079 i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1080 {
1081 	rx_lane_stat_entry_t	*local_stat_entry;
1082 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
1083 
1084 	rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1085 	if (rx_lane_stat_entry == NULL)
1086 		goto done;
1087 
1088 	local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1089 	if (local_stat_entry == NULL)
1090 		goto done;
1091 
1092 	local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1093 	local_stat_entry->rle_id = L_LOCAL;
1094 
1095 	i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
1096 	    rx_swlane_stats_list, RX_SWLANE_STAT_SIZE);
1097 
1098 	local_stat_entry->rle_stats.rl_ipackets =
1099 	    rx_lane_stat_entry->rle_stats.rl_lclpackets;
1100 	local_stat_entry->rle_stats.rl_rbytes =
1101 	    rx_lane_stat_entry->rle_stats.rl_lclbytes;
1102 
1103 done:
1104 	free(rx_lane_stat_entry);
1105 	return (local_stat_entry);
1106 }
1107 
1108 static dladm_stat_chain_t *
1109 i_dlstat_rx_local_stats(dladm_handle_t handle, const char *linkname)
1110 {
1111 	dladm_stat_chain_t	*local_stats = NULL;
1112 
1113 	local_stats = i_dlstat_query_stats(handle, linkname,
1114 	    DLSTAT_MAC_RX_SWLANE,
1115 	    default_idlist, default_idlist_size,
1116 	    i_dlstat_rx_local_retrieve_stat);
1117 
1118 	if (local_stats != NULL) {
1119 		(void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
1120 		    sizeof (local_stats->dc_statheader));
1121 	}
1122 	return (local_stats);
1123 }
1124 
1125 static dladm_stat_chain_t *
1126 i_dlstat_rx_bcast_stats(dladm_handle_t handle, const char *linkname)
1127 {
1128 	misc_stat_entry_t	*misc_stat_entry;
1129 	dladm_stat_chain_t	*head = NULL;
1130 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
1131 
1132 	misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1133 	if (misc_stat_entry == NULL)
1134 		goto done;
1135 
1136 	rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1137 	if (rx_lane_stat_entry == NULL)
1138 		goto done;
1139 
1140 	rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1141 	rx_lane_stat_entry->rle_id = L_BCAST;
1142 
1143 	rx_lane_stat_entry->rle_stats.rl_ipackets =
1144 	    misc_stat_entry->mse_stats.ms_brdcstrcv +
1145 	    misc_stat_entry->mse_stats.ms_multircv;
1146 	rx_lane_stat_entry->rle_stats.rl_intrs =
1147 	    misc_stat_entry->mse_stats.ms_brdcstrcv +
1148 	    misc_stat_entry->mse_stats.ms_multircv;
1149 	rx_lane_stat_entry->rle_stats.rl_rbytes =
1150 	    misc_stat_entry->mse_stats.ms_brdcstrcvbytes +
1151 	    misc_stat_entry->mse_stats.ms_multircvbytes;
1152 
1153 	head = malloc(sizeof (dladm_stat_chain_t));
1154 	if (head == NULL) {
1155 		free(rx_lane_stat_entry);
1156 		goto done;
1157 	}
1158 
1159 	head->dc_statentry = rx_lane_stat_entry;
1160 	head->dc_next = NULL;
1161 
1162 	free(misc_stat_entry);
1163 done:
1164 	return (head);
1165 }
1166 
1167 static dladm_stat_chain_t *
1168 i_dlstat_rx_defunctlane_stats(dladm_handle_t handle, const char *linkname)
1169 {
1170 	misc_stat_entry_t	*misc_stat_entry;
1171 	dladm_stat_chain_t	*head = NULL;
1172 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
1173 
1174 	misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1175 	if (misc_stat_entry == NULL)
1176 		goto done;
1177 
1178 	rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
1179 	if (rx_lane_stat_entry == NULL)
1180 		goto done;
1181 
1182 	rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
1183 	rx_lane_stat_entry->rle_id = L_DFNCT;
1184 
1185 	rx_lane_stat_entry->rle_stats.rl_ipackets =
1186 	    misc_stat_entry->mse_stats.ms_ipackets;
1187 	rx_lane_stat_entry->rle_stats.rl_rbytes =
1188 	    misc_stat_entry->mse_stats.ms_rbytes;
1189 	rx_lane_stat_entry->rle_stats.rl_intrs =
1190 	    misc_stat_entry->mse_stats.ms_intrs;
1191 	rx_lane_stat_entry->rle_stats.rl_polls =
1192 	    misc_stat_entry->mse_stats.ms_polls;
1193 	rx_lane_stat_entry->rle_stats.rl_sdrops =
1194 	    misc_stat_entry->mse_stats.ms_rxsdrops;
1195 	rx_lane_stat_entry->rle_stats.rl_chl10 =
1196 	    misc_stat_entry->mse_stats.ms_chainunder10;
1197 	rx_lane_stat_entry->rle_stats.rl_ch10_50 =
1198 	    misc_stat_entry->mse_stats.ms_chain10to50;
1199 	rx_lane_stat_entry->rle_stats.rl_chg50 =
1200 	    misc_stat_entry->mse_stats.ms_chainover50;
1201 
1202 	head = malloc(sizeof (dladm_stat_chain_t));
1203 	if (head == NULL) {
1204 		free(rx_lane_stat_entry);
1205 		goto done;
1206 	}
1207 
1208 	head->dc_statentry = rx_lane_stat_entry;
1209 	head->dc_next = NULL;
1210 
1211 done:
1212 	return (head);
1213 }
1214 
1215 static dladm_stat_chain_t *
1216 i_dlstat_rx_hwlane_stats(dladm_handle_t handle, const char *linkname)
1217 {
1218 	uint_t	rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1219 	uint_t	rx_hwlane_idlist_size;
1220 
1221 	i_dlstat_get_idlist(handle, linkname, DLSTAT_RX_HWLANE_IDLIST,
1222 	    rx_hwlane_idlist, &rx_hwlane_idlist_size);
1223 
1224 	return (i_dlstat_query_stats(handle, linkname, DLSTAT_MAC_RX_HWLANE,
1225 	    rx_hwlane_idlist, rx_hwlane_idlist_size,
1226 	    i_dlstat_rx_hwlane_retrieve_stat));
1227 }
1228 
1229 /*ARGSUSED*/
1230 static dladm_stat_chain_t *
1231 i_dlstat_rx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1232     const char *linkname)
1233 {
1234 	return (i_dlstat_query_stats(dh, linkname, DLSTAT_MAC_RX_SWLANE,
1235 	    default_idlist, default_idlist_size,
1236 	    i_dlstat_rx_swlane_retrieve_stat));
1237 }
1238 
1239 void *
1240 dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1241 {
1242 	dladm_stat_chain_t	*head = NULL;
1243 	dladm_stat_chain_t 	*local_stats = NULL;
1244 	dladm_stat_chain_t 	*bcast_stats = NULL;
1245 	dladm_stat_chain_t 	*defunctlane_stats = NULL;
1246 	dladm_stat_chain_t 	*lane_stats = NULL;
1247 	char 			linkname[MAXLINKNAMELEN];
1248 	boolean_t		is_legacy_driver;
1249 
1250 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1251 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1252 		goto done;
1253 	}
1254 
1255 	/* Check if it is legacy driver */
1256 	if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1257 	    "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1258 		goto done;
1259 	}
1260 
1261 	if (is_legacy_driver) {
1262 		head = i_dlstat_legacy_rx_lane_stats(dh, linkname);
1263 		goto done;
1264 	}
1265 
1266 	local_stats = i_dlstat_rx_local_stats(dh, linkname);
1267 	bcast_stats = i_dlstat_rx_bcast_stats(dh, linkname);
1268 	defunctlane_stats = i_dlstat_rx_defunctlane_stats(dh, linkname);
1269 	lane_stats = i_dlstat_rx_hwlane_stats(dh, linkname);
1270 	if (lane_stats == NULL)
1271 		lane_stats = i_dlstat_rx_swlane_stats(dh, linkid, linkname);
1272 
1273 	head = i_dlstat_join_lists(local_stats, bcast_stats);
1274 	head = i_dlstat_join_lists(head, defunctlane_stats);
1275 	head = i_dlstat_join_lists(head, lane_stats);
1276 done:
1277 	return (head);
1278 }
1279 
1280 /* Tx lane statistic specific functions */
1281 static boolean_t
1282 i_dlstat_tx_lane_match(void *arg1, void *arg2)
1283 {
1284 	tx_lane_stat_entry_t *s1 = arg1;
1285 	tx_lane_stat_entry_t *s2 = arg2;
1286 
1287 	return (s1->tle_index == s2->tle_index &&
1288 	    s1->tle_id == s2->tle_id);
1289 }
1290 
1291 static void *
1292 i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
1293 {
1294 	tx_lane_stat_entry_t *s1 = arg1;
1295 	tx_lane_stat_entry_t *s2 = arg2;
1296 	tx_lane_stat_entry_t *diff_entry;
1297 
1298 	diff_entry = malloc(sizeof (tx_lane_stat_entry_t));
1299 	if (diff_entry == NULL)
1300 		goto done;
1301 
1302 	diff_entry->tle_index = s1->tle_index;
1303 	diff_entry->tle_id = s1->tle_id;
1304 
1305 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
1306 	    TX_LANE_STAT_SIZE);
1307 
1308 done:
1309 	return (diff_entry);
1310 }
1311 
1312 static void *
1313 i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1314 {
1315 	tx_lane_stat_entry_t	*tx_lane_stat_entry;
1316 
1317 	tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1318 	if (tx_lane_stat_entry == NULL)
1319 		goto done;
1320 
1321 	tx_lane_stat_entry->tle_index	= i;
1322 	tx_lane_stat_entry->tle_id	= L_HWLANE;
1323 
1324 	i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1325 	    tx_lane_stats_list, TX_LANE_STAT_SIZE);
1326 
1327 done:
1328 	return (tx_lane_stat_entry);
1329 }
1330 
1331 /*ARGSUSED*/
1332 static void *
1333 i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1334 {
1335 	tx_lane_stat_entry_t	*tx_lane_stat_entry;
1336 
1337 	tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1338 	if (tx_lane_stat_entry == NULL)
1339 		goto done;
1340 
1341 	tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1342 	tx_lane_stat_entry->tle_id = L_SWLANE;
1343 
1344 	i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
1345 	    tx_lane_stats_list, TX_LANE_STAT_SIZE);
1346 
1347 done:
1348 	return (tx_lane_stat_entry);
1349 }
1350 
1351 static dladm_stat_chain_t *
1352 i_dlstat_tx_bcast_stats(dladm_handle_t handle, const char *linkname)
1353 {
1354 	misc_stat_entry_t	*misc_stat_entry;
1355 	dladm_stat_chain_t	*head = NULL;
1356 	tx_lane_stat_entry_t	*tx_lane_stat_entry;
1357 
1358 	misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1359 	if (misc_stat_entry == NULL)
1360 		goto done;
1361 
1362 	tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1363 	if (tx_lane_stat_entry == NULL)
1364 		goto done;
1365 
1366 	tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1367 	tx_lane_stat_entry->tle_id = L_BCAST;
1368 
1369 	tx_lane_stat_entry->tle_stats.tl_opackets =
1370 	    misc_stat_entry->mse_stats.ms_brdcstxmt +
1371 	    misc_stat_entry->mse_stats.ms_multixmt;
1372 
1373 	tx_lane_stat_entry->tle_stats.tl_obytes =
1374 	    misc_stat_entry->mse_stats.ms_brdcstxmtbytes +
1375 	    misc_stat_entry->mse_stats.ms_multixmtbytes;
1376 
1377 	head = malloc(sizeof (dladm_stat_chain_t));
1378 	if (head == NULL) {
1379 		free(tx_lane_stat_entry);
1380 		goto done;
1381 	}
1382 
1383 	head->dc_statentry = tx_lane_stat_entry;
1384 	head->dc_next = NULL;
1385 
1386 	free(misc_stat_entry);
1387 done:
1388 	return (head);
1389 }
1390 
1391 static dladm_stat_chain_t *
1392 i_dlstat_tx_defunctlane_stats(dladm_handle_t handle, const char *linkname)
1393 {
1394 	misc_stat_entry_t	*misc_stat_entry;
1395 	dladm_stat_chain_t	*head = NULL;
1396 	tx_lane_stat_entry_t	*tx_lane_stat_entry;
1397 
1398 	misc_stat_entry = i_dlstat_misc_stats(handle, linkname);
1399 	if (misc_stat_entry == NULL)
1400 		goto done;
1401 
1402 	tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
1403 	if (tx_lane_stat_entry == NULL)
1404 		goto done;
1405 
1406 	tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
1407 	tx_lane_stat_entry->tle_id = L_DFNCT;
1408 
1409 	tx_lane_stat_entry->tle_stats.tl_opackets =
1410 	    misc_stat_entry->mse_stats.ms_opackets;
1411 	tx_lane_stat_entry->tle_stats.tl_obytes =
1412 	    misc_stat_entry->mse_stats.ms_obytes;
1413 	tx_lane_stat_entry->tle_stats.tl_sdrops =
1414 	    misc_stat_entry->mse_stats.ms_txsdrops;
1415 
1416 	head = malloc(sizeof (dladm_stat_chain_t));
1417 	if (head == NULL) {
1418 		free(tx_lane_stat_entry);
1419 		goto done;
1420 	}
1421 
1422 	head->dc_statentry = tx_lane_stat_entry;
1423 	head->dc_next = NULL;
1424 
1425 done:
1426 	return (head);
1427 }
1428 
1429 static dladm_stat_chain_t *
1430 i_dlstat_tx_hwlane_stats(dladm_handle_t handle, const char *linkname)
1431 {
1432 	uint_t	tx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1433 	uint_t	tx_hwlane_idlist_size;
1434 
1435 	i_dlstat_get_idlist(handle, linkname, DLSTAT_TX_HWLANE_IDLIST,
1436 	    tx_hwlane_idlist, &tx_hwlane_idlist_size);
1437 
1438 	return (i_dlstat_query_stats(handle, linkname, DLSTAT_MAC_TX_HWLANE,
1439 	    tx_hwlane_idlist, tx_hwlane_idlist_size,
1440 	    i_dlstat_tx_hwlane_retrieve_stat));
1441 }
1442 
1443 /*ARGSUSED*/
1444 static dladm_stat_chain_t *
1445 i_dlstat_tx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1446     const char *linkname)
1447 {
1448 	return (i_dlstat_query_stats(dh, linkname, DLSTAT_MAC_TX_SWLANE,
1449 	    default_idlist, default_idlist_size,
1450 	    i_dlstat_tx_swlane_retrieve_stat));
1451 }
1452 
1453 void *
1454 dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
1455 {
1456 	dladm_stat_chain_t	*head = NULL;
1457 	dladm_stat_chain_t 	*bcast_stats = NULL;
1458 	dladm_stat_chain_t 	*defunctlane_stats = NULL;
1459 	dladm_stat_chain_t 	*lane_stats;
1460 	char 			linkname[MAXLINKNAMELEN];
1461 	boolean_t		is_legacy_driver;
1462 
1463 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1464 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1465 		goto done;
1466 	}
1467 
1468 	/* Check if it is legacy driver */
1469 	if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
1470 	    "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
1471 		goto done;
1472 	}
1473 
1474 	if (is_legacy_driver) {
1475 		head = i_dlstat_legacy_tx_lane_stats(dh, linkname);
1476 		goto done;
1477 	}
1478 
1479 	bcast_stats = i_dlstat_tx_bcast_stats(dh, linkname);
1480 	defunctlane_stats = i_dlstat_tx_defunctlane_stats(dh, linkname);
1481 	lane_stats = i_dlstat_tx_hwlane_stats(dh, linkname);
1482 	if (lane_stats == NULL)
1483 		lane_stats = i_dlstat_tx_swlane_stats(dh, linkid, linkname);
1484 
1485 	head = i_dlstat_join_lists(bcast_stats, defunctlane_stats);
1486 	head = i_dlstat_join_lists(head, lane_stats);
1487 
1488 done:
1489 	return (head);
1490 }
1491 
1492 /* Rx lane total statistic specific functions */
1493 void *
1494 dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1495 {
1496 	dladm_stat_chain_t	*total_head = NULL;
1497 	dladm_stat_chain_t	*rx_lane_head, *curr;
1498 	rx_lane_stat_entry_t	*total_stats;
1499 
1500 	/* Get per rx lane stats */
1501 	rx_lane_head = dlstat_rx_lane_stats(dh, linkid);
1502 	if (rx_lane_head == NULL)
1503 		goto done;
1504 
1505 	total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
1506 	if (total_stats == NULL)
1507 		goto done;
1508 
1509 	total_stats->rle_index = DLSTAT_INVALID_ENTRY;
1510 	total_stats->rle_id = DLSTAT_INVALID_ENTRY;
1511 
1512 	for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) {
1513 		rx_lane_stat_entry_t	*curr_lane_stats = curr->dc_statentry;
1514 
1515 		i_dlstat_sum_stats(&total_stats->rle_stats,
1516 		    &curr_lane_stats->rle_stats, &total_stats->rle_stats,
1517 		    rx_lane_stats_list, RX_LANE_STAT_SIZE);
1518 	}
1519 
1520 	total_head = malloc(sizeof (dladm_stat_chain_t));
1521 	if (total_head == NULL) {
1522 		free(total_stats);
1523 		goto done;
1524 	}
1525 
1526 	total_head->dc_statentry = total_stats;
1527 	(void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total",
1528 	    sizeof (total_head->dc_statheader));
1529 	total_head->dc_next = NULL;
1530 	free(rx_lane_head);
1531 
1532 done:
1533 	return (total_head);
1534 }
1535 
1536 /* Tx lane total statistic specific functions */
1537 void *
1538 dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1539 {
1540 	dladm_stat_chain_t	*total_head = NULL;
1541 	dladm_stat_chain_t	*tx_lane_head, *curr;
1542 	tx_lane_stat_entry_t	*total_stats;
1543 
1544 	/* Get per tx lane stats */
1545 	tx_lane_head = dlstat_tx_lane_stats(dh, linkid);
1546 	if (tx_lane_head == NULL)
1547 		goto done;
1548 
1549 	total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
1550 	if (total_stats == NULL)
1551 		goto done;
1552 
1553 	total_stats->tle_index = DLSTAT_INVALID_ENTRY;
1554 	total_stats->tle_id = DLSTAT_INVALID_ENTRY;
1555 
1556 	for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) {
1557 		tx_lane_stat_entry_t	*curr_lane_stats = curr->dc_statentry;
1558 
1559 		i_dlstat_sum_stats(&total_stats->tle_stats,
1560 		    &curr_lane_stats->tle_stats, &total_stats->tle_stats,
1561 		    tx_lane_stats_list, TX_LANE_STAT_SIZE);
1562 	}
1563 
1564 	total_head = malloc(sizeof (dladm_stat_chain_t));
1565 	if (total_head == NULL) {
1566 		free(total_stats);
1567 		goto done;
1568 	}
1569 
1570 	total_head->dc_statentry = total_stats;
1571 	(void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total",
1572 	    sizeof (total_head->dc_statheader));
1573 	total_head->dc_next = NULL;
1574 	free(tx_lane_head);
1575 
1576 done:
1577 	return (total_head);
1578 }
1579 
1580 /* Fanout specific functions */
1581 static boolean_t
1582 i_dlstat_fanout_match(void *arg1, void *arg2)
1583 {
1584 	fanout_stat_entry_t	*s1 = arg1;
1585 	fanout_stat_entry_t	*s2 = arg2;
1586 
1587 	return (s1->fe_index == s2->fe_index &&
1588 	    s1->fe_id == s2->fe_id &&
1589 	    s1->fe_foutindex == s2->fe_foutindex);
1590 }
1591 
1592 static void *
1593 i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2)
1594 {
1595 	fanout_stat_entry_t	*s1 = arg1;
1596 	fanout_stat_entry_t	*s2 = arg2;
1597 	fanout_stat_entry_t	*diff_entry;
1598 
1599 	diff_entry = malloc(sizeof (fanout_stat_entry_t));
1600 	if (diff_entry == NULL)
1601 		goto done;
1602 
1603 	diff_entry->fe_index = s1->fe_index;
1604 	diff_entry->fe_id = s1->fe_id;
1605 	diff_entry->fe_foutindex = s1->fe_foutindex;
1606 
1607 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list,
1608 	    FANOUT_STAT_SIZE);
1609 
1610 done:
1611 	return (diff_entry);
1612 }
1613 
1614 static void *
1615 i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1616 {
1617 	fanout_stat_entry_t	*fanout_stat_entry;
1618 
1619 	fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t));
1620 	if (fanout_stat_entry == NULL)
1621 		goto done;
1622 
1623 					/* Set by the caller later */
1624 	fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY;
1625 	fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY;
1626 
1627 	fanout_stat_entry->fe_foutindex = i;
1628 
1629 	i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats,
1630 	    fanout_stats_list, FANOUT_STAT_SIZE);
1631 
1632 done:
1633 	return (fanout_stat_entry);
1634 }
1635 
1636 static void *
1637 i_dlstat_query_fanout_stats(dladm_handle_t dh, datalink_id_t linkid,
1638     uint_t idlist[], uint_t idlist_size,
1639     const char *modname, const char *prefix)
1640 {
1641 	int			i;
1642 	char			statprefix[MAXLINKNAMELEN];
1643 	char			linkname[MAXLINKNAMELEN];
1644 	dladm_stat_chain_t	*curr, *curr_head;
1645 	dladm_stat_chain_t	*head = NULL, *prev = NULL;
1646 	uint_t 			fanout_idlist[MAX_RINGS_PER_GROUP];
1647 	uint_t 			fanout_idlist_size;
1648 
1649 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1650 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1651 		return (NULL);
1652 	}
1653 
1654 	i_dlstat_get_idlist(dh, linkname, DLSTAT_FANOUT_IDLIST,
1655 	    fanout_idlist, &fanout_idlist_size);
1656 
1657 	for (i = 0; i < idlist_size; i++) {
1658 		uint_t	index = idlist[i];
1659 
1660 		(void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout",
1661 		    prefix, index);
1662 
1663 		curr_head = i_dlstat_query_stats(dh, modname, statprefix,
1664 		    fanout_idlist, fanout_idlist_size,
1665 		    i_dlstat_fanout_retrieve_stat);
1666 
1667 		if (curr_head == NULL)	/* Last lane */
1668 			break;
1669 
1670 		if (head == NULL)	/* First lane */
1671 			head = curr_head;
1672 		else	/* Link new lane list to end of previous lane list */
1673 			prev->dc_next = curr_head;
1674 
1675 		/* Walk new lane list and set ids */
1676 		for (curr = curr_head; curr != NULL; curr = curr->dc_next) {
1677 			fanout_stat_entry_t *curr_stats = curr->dc_statentry;
1678 
1679 			curr_stats->fe_index = index;
1680 			curr_stats->fe_id = L_HWLANE;
1681 			/*
1682 			 * Save last pointer of previous linked list.
1683 			 * This pointer is used to chain linked lists
1684 			 * generated in each iteration.
1685 			 */
1686 			prev = curr;
1687 		}
1688 	}
1689 
1690 	return (head);
1691 }
1692 
1693 void *
1694 dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid,
1695     const char *linkname)
1696 {
1697 	return (i_dlstat_query_fanout_stats(dh, linkid,
1698 	    default_idlist, default_idlist_size, linkname,
1699 	    DLSTAT_MAC_RX_SWLANE));
1700 }
1701 
1702 void *
1703 dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid,
1704     const char *linkname)
1705 {
1706 	uint_t	rx_hwlane_idlist[MAX_RINGS_PER_GROUP];
1707 	uint_t	rx_hwlane_idlist_size;
1708 
1709 	i_dlstat_get_idlist(dh, linkname, DLSTAT_RX_HWLANE_IDLIST,
1710 	    rx_hwlane_idlist, &rx_hwlane_idlist_size);
1711 
1712 	return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist,
1713 	    rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE));
1714 }
1715 
1716 void *
1717 dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid)
1718 {
1719 	dladm_stat_chain_t	*head = NULL;
1720 	dladm_stat_chain_t	*fout_hwlane_stats;
1721 	dladm_stat_chain_t	*fout_swlane_and_local_stats;
1722 	fanout_stat_entry_t	*fout_stats;
1723 	char 			linkname[MAXLINKNAMELEN];
1724 
1725 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
1726 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1727 		goto done;
1728 	}
1729 
1730 	fout_swlane_and_local_stats =
1731 	    dlstat_fanout_swlane_and_local_stats(dh, linkid, linkname);
1732 	fout_hwlane_stats = dlstat_fanout_hwlane_stats(dh, linkid, linkname);
1733 
1734 	if (fout_swlane_and_local_stats == NULL) {
1735 		head = fout_hwlane_stats;
1736 		goto done;
1737 	}
1738 
1739 	fout_stats = fout_swlane_and_local_stats->dc_statentry;
1740 
1741 	if (fout_hwlane_stats != NULL) { /* hwlane(s), only local traffic */
1742 		fout_stats->fe_id = L_LOCAL;
1743 		fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1744 	} else { /* no hwlane, mix of local+sw classified */
1745 		fout_stats->fe_id = L_LCLSWLANE;
1746 		fout_stats->fe_index = DLSTAT_INVALID_ENTRY;
1747 	}
1748 
1749 	fout_swlane_and_local_stats->dc_next = fout_hwlane_stats;
1750 	head = fout_swlane_and_local_stats;
1751 
1752 done:
1753 	return (head);
1754 }
1755 
1756 /* Rx ring statistic specific functions */
1757 static boolean_t
1758 i_dlstat_rx_ring_match(void *arg1, void *arg2)
1759 {
1760 	rx_lane_stat_entry_t	*s1 = arg1;
1761 	rx_lane_stat_entry_t	*s2 = arg2;
1762 
1763 	return (s1->rle_index == s2->rle_index);
1764 }
1765 
1766 static void *
1767 i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
1768 {
1769 	ring_stat_entry_t 	*s1 = arg1;
1770 	ring_stat_entry_t 	*s2 = arg2;
1771 	ring_stat_entry_t 	*diff_entry;
1772 
1773 	diff_entry = malloc(sizeof (ring_stat_entry_t));
1774 	if (diff_entry == NULL)
1775 		goto done;
1776 
1777 	diff_entry->re_index	= s1->re_index;
1778 
1779 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
1780 	    RX_RING_STAT_SIZE);
1781 
1782 done:
1783 	return (diff_entry);
1784 }
1785 
1786 static void *
1787 i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1788 {
1789 	ring_stat_entry_t	*rx_ring_stat_entry;
1790 
1791 	rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1792 	if (rx_ring_stat_entry == NULL)
1793 		goto done;
1794 
1795 	rx_ring_stat_entry->re_index	= i;
1796 
1797 	i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
1798 	    rx_ring_stats_list, RX_RING_STAT_SIZE);
1799 
1800 done:
1801 	return (rx_ring_stat_entry);
1802 }
1803 
1804 void *
1805 dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1806 {
1807 	uint_t			rx_ring_idlist[MAX_RINGS_PER_GROUP];
1808 	uint_t			rx_ring_idlist_size;
1809 	dladm_phys_attr_t	dpa;
1810 	char			linkname[MAXLINKNAMELEN];
1811 	char			*modname;
1812 	datalink_class_t	class;
1813 
1814 	/*
1815 	 * kstats corresponding to physical device rings continue to use
1816 	 * device names even if the link is renamed using dladm rename-link.
1817 	 * Thus, given a linkid, we lookup the physical device name.
1818 	 * However, if an aggr is renamed, kstats corresponding to its
1819 	 * pseudo rings are renamed as well.
1820 	 */
1821 	if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1822 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1823 		return (NULL);
1824 	}
1825 
1826 	if (class != DATALINK_CLASS_AGGR) {
1827 		if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1828 		    DLADM_STATUS_OK) {
1829 			return (NULL);
1830 		}
1831 		modname = dpa.dp_dev;
1832 	} else
1833 		modname = linkname;
1834 
1835 	i_dlstat_get_idlist(dh, modname, DLSTAT_RX_RING_IDLIST,
1836 	    rx_ring_idlist, &rx_ring_idlist_size);
1837 
1838 	return (i_dlstat_query_stats(dh, modname, DLSTAT_MAC_RX_RING,
1839 	    rx_ring_idlist, rx_ring_idlist_size,
1840 	    i_dlstat_rx_ring_retrieve_stat));
1841 }
1842 
1843 /* Tx ring statistic specific functions */
1844 static boolean_t
1845 i_dlstat_tx_ring_match(void *arg1, void *arg2)
1846 {
1847 	tx_lane_stat_entry_t	*s1 = arg1;
1848 	tx_lane_stat_entry_t	*s2 = arg2;
1849 
1850 	return (s1->tle_index == s2->tle_index);
1851 }
1852 
1853 static void *
1854 i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
1855 {
1856 	ring_stat_entry_t	*s1 = arg1;
1857 	ring_stat_entry_t	*s2 = arg2;
1858 	ring_stat_entry_t	*diff_entry;
1859 
1860 	diff_entry = malloc(sizeof (ring_stat_entry_t));
1861 	if (diff_entry == NULL)
1862 		goto done;
1863 
1864 	diff_entry->re_index	= s1->re_index;
1865 
1866 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
1867 	    TX_RING_STAT_SIZE);
1868 
1869 done:
1870 	return (diff_entry);
1871 }
1872 
1873 static void *
1874 i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
1875 {
1876 	ring_stat_entry_t	*tx_ring_stat_entry;
1877 
1878 	tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
1879 	if (tx_ring_stat_entry == NULL)
1880 		goto done;
1881 
1882 	tx_ring_stat_entry->re_index	= i;
1883 
1884 	i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
1885 	    tx_ring_stats_list, TX_RING_STAT_SIZE);
1886 
1887 done:
1888 	return (tx_ring_stat_entry);
1889 }
1890 
1891 void *
1892 dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
1893 {
1894 	uint_t			tx_ring_idlist[MAX_RINGS_PER_GROUP];
1895 	uint_t			tx_ring_idlist_size;
1896 	dladm_phys_attr_t	dpa;
1897 	char			linkname[MAXLINKNAMELEN];
1898 	char			*modname;
1899 	datalink_class_t	class;
1900 
1901 	/*
1902 	 * kstats corresponding to physical device rings continue to use
1903 	 * device names even if the link is renamed using dladm rename-link.
1904 	 * Thus, given a linkid, we lookup the physical device name.
1905 	 * However, if an aggr is renamed, kstats corresponding to its
1906 	 * pseudo rings are renamed as well.
1907 	 */
1908 	if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
1909 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
1910 		return (NULL);
1911 	}
1912 
1913 	if (class != DATALINK_CLASS_AGGR) {
1914 		if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
1915 		    DLADM_STATUS_OK) {
1916 			return (NULL);
1917 		}
1918 		modname = dpa.dp_dev;
1919 	} else
1920 		modname = linkname;
1921 
1922 	i_dlstat_get_idlist(dh, modname, DLSTAT_TX_RING_IDLIST,
1923 	    tx_ring_idlist, &tx_ring_idlist_size);
1924 
1925 	return (i_dlstat_query_stats(dh, modname, DLSTAT_MAC_TX_RING,
1926 	    tx_ring_idlist, tx_ring_idlist_size,
1927 	    i_dlstat_tx_ring_retrieve_stat));
1928 }
1929 
1930 /* Rx ring total statistic specific functions */
1931 void *
1932 dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1933 {
1934 	dladm_stat_chain_t	*total_head = NULL;
1935 	dladm_stat_chain_t	*rx_ring_head, *curr;
1936 	ring_stat_entry_t	*total_stats;
1937 
1938 	/* Get per rx ring stats */
1939 	rx_ring_head = dlstat_rx_ring_stats(dh, linkid);
1940 	if (rx_ring_head == NULL)
1941 		goto done;
1942 
1943 	total_stats = calloc(1, sizeof (ring_stat_entry_t));
1944 	if (total_stats == NULL)
1945 		goto done;
1946 
1947 	total_stats->re_index = DLSTAT_INVALID_ENTRY;
1948 
1949 	for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) {
1950 		ring_stat_entry_t	*curr_ring_stats = curr->dc_statentry;
1951 
1952 		i_dlstat_sum_stats(&total_stats->re_stats,
1953 		    &curr_ring_stats->re_stats, &total_stats->re_stats,
1954 		    rx_ring_stats_list, RX_RING_STAT_SIZE);
1955 	}
1956 
1957 	total_head = malloc(sizeof (dladm_stat_chain_t));
1958 	if (total_head == NULL) {
1959 		free(total_stats);
1960 		goto done;
1961 	}
1962 
1963 	total_head->dc_statentry = total_stats;
1964 	(void) strlcpy(total_head->dc_statheader, "mac_rx_ring_total",
1965 	    sizeof (total_head->dc_statheader));
1966 	total_head->dc_next = NULL;
1967 	free(rx_ring_head);
1968 
1969 done:
1970 	return (total_head);
1971 }
1972 
1973 /* Tx ring total statistic specific functions */
1974 void *
1975 dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
1976 {
1977 	dladm_stat_chain_t	*total_head = NULL;
1978 	dladm_stat_chain_t	*tx_ring_head, *curr;
1979 	ring_stat_entry_t	*total_stats;
1980 
1981 	/* Get per tx ring stats */
1982 	tx_ring_head = dlstat_tx_ring_stats(dh, linkid);
1983 	if (tx_ring_head == NULL)
1984 		goto done;
1985 
1986 	total_stats = calloc(1, sizeof (ring_stat_entry_t));
1987 	if (total_stats == NULL)
1988 		goto done;
1989 
1990 	total_stats->re_index = DLSTAT_INVALID_ENTRY;
1991 
1992 	for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) {
1993 		ring_stat_entry_t	*curr_ring_stats = curr->dc_statentry;
1994 
1995 		i_dlstat_sum_stats(&total_stats->re_stats,
1996 		    &curr_ring_stats->re_stats, &total_stats->re_stats,
1997 		    tx_ring_stats_list, TX_RING_STAT_SIZE);
1998 	}
1999 
2000 	total_head = malloc(sizeof (dladm_stat_chain_t));
2001 	if (total_head == NULL) {
2002 		free(total_stats);
2003 		goto done;
2004 	}
2005 
2006 	total_head->dc_statentry = total_stats;
2007 	(void) strlcpy(total_head->dc_statheader, "mac_tx_ring_total",
2008 	    sizeof (total_head->dc_statheader));
2009 	total_head->dc_next = NULL;
2010 	free(tx_ring_head);
2011 
2012 done:
2013 	return (total_head);
2014 }
2015 
2016 /* Summary statistic specific functions */
2017 /*ARGSUSED*/
2018 static boolean_t
2019 i_dlstat_total_match(void *arg1, void *arg2)
2020 {
2021 	/* Always single entry for total */
2022 	return (B_TRUE);
2023 }
2024 
2025 static void *
2026 i_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
2027 {
2028 	total_stat_entry_t	*s1 = arg1;
2029 	total_stat_entry_t	*s2 = arg2;
2030 	total_stat_entry_t	*diff_entry;
2031 
2032 	diff_entry = malloc(sizeof (total_stat_entry_t));
2033 	if (diff_entry == NULL)
2034 		goto done;
2035 
2036 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
2037 	    TOTAL_STAT_SIZE);
2038 
2039 done:
2040 	return (diff_entry);
2041 }
2042 
2043 void *
2044 dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2045 {
2046 	dladm_stat_chain_t	*head = NULL;
2047 	dladm_stat_chain_t	*rx_total;
2048 	dladm_stat_chain_t	*tx_total;
2049 	total_stat_entry_t	*total_stat_entry;
2050 	rx_lane_stat_entry_t	*rx_lane_stat_entry;
2051 	tx_lane_stat_entry_t	*tx_lane_stat_entry;
2052 
2053 	/* Get total rx lane stats */
2054 	rx_total = dlstat_rx_lane_total_stats(dh, linkid);
2055 	if (rx_total == NULL)
2056 		goto done;
2057 
2058 	/* Get total tx lane stats */
2059 	tx_total = dlstat_tx_lane_total_stats(dh, linkid);
2060 	if (tx_total == NULL)
2061 		goto done;
2062 
2063 	/* Build total stat */
2064 	total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2065 	if (total_stat_entry == NULL)
2066 		goto done;
2067 
2068 	rx_lane_stat_entry = rx_total->dc_statentry;
2069 	tx_lane_stat_entry = tx_total->dc_statentry;
2070 
2071 	/* Extract total rx ipackets, rbytes */
2072 	total_stat_entry->tse_stats.ts_ipackets =
2073 	    rx_lane_stat_entry->rle_stats.rl_ipackets;
2074 	total_stat_entry->tse_stats.ts_rbytes =
2075 	    rx_lane_stat_entry->rle_stats.rl_rbytes;
2076 
2077 	/* Extract total tx opackets, obytes */
2078 	total_stat_entry->tse_stats.ts_opackets =
2079 	    tx_lane_stat_entry->tle_stats.tl_opackets;
2080 	total_stat_entry->tse_stats.ts_obytes =
2081 	    tx_lane_stat_entry->tle_stats.tl_obytes;
2082 
2083 	head = malloc(sizeof (dladm_stat_chain_t));
2084 	if (head == NULL) {
2085 		free(total_stat_entry);
2086 		goto done;
2087 	}
2088 
2089 	head->dc_statentry = total_stat_entry;
2090 	(void) strlcpy(head->dc_statheader, "mac_lane_total",
2091 	    sizeof (head->dc_statheader));
2092 	head->dc_next = NULL;
2093 	free(rx_total);
2094 	free(tx_total);
2095 
2096 done:
2097 	return (head);
2098 }
2099 
2100 /* Aggr total statistic(summed across all component ports) specific functions */
2101 void *
2102 dlstat_aggr_total_stats(dladm_stat_chain_t *head)
2103 {
2104 	dladm_stat_chain_t	*curr;
2105 	dladm_stat_chain_t	*total_head;
2106 	aggr_port_stat_entry_t	*total_stats;
2107 
2108 	total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
2109 	if (total_stats == NULL)
2110 		goto done;
2111 
2112 	total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
2113 
2114 	for (curr = head; curr != NULL; curr = curr->dc_next) {
2115 		aggr_port_stat_entry_t	*curr_aggr_port_stats;
2116 
2117 		curr_aggr_port_stats = curr->dc_statentry;
2118 
2119 		i_dlstat_sum_stats(&total_stats->ape_stats,
2120 		    &curr_aggr_port_stats->ape_stats, &total_stats->ape_stats,
2121 		    aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2122 	}
2123 
2124 	total_head = malloc(sizeof (dladm_stat_chain_t));
2125 	if (total_head == NULL) {
2126 		free(total_stats);
2127 		goto done;
2128 	}
2129 
2130 	total_head->dc_statentry = total_stats;
2131 	total_head->dc_next = NULL;
2132 
2133 done:
2134 	return (total_head);
2135 }
2136 
2137 /* Aggr port statistic specific functions */
2138 static boolean_t
2139 i_dlstat_aggr_port_match(void *arg1, void *arg2)
2140 {
2141 	aggr_port_stat_entry_t *s1 = arg1;
2142 	aggr_port_stat_entry_t *s2 = arg2;
2143 
2144 	return (s1->ape_portlinkid == s2->ape_portlinkid);
2145 }
2146 
2147 static void *
2148 i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
2149 {
2150 	aggr_port_stat_entry_t	*s1 = arg1;
2151 	aggr_port_stat_entry_t	*s2 = arg2;
2152 	aggr_port_stat_entry_t	*diff_entry;
2153 
2154 	diff_entry = malloc(sizeof (aggr_port_stat_entry_t));
2155 	if (diff_entry == NULL)
2156 		goto done;
2157 
2158 	diff_entry->ape_portlinkid = s1->ape_portlinkid;
2159 
2160 	DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
2161 	    AGGR_PORT_STAT_SIZE);
2162 
2163 done:
2164 	return (diff_entry);
2165 }
2166 
2167 /*
2168  * Query dls stats for the aggr port. This results in query for stats into
2169  * the corresponding device driver.
2170  */
2171 static aggr_port_stat_entry_t *
2172 i_dlstat_single_port_stats(dladm_handle_t handle, const char *portname,
2173     datalink_id_t linkid)
2174 {
2175 	kstat_t			*ksp;
2176 	char			module[DLPI_LINKNAME_MAX];
2177 	uint_t			instance;
2178 	aggr_port_stat_entry_t	*aggr_port_stat_entry = NULL;
2179 
2180 	if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK)
2181 		goto done;
2182 
2183 	if (dladm_dld_kcp(handle) == NULL) {
2184 		warn("kstat open operation failed");
2185 		return (NULL);
2186 	}
2187 
2188 	ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), module, instance,
2189 	    "mac", NULL);
2190 	if (ksp == NULL)
2191 		goto done;
2192 
2193 	aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2194 	if (aggr_port_stat_entry == NULL)
2195 		goto done;
2196 
2197 	/* Save port's linkid */
2198 	aggr_port_stat_entry->ape_portlinkid = linkid;
2199 
2200 	i_dlstat_get_stats(dladm_dld_kcp(handle), ksp,
2201 	    &aggr_port_stat_entry->ape_stats,
2202 	    aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2203 done:
2204 	return (aggr_port_stat_entry);
2205 }
2206 
2207 void *
2208 dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
2209 {
2210 	dladm_aggr_grp_attr_t	ginfo;
2211 	int			i;
2212 	dladm_aggr_port_attr_t	 *portp;
2213 	dladm_phys_attr_t	dpa;
2214 	aggr_port_stat_entry_t	*aggr_port_stat_entry;
2215 	dladm_stat_chain_t	*head = NULL, *prev = NULL, *curr;
2216 	dladm_stat_chain_t	*total_stats;
2217 
2218 	/* Get aggr info */
2219 	bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
2220 	if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE)
2221 	    != DLADM_STATUS_OK)
2222 		goto done;
2223 	/* For every port that is member of this aggr do */
2224 	for (i = 0; i < ginfo.lg_nports; i++) {
2225 		portp = &(ginfo.lg_ports[i]);
2226 		if (dladm_phys_info(dh, portp->lp_linkid, &dpa,
2227 		    DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
2228 			goto done;
2229 		}
2230 
2231 		aggr_port_stat_entry = i_dlstat_single_port_stats(dh,
2232 		    dpa.dp_dev, portp->lp_linkid);
2233 
2234 		/* Create dladm_stat_chain_t object for this stat */
2235 		curr = malloc(sizeof (dladm_stat_chain_t));
2236 		if (curr == NULL) {
2237 			free(aggr_port_stat_entry);
2238 			goto done;
2239 		}
2240 		(void) strlcpy(curr->dc_statheader, dpa.dp_dev,
2241 		    sizeof (curr->dc_statheader));
2242 		curr->dc_statentry = aggr_port_stat_entry;
2243 		curr->dc_next = NULL;
2244 
2245 		/* Chain this aggr port stat entry */
2246 		/* head of the stat list */
2247 		if (prev == NULL)
2248 			head = curr;
2249 		else
2250 			prev->dc_next = curr;
2251 		prev = curr;
2252 	}
2253 
2254 	/*
2255 	 * Prepend the stat list with cumulative aggr stats i.e. summed over all
2256 	 * component ports
2257 	 */
2258 	total_stats = dlstat_aggr_total_stats(head);
2259 	if (total_stats != NULL) {
2260 		total_stats->dc_next = head;
2261 		head = total_stats;
2262 	}
2263 
2264 done:
2265 	free(ginfo.lg_ports);
2266 	return (head);
2267 }
2268 
2269 /* Misc stat specific functions */
2270 void *
2271 dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid)
2272 {
2273 	misc_stat_entry_t	*misc_stat_entry;
2274 	dladm_stat_chain_t	*head = NULL;
2275 	char 			linkname[MAXLINKNAMELEN];
2276 
2277 	if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2278 	    DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2279 		goto done;
2280 	}
2281 
2282 	misc_stat_entry = i_dlstat_misc_stats(dh, linkname);
2283 	if (misc_stat_entry == NULL)
2284 		goto done;
2285 
2286 	head = malloc(sizeof (dladm_stat_chain_t));
2287 	if (head == NULL) {
2288 		free(misc_stat_entry);
2289 		goto done;
2290 	}
2291 
2292 	head->dc_statentry = misc_stat_entry;
2293 	(void) strlcpy(head->dc_statheader, "mac_misc_stat",
2294 	    sizeof (head->dc_statheader));
2295 	head->dc_next = NULL;
2296 
2297 done:
2298 	return (head);
2299 }
2300 
2301 /* Exported functions */
2302 dladm_stat_chain_t *
2303 dladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid,
2304     dladm_stat_type_t stattype)
2305 {
2306 	return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
2307 }
2308 
2309 dladm_stat_chain_t *
2310 dladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2,
2311     dladm_stat_type_t stattype)
2312 {
2313 	dladm_stat_chain_t	*op1_curr, *op2_curr;
2314 	dladm_stat_chain_t	*diff_curr;
2315 	dladm_stat_chain_t	*diff_prev = NULL, *diff_head = NULL;
2316 
2317 				/* Perform op1 - op2, store result in diff */
2318 	for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) {
2319 		for (op2_curr = op2; op2_curr != NULL;
2320 		    op2_curr = op2_curr->dc_next) {
2321 			if (dlstat_match_stats(op1_curr->dc_statentry,
2322 			    op2_curr->dc_statentry, stattype)) {
2323 				break;
2324 			}
2325 		}
2326 		diff_curr = malloc(sizeof (dladm_stat_chain_t));
2327 		if (diff_curr == NULL)
2328 			goto done;
2329 
2330 		diff_curr->dc_next = NULL;
2331 
2332 		if (op2_curr == NULL) {
2333 			/* prev iteration did not have this stat entry */
2334 			diff_curr->dc_statentry =
2335 			    dlstat_diff_stats(op1_curr->dc_statentry,
2336 			    NULL, stattype);
2337 		} else {
2338 			diff_curr->dc_statentry =
2339 			    dlstat_diff_stats(op1_curr->dc_statentry,
2340 			    op2_curr->dc_statentry, stattype);
2341 		}
2342 
2343 		if (diff_curr->dc_statentry == NULL) {
2344 			free(diff_curr);
2345 			goto done;
2346 		}
2347 
2348 		if (diff_prev == NULL) /* head of the diff stat list */
2349 			diff_head = diff_curr;
2350 		else
2351 			diff_prev->dc_next = diff_curr;
2352 		diff_prev = diff_curr;
2353 	}
2354 done:
2355 	return (diff_head);
2356 }
2357 
2358 void
2359 dladm_link_stat_free(dladm_stat_chain_t *curr)
2360 {
2361 	while (curr != NULL) {
2362 		dladm_stat_chain_t	*tofree = curr;
2363 
2364 		curr = curr->dc_next;
2365 		free(tofree->dc_statentry);
2366 		free(tofree);
2367 	}
2368 }
2369 
2370 /* Query all link stats */
2371 static name_value_stat_t *
2372 i_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size)
2373 {
2374 	int			i;
2375 	name_value_stat_t	*head_stat = NULL, *prev_stat = NULL;
2376 	name_value_stat_t	*curr_stat;
2377 
2378 	for (i = 0; i < size; i++) {
2379 		uint64_t *val = (void *)
2380 		    ((uchar_t *)stats + stats_list[i].si_offset);
2381 
2382 		curr_stat = calloc(1, sizeof (name_value_stat_t));
2383 		if (curr_stat == NULL)
2384 			break;
2385 
2386 		(void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name,
2387 		    sizeof (curr_stat->nv_statname));
2388 		curr_stat->nv_statval = *val;
2389 		curr_stat->nv_nextstat = NULL;
2390 
2391 		if (head_stat == NULL)	/* First node */
2392 			head_stat = curr_stat;
2393 		else
2394 			prev_stat->nv_nextstat = curr_stat;
2395 
2396 		prev_stat = curr_stat;
2397 	}
2398 	return (head_stat);
2399 }
2400 
2401 void *
2402 build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
2403 {
2404 	name_value_stat_entry_t	*name_value_stat_entry;
2405 	dladm_stat_desc_t	*stattbl_ptr;
2406 	void			*statfields;
2407 
2408 	stattbl_ptr = &dladm_stat_table[stattype];
2409 
2410 	/* Allocate memory for query all stat entry */
2411 	name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2412 	if (name_value_stat_entry == NULL)
2413 		goto done;
2414 
2415 	/* Header for these stat fields */
2416 	(void) strlcpy(name_value_stat_entry->nve_header, statheader,
2417 	    sizeof (name_value_stat_entry->nve_header));
2418 
2419 	/* Extract stat fields from the statentry */
2420 	statfields = (uchar_t *)statentry +
2421 	    dladm_stat_table[stattype].ds_offset;
2422 
2423 	/* Convert curr_stat to <statname, statval> pair */
2424 	name_value_stat_entry->nve_stats =
2425 	    i_dlstat_convert_stats(statfields,
2426 	    stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize);
2427 done:
2428 	return (name_value_stat_entry);
2429 }
2430 
2431 void *
2432 i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
2433 {
2434 	dladm_stat_chain_t	*curr;
2435 	dladm_stat_chain_t	*nvstat_head = NULL, *nvstat_prev = NULL;
2436 	dladm_stat_chain_t	*nvstat_curr;
2437 
2438 	/*
2439 	 * For every stat in the chain, build header and convert all
2440 	 * its stat fields
2441 	 */
2442 	for (curr = stat_head; curr != NULL; curr = curr->dc_next) {
2443 		nvstat_curr = malloc(sizeof (dladm_stat_chain_t));
2444 		if (nvstat_curr == NULL)
2445 			break;
2446 
2447 		nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
2448 		    curr->dc_statentry, stattype);
2449 
2450 		if (nvstat_curr->dc_statentry == NULL) {
2451 			free(nvstat_curr);
2452 			break;
2453 		}
2454 
2455 		nvstat_curr->dc_next = NULL;
2456 
2457 		if (nvstat_head == NULL)	/* First node */
2458 			nvstat_head = nvstat_curr;
2459 		else
2460 			nvstat_prev->dc_next = nvstat_curr;
2461 
2462 		nvstat_prev = nvstat_curr;
2463 	}
2464 done:
2465 	return (nvstat_head);
2466 }
2467 
2468 dladm_stat_chain_t *
2469 dladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid,
2470     dladm_stat_type_t stattype)
2471 {
2472 	dladm_stat_chain_t	*stat_head;
2473 	dladm_stat_chain_t	*nvstat_head = NULL;
2474 
2475 	/* Query the requested stat */
2476 	stat_head = dladm_link_stat_query(dh, linkid, stattype);
2477 	if (stat_head == NULL)
2478 		goto done;
2479 
2480 	/*
2481 	 * Convert every statfield in every stat-entry of stat chain to
2482 	 * <statname, statval> pair
2483 	 */
2484 	nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
2485 
2486 	/* Free stat_head */
2487 	dladm_link_stat_free(stat_head);
2488 
2489 done:
2490 	return (nvstat_head);
2491 }
2492 
2493 void
2494 dladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
2495 {
2496 	while (curr != NULL) {
2497 		dladm_stat_chain_t	*tofree = curr;
2498 		name_value_stat_entry_t	*nv_entry = curr->dc_statentry;
2499 		name_value_stat_t	*nv_curr = nv_entry->nve_stats;
2500 
2501 		while (nv_curr != NULL) {
2502 			name_value_stat_t	*nv_tofree = nv_curr;
2503 
2504 			nv_curr = nv_curr->nv_nextstat;
2505 			free(nv_tofree);
2506 		}
2507 
2508 		curr = curr->dc_next;
2509 		free(nv_entry);
2510 		free(tofree);
2511 	}
2512 }
2513 
2514 /* flow stats specific routines */
2515 flow_stat_t *
2516 dladm_flow_stat_query(dladm_handle_t handle, const char *flowname)
2517 {
2518 	kstat_t		*ksp;
2519 	flow_stat_t	*flow_stat = NULL;
2520 
2521 	if (dladm_dld_kcp(handle) == NULL)
2522 		return (NULL);
2523 
2524 	flow_stat = calloc(1, sizeof (flow_stat_t));
2525 	if (flow_stat == NULL)
2526 		goto done;
2527 
2528 	ksp = dladm_kstat_lookup(dladm_dld_kcp(handle), NULL, -1, flowname,
2529 	    "flow");
2530 
2531 	if (ksp != NULL) {
2532 		i_dlstat_get_stats(dladm_dld_kcp(handle), ksp, flow_stat,
2533 		    flow_stats_list, FLOW_STAT_SIZE);
2534 	}
2535 
2536 done:
2537 	return (flow_stat);
2538 }
2539 
2540 flow_stat_t *
2541 dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
2542 {
2543 	flow_stat_t	*diff_stat;
2544 
2545 	diff_stat = calloc(1, sizeof (flow_stat_t));
2546 	if (diff_stat == NULL)
2547 		goto done;
2548 
2549 	if (op2 == NULL) {
2550 		bcopy(op1, diff_stat, sizeof (flow_stat_t));
2551 	} else {
2552 		i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list,
2553 		    FLOW_STAT_SIZE);
2554 	}
2555 done:
2556 	return (diff_stat);
2557 }
2558 
2559 void
2560 dladm_flow_stat_free(flow_stat_t *curr)
2561 {
2562 	free(curr);
2563 }
2564 
2565 /* Query all flow stats */
2566 name_value_stat_entry_t *
2567 dladm_flow_stat_query_all(dladm_handle_t handle, const char *flowname)
2568 {
2569 	flow_stat_t		*flow_stat;
2570 	name_value_stat_entry_t	*name_value_stat_entry = NULL;
2571 
2572 	/* Query flow stats */
2573 	flow_stat = dladm_flow_stat_query(handle, flowname);
2574 	if (flow_stat == NULL)
2575 		goto done;
2576 
2577 	/* Allocate memory for query all stat entry */
2578 	name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2579 	if (name_value_stat_entry == NULL) {
2580 		dladm_flow_stat_free(flow_stat);
2581 		goto done;
2582 	}
2583 
2584 	/* Header for these stat fields */
2585 	(void) strncpy(name_value_stat_entry->nve_header, flowname,
2586 	    MAXFLOWNAMELEN);
2587 
2588 	/* Convert every statfield in flow_stat to <statname, statval> pair */
2589 	name_value_stat_entry->nve_stats =
2590 	    i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE);
2591 
2592 	/* Free flow_stat */
2593 	dladm_flow_stat_free(flow_stat);
2594 
2595 done:
2596 	return (name_value_stat_entry);
2597 }
2598 
2599 void
2600 dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
2601 {
2602 	name_value_stat_t	*nv_curr = curr->nve_stats;
2603 
2604 	while (nv_curr != NULL) {
2605 		name_value_stat_t	*nv_tofree = nv_curr;
2606 
2607 		nv_curr = nv_curr->nv_nextstat;
2608 		free(nv_tofree);
2609 	}
2610 }
2611