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