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 *
findstat(const char * flowname,datalink_id_t linkid)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
print_flow_stats(dladm_handle_t handle,struct flowlist * flist)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
flow_kstats(dladm_handle_t handle,dladm_flow_attr_t * attr,void * arg)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
print_link_stats(dladm_handle_t handle,struct flowlist * flist)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
link_flowstats(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
link_kstats(dladm_handle_t handle,datalink_id_t linkid,void * arg)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
closedevnet()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
sig_break(int s)362 sig_break(int s)
363 {
364 handle_break = 1;
365 }
366
367 /*ARGSUSED*/
368 static void
sig_resize(int s)369 sig_resize(int s)
370 {
371 handle_resize = 1;
372 }
373
374 static void
curses_init()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
curses_fin()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
stat_report(dladm_handle_t handle,kstat_ctl_t * kcp,datalink_id_t linkid,const char * flowname,int opt)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
dladm_continuous(dladm_handle_t handle,datalink_id_t linkid,const char * flowname,int interval,int opt)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 *
dladm_kstat_lookup(kstat_ctl_t * kcp,const char * module,int instance,const char * name,const char * class)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
dladm_get_stats(kstat_ctl_t * kcp,kstat_t * ksp,pktsum_t * stats)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
dladm_kstat_value(kstat_t * ksp,const char * name,uint8_t type,void * buf)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
dladm_get_single_mac_stat(dladm_handle_t handle,datalink_id_t linkid,const char * name,uint8_t type,void * val)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
dladm_stats_total(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)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
dladm_stats_diff(pktsum_t * s1,pktsum_t * s2,pktsum_t * s3)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 *
dlstat_diff_stats(void * arg1,void * arg2,dladm_stat_type_t stattype)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
dlstat_match_stats(void * arg1,void * arg2,dladm_stat_type_t stattype)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
i_dlstat_diff_stats(void * diff,void * op1,void * op2,stat_info_t stats_list[],uint_t size)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
i_dlstat_sum_stats(void * sum,void * op1,void * op2,stat_info_t stats_list[],uint_t size)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
i_dlstat_get_stats(kstat_ctl_t * kcp,kstat_t * ksp,void * stats,stat_info_t stats_list[],uint_t size)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 *
i_dlstat_join_lists(dladm_stat_chain_t * list1,dladm_stat_chain_t * list2)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
dladm_sort_index_list(uint_t idlist[],uint_t size)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
i_query_legacy_stats(const char * linkname,pktsum_t * stats)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 *
i_dlstat_legacy_rx_lane_stats(const char * linkname)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 *
i_dlstat_legacy_tx_lane_stats(const char * linkname)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
i_dlstat_rx_hwlane_search(kstat_t * ksp)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
i_dlstat_tx_hwlane_search(kstat_t * ksp)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
i_dlstat_fanout_search(kstat_t * ksp)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
i_dlstat_rx_ring_search(kstat_t * ksp)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
i_dlstat_tx_ring_search(kstat_t * ksp)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
i_dlstat_get_idlist(const char * modname,dlstat_idlist_type_t idlist_type,uint_t idlist[],uint_t * size)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 *
i_dlstat_query_stats(const char * modname,const char * prefix,uint_t idlist[],uint_t idlist_size,void * (* fn)(kstat_ctl_t *,kstat_t *,int))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 *
i_dlstat_misc_stats(const char * linkname)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
i_dlstat_rx_lane_match(void * arg1,void * arg2)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 *
i_dlstat_rx_lane_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_rx_local_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_rx_local_stats(const char * linkname)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 *
i_dlstat_rx_bcast_stats(const char * linkname)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 *
i_dlstat_rx_defunctlane_stats(const char * linkname)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 *
i_dlstat_rx_hwlane_stats(const char * linkname)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 *
i_dlstat_rx_swlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)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 *
dlstat_rx_lane_stats(dladm_handle_t dh,datalink_id_t linkid)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
i_dlstat_tx_lane_match(void * arg1,void * arg2)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 *
i_dlstat_tx_lane_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_tx_bcast_stats(const char * linkname)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 *
i_dlstat_tx_defunctlane_stats(const char * linkname)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 *
i_dlstat_tx_hwlane_stats(const char * linkname)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 *
i_dlstat_tx_swlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)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 *
dlstat_tx_lane_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_rx_lane_total_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_tx_lane_total_stats(dladm_handle_t dh,datalink_id_t linkid)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
i_dlstat_fanout_match(void * arg1,void * arg2)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 *
i_dlstat_fanout_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_fanout_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
i_dlstat_query_fanout_stats(dladm_handle_t dh,datalink_id_t linkid,uint_t idlist[],uint_t idlist_size,const char * modname,const char * prefix)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 *
dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)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 *
dlstat_fanout_hwlane_stats(dladm_handle_t dh,datalink_id_t linkid,const char * linkname)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 *
dlstat_fanout_stats(dladm_handle_t dh,datalink_id_t linkid)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
i_dlstat_rx_ring_match(void * arg1,void * arg2)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 *
i_dlstat_rx_ring_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
dlstat_rx_ring_stats(dladm_handle_t dh,datalink_id_t linkid)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
i_dlstat_tx_ring_match(void * arg1,void * arg2)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 *
i_dlstat_tx_ring_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t * kcp,kstat_t * ksp,int i)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 *
dlstat_tx_ring_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_rx_ring_total_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_tx_ring_total_stats(dladm_handle_t dh,datalink_id_t linkid)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
i_dlstat_total_match(void * arg1,void * arg2)2498 i_dlstat_total_match(void *arg1, void *arg2)
2499 { /* Always single entry for total */
2500 return (B_TRUE);
2501 }
2502
2503 static void *
i_dlstat_total_stat_entry_diff(void * arg1,void * arg2)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 *
dlstat_total_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_aggr_total_stats(dladm_stat_chain_t * head)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
i_dlstat_aggr_port_match(void * arg1,void * arg2)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 *
i_dlstat_aggr_port_stat_entry_diff(void * arg1,void * arg2)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 *
i_dlstat_single_port_stats(const char * portname,datalink_id_t linkid)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 *
dlstat_aggr_port_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dlstat_misc_stats(dladm_handle_t dh,datalink_id_t linkid)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 *
dladm_link_stat_query(dladm_handle_t dh,datalink_id_t linkid,dladm_stat_type_t stattype)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 *
dladm_link_stat_diffchain(dladm_stat_chain_t * op1,dladm_stat_chain_t * op2,dladm_stat_type_t stattype)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
dladm_link_stat_free(dladm_stat_chain_t * curr)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 *
i_dlstat_convert_stats(void * stats,stat_info_t stats_list[],uint_t size)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 *
build_nvs_entry(char * statheader,void * statentry,dladm_stat_type_t stattype)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 *
i_walk_dlstat_chain(dladm_stat_chain_t * stat_head,dladm_stat_type_t stattype)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 *
dladm_link_stat_query_all(dladm_handle_t dh,datalink_id_t linkid,dladm_stat_type_t stattype)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
dladm_link_stat_query_all_free(dladm_stat_chain_t * curr)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 *
dladm_flow_stat_query(const char * flowname)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 *
dladm_flow_stat_diff(flow_stat_t * op1,flow_stat_t * op2)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
dladm_flow_stat_free(flow_stat_t * curr)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 *
dladm_flow_stat_query_all(const char * flowname)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
dladm_flow_stat_query_all_free(name_value_stat_entry_t * curr)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