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