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