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