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