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