/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2010 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * x86 ERR conflicts with ERR. * Include curses.h last. */ #if defined(ERR) #undef ERR #endif #include struct flowlist { char flowname[MAXFLOWNAMELEN]; char linkname[MAXLINKNAMELEN]; datalink_id_t linkid; int fd; uint64_t ifspeed; boolean_t first; boolean_t display; pktsum_t prevstats; pktsum_t diffstats; }; static int maxx, maxy, redraw = 0; static volatile uint_t handle_resize = 0, handle_break = 0; pktsum_t totalstats; struct flowlist *stattable = NULL; static int statentry = -1, maxstatentries = 0; #define STATGROWSIZE 16 /* * Search for flowlist entry in stattable which matches * the flowname and linkid. If no match is found, use * next available slot. If no slots are available, * reallocate table with more slots. * * Return: *flowlist of matching flow * NULL if realloc fails */ static struct flowlist * findstat(const char *flowname, datalink_id_t linkid) { int match = 0; struct flowlist *flist; /* Look for match in the stattable */ for (match = 0, flist = stattable; match <= statentry; match++, flist++) { if (flist == NULL) break; /* match the flowname */ if (flowname != NULL) { if (strncmp(flowname, flist->flowname, MAXFLOWNAMELEN) == NULL) return (flist); /* match the linkid */ } else { if (linkid == flist->linkid) return (flist); } } /* * No match found in the table. Store statistics in the next slot. * If necessary, make room for this entry. */ statentry++; if ((maxstatentries == 0) || (maxstatentries == statentry)) { maxstatentries += STATGROWSIZE; stattable = realloc(stattable, maxstatentries * sizeof (struct flowlist)); if (stattable == NULL) { perror("realloc"); return (struct flowlist *)(NULL); } } flist = &stattable[statentry]; bzero(flist, sizeof (struct flowlist)); if (flowname != NULL) (void) strncpy(flist->flowname, flowname, MAXFLOWNAMELEN); flist->linkid = linkid; flist->fd = INT32_MAX; return (flist); } /*ARGSUSED*/ static void print_flow_stats(dladm_handle_t handle, struct flowlist *flist) { struct flowlist *fcurr; double ikbs, okbs; double ipks, opks; double dlt; int fcount; static boolean_t first = B_TRUE; if (first) { first = B_FALSE; (void) printw("please wait...\n"); return; } for (fcount = 0, fcurr = flist; fcount <= statentry; fcount++, fcurr++) { if (fcurr->flowname && fcurr->display) { dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024; okbs = fcurr->diffstats.obytes * 8 / dlt / 1024; ipks = fcurr->diffstats.ipackets / dlt; opks = fcurr->diffstats.opackets / dlt; (void) printw("%-15.15s", fcurr->flowname); (void) printw("%-10.10s", fcurr->linkname); (void) printw("%9.2f %9.2f %9.2f %9.2f ", ikbs, okbs, ipks, opks); (void) printw("\n"); } } } /*ARGSUSED*/ static int flow_kstats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg) { kstat_ctl_t *kcp = (kstat_ctl_t *)arg; kstat_t *ksp; struct flowlist *flist; pktsum_t currstats, *prevstats, *diffstats; flist = findstat(attr->fa_flowname, attr->fa_linkid); if (flist == NULL) return (DLADM_WALK_CONTINUE); flist->display = B_FALSE; prevstats = &flist->prevstats; diffstats = &flist->diffstats; (void) dladm_datalink_id2info(handle, attr->fa_linkid, NULL, NULL, NULL, flist->linkname, sizeof (flist->linkname)); /* lookup kstat entry */ ksp = dladm_kstat_lookup(kcp, NULL, -1, attr->fa_flowname, "flow"); if (ksp == NULL) return (DLADM_WALK_CONTINUE); /* read packet and byte stats */ dladm_get_stats(kcp, ksp, &currstats); if (flist->ifspeed == 0) (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, &flist->ifspeed); if (flist->first) { flist->first = B_FALSE; } else { dladm_stats_diff(diffstats, &currstats, prevstats); if (diffstats->snaptime == 0) return (DLADM_WALK_CONTINUE); dladm_stats_total(&totalstats, diffstats, &totalstats); } bcopy(&currstats, prevstats, sizeof (pktsum_t)); flist->display = B_TRUE; return (DLADM_WALK_CONTINUE); } /*ARGSUSED*/ static void print_link_stats(dladm_handle_t handle, struct flowlist *flist) { struct flowlist *fcurr; double ikbs, okbs; double ipks, opks; double util; double dlt; int fcount; static boolean_t first = B_TRUE; if (first) { first = B_FALSE; (void) printw("please wait...\n"); return; } for (fcount = 0, fcurr = flist; fcount <= statentry; fcount++, fcurr++) { if ((fcurr->linkid != DATALINK_INVALID_LINKID) && fcurr->display) { dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC; ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024; okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024; ipks = (double)fcurr->diffstats.ipackets / dlt; opks = (double)fcurr->diffstats.opackets / dlt; (void) printw("%-10.10s", fcurr->linkname); (void) printw("%9.2f %9.2f %9.2f %9.2f ", ikbs, okbs, ipks, opks); if (fcurr->ifspeed != 0) util = ((ikbs + okbs) * 1024) * 100/ fcurr->ifspeed; else util = (double)0; (void) attron(A_BOLD); (void) printw(" %6.2f", util); (void) attroff(A_BOLD); (void) printw("\n"); } } } /* * This function is called through the dladm_walk_datalink_id() walker and * calls the dladm_walk_flow() walker. */ /*ARGSUSED*/ static int link_flowstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) { dladm_status_t status; status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE); if (status == DLADM_STATUS_OK) return (DLADM_WALK_CONTINUE); else return (DLADM_WALK_TERMINATE); } /*ARGSUSED*/ static int link_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg) { kstat_ctl_t *kcp = (kstat_ctl_t *)arg; struct flowlist *flist; pktsum_t currstats, *prevstats, *diffstats; datalink_class_t class; kstat_t *ksp; char dnlink[MAXPATHLEN]; /* find the flist entry */ flist = findstat(NULL, linkid); flist->display = B_FALSE; if (flist != NULL) { prevstats = &flist->prevstats; diffstats = &flist->diffstats; } else { return (DLADM_WALK_CONTINUE); } if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL, flist->linkname, sizeof (flist->linkname)) != DLADM_STATUS_OK) return (DLADM_WALK_CONTINUE); if (flist->fd == INT32_MAX) { if (class == DATALINK_CLASS_PHYS) { (void) snprintf(dnlink, MAXPATHLEN, "/dev/net/%s", flist->linkname); if ((flist->fd = open(dnlink, O_RDWR)) < 0) return (DLADM_WALK_CONTINUE); } else { flist->fd = -1; } (void) kstat_chain_update(kcp); } /* lookup kstat entry */ ksp = dladm_kstat_lookup(kcp, NULL, -1, flist->linkname, "net"); if (ksp == NULL) return (DLADM_WALK_CONTINUE); /* read packet and byte stats */ dladm_get_stats(kcp, ksp, &currstats); if (flist->ifspeed == 0) (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64, &flist->ifspeed); if (flist->first) { flist->first = B_FALSE; } else { dladm_stats_diff(diffstats, &currstats, prevstats); if (diffstats->snaptime == 0) return (DLADM_WALK_CONTINUE); } bcopy(&currstats, prevstats, sizeof (*prevstats)); flist->display = B_TRUE; return (DLADM_WALK_CONTINUE); } static void closedevnet() { int index = 0; struct flowlist *flist; /* Close all open /dev/net/ files */ for (flist = stattable; index < maxstatentries; index++, flist++) { if (flist->linkid == DATALINK_INVALID_LINKID) break; if (flist->fd != -1 && flist->fd != INT32_MAX) (void) close(flist->fd); } } /*ARGSUSED*/ static void sig_break(int s) { handle_break = 1; } /*ARGSUSED*/ static void sig_resize(int s) { handle_resize = 1; } static void curses_init() { maxx = maxx; /* lint */ maxy = maxy; /* lint */ /* Install signal handlers */ (void) signal(SIGINT, sig_break); (void) signal(SIGQUIT, sig_break); (void) signal(SIGTERM, sig_break); (void) signal(SIGWINCH, sig_resize); /* Initialize ncurses */ (void) initscr(); (void) cbreak(); (void) noecho(); (void) curs_set(0); timeout(0); getmaxyx(stdscr, maxy, maxx); } static void curses_fin() { (void) printw("\n"); (void) curs_set(1); (void) nocbreak(); (void) endwin(); free(stattable); } static void stat_report(dladm_handle_t handle, kstat_ctl_t *kcp, datalink_id_t linkid, const char *flowname, int opt) { double dlt, ikbs, okbs, ipks, opks; struct flowlist *fstable = stattable; if ((opt != LINK_REPORT) && (opt != FLOW_REPORT)) return; /* Handle window resizes */ if (handle_resize) { (void) endwin(); (void) initscr(); (void) cbreak(); (void) noecho(); (void) curs_set(0); timeout(0); getmaxyx(stdscr, maxy, maxx); redraw = 1; handle_resize = 0; } /* Print title */ (void) erase(); (void) attron(A_BOLD); (void) move(0, 0); if (opt == FLOW_REPORT) (void) printw("%-15.15s", "Flow"); (void) printw("%-10.10s", "Link"); (void) printw("%9.9s %9.9s %9.9s %9.9s ", "iKb/s", "oKb/s", "iPk/s", "oPk/s"); if (opt == LINK_REPORT) (void) printw(" %6.6s", "%Util"); (void) printw("\n"); (void) attroff(A_BOLD); (void) move(2, 0); /* Print stats for each link or flow */ bzero(&totalstats, sizeof (totalstats)); if (opt == LINK_REPORT) { /* Display all links */ if (linkid == DATALINK_ALL_LINKID) { (void) dladm_walk_datalink_id(link_kstats, handle, (void *)kcp, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); /* Display 1 link */ } else { (void) link_kstats(handle, linkid, kcp); } print_link_stats(handle, fstable); } else if (opt == FLOW_REPORT) { /* Display 1 flow */ if (flowname != NULL) { dladm_flow_attr_t fattr; if (dladm_flow_info(handle, flowname, &fattr) != DLADM_STATUS_OK) return; (void) flow_kstats(handle, &fattr, kcp); /* Display all flows on all links */ } else if (linkid == DATALINK_ALL_LINKID) { (void) dladm_walk_datalink_id(link_flowstats, handle, (void *)kcp, DATALINK_CLASS_ALL, DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE); /* Display all flows on a link */ } else if (linkid != DATALINK_INVALID_LINKID) { (void) dladm_walk_flow(flow_kstats, handle, linkid, kcp, B_FALSE); } print_flow_stats(handle, fstable); /* Print totals */ (void) attron(A_BOLD); dlt = (double)totalstats.snaptime / (double)NANOSEC; ikbs = totalstats.rbytes / dlt / 1024; okbs = totalstats.obytes / dlt / 1024; ipks = totalstats.ipackets / dlt; opks = totalstats.opackets / dlt; (void) printw("\n%-25.25s", "Totals"); (void) printw("%9.2f %9.2f %9.2f %9.2f ", ikbs, okbs, ipks, opks); (void) attroff(A_BOLD); } if (redraw) (void) clearok(stdscr, 1); if (refresh() == ERR) return; if (redraw) { (void) clearok(stdscr, 0); redraw = 0; } } /* Exported functions */ /* * Continuously display link or flow statstics using a libcurses * based display. */ void dladm_continuous(dladm_handle_t handle, datalink_id_t linkid, const char *flowname, int interval, int opt) { kstat_ctl_t *kcp; if ((kcp = kstat_open()) == NULL) { warn("kstat open operation failed"); return; } curses_init(); for (;;) { if (handle_break) break; stat_report(handle, kcp, linkid, flowname, opt); (void) sleep(max(1, interval)); } closedevnet(); curses_fin(); (void) kstat_close(kcp); } /* * dladm_kstat_lookup() is a modified version of kstat_lookup which * adds the class as a selector. */ kstat_t * dladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance, const char *name, const char *class) { kstat_t *ksp = NULL; for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { if ((module == NULL || strcmp(ksp->ks_module, module) == 0) && (instance == -1 || ksp->ks_instance == instance) && (name == NULL || strcmp(ksp->ks_name, name) == 0) && (class == NULL || strcmp(ksp->ks_class, class) == 0)) return (ksp); } errno = ENOENT; return (NULL); } /* * dladm_get_stats() populates the supplied pktsum_t structure with * the input and output packet and byte kstats from the kstat_t * found with dladm_kstat_lookup. */ void dladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats) { if (kstat_read(kcp, ksp, NULL) == -1) return; stats->snaptime = gethrtime(); if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64, &stats->ipackets) < 0) { if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64, &stats->ipackets) < 0) return; } if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64, &stats->opackets) < 0) { if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64, &stats->opackets) < 0) return; } if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64, &stats->rbytes) < 0) { if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64, &stats->rbytes) < 0) return; } if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64, &stats->obytes) < 0) { if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64, &stats->obytes) < 0) return; } if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32, &stats->ierrors) < 0) { if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64, &stats->ierrors) < 0) return; } if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32, &stats->oerrors) < 0) { if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64, &stats->oerrors) < 0) return; } } int dladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf) { kstat_named_t *knp; if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL) return (-1); if (knp->data_type != type) return (-1); switch (type) { case KSTAT_DATA_UINT64: *(uint64_t *)buf = knp->value.ui64; break; case KSTAT_DATA_UINT32: *(uint32_t *)buf = knp->value.ui32; break; default: return (-1); } return (0); } dladm_status_t dladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid, const char *name, uint8_t type, void *val) { kstat_ctl_t *kcp; char module[DLPI_LINKNAME_MAX]; uint_t instance; char link[DLPI_LINKNAME_MAX]; dladm_status_t status; uint32_t flags, media; kstat_t *ksp; dladm_phys_attr_t dpap; if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK) return (status); if (media != DL_ETHER) return (DLADM_STATUS_LINKINVAL); status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST); if (status != DLADM_STATUS_OK) return (status); status = dladm_parselink(dpap.dp_dev, module, &instance); if (status != DLADM_STATUS_OK) return (status); if ((kcp = kstat_open()) == NULL) { warn("kstat_open operation failed"); return (-1); } /* * The kstat query could fail if the underlying MAC * driver was already detached. */ if ((ksp = kstat_lookup(kcp, module, instance, "mac")) == NULL && (ksp = kstat_lookup(kcp, module, instance, NULL)) == NULL) goto bail; if (kstat_read(kcp, ksp, NULL) == -1) goto bail; if (dladm_kstat_value(ksp, name, type, val) < 0) goto bail; (void) kstat_close(kcp); return (DLADM_STATUS_OK); bail: (void) kstat_close(kcp); return (dladm_errno2status(errno)); } /* Compute sum of 2 pktsums (s1 = s2 + s3) */ void dladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) { s1->rbytes = s2->rbytes + s3->rbytes; s1->ipackets = s2->ipackets + s3->ipackets; s1->ierrors = s2->ierrors + s3->ierrors; s1->obytes = s2->obytes + s3->obytes; s1->opackets = s2->opackets + s3->opackets; s1->oerrors = s2->oerrors + s3->oerrors; s1->snaptime = s2->snaptime; } #define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0) /* Compute differences between 2 pktsums (s1 = s2 - s3) */ void dladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3) { s1->rbytes = DIFF_STAT(s2->rbytes, s3->rbytes); s1->ipackets = DIFF_STAT(s2->ipackets, s3->ipackets); s1->ierrors = DIFF_STAT(s2->ierrors, s3->ierrors); s1->obytes = DIFF_STAT(s2->obytes, s3->obytes); s1->opackets = DIFF_STAT(s2->opackets, s3->opackets); s1->oerrors = DIFF_STAT(s2->oerrors, s3->oerrors); s1->snaptime = DIFF_STAT(s2->snaptime, s3->snaptime); } #define DLSTAT_MAC_RX_SWLANE "mac_rx_swlane" #define DLSTAT_MAC_RX_HWLANE "mac_rx_hwlane" #define DLSTAT_MAC_TX_SWLANE "mac_tx_swlane" #define DLSTAT_MAC_TX_HWLANE "mac_tx_hwlane" #define DLSTAT_MAC_MISC_STAT "mac_misc_stat" #define DLSTAT_MAC_RX_RING "mac_rx_ring" #define DLSTAT_MAC_TX_RING "mac_tx_ring" #define DLSTAT_MAC_FANOUT "mac_rx_swlane0_fanout" typedef struct { const char *si_name; uint_t si_offset; } stat_info_t; #define A_CNT(arr) (sizeof (arr) / sizeof (arr[0])) /* Definitions for rx lane stats */ #define RL_OFF(f) (offsetof(rx_lane_stat_t, f)) static stat_info_t rx_hwlane_stats_list[] = { {"ipackets", RL_OFF(rl_ipackets)}, {"rbytes", RL_OFF(rl_rbytes)}, {"intrs", RL_OFF(rl_intrs)}, {"intrbytes", RL_OFF(rl_intrbytes)}, {"polls", RL_OFF(rl_polls)}, {"pollbytes", RL_OFF(rl_pollbytes)}, {"rxsdrops", RL_OFF(rl_sdrops)}, {"chainunder10", RL_OFF(rl_chl10)}, {"chain10to50", RL_OFF(rl_ch10_50)}, {"chainover50", RL_OFF(rl_chg50)} }; #define RX_HWLANE_STAT_SIZE A_CNT(rx_hwlane_stats_list) static stat_info_t rx_swlane_stats_list[] = { {"ipackets", RL_OFF(rl_ipackets)}, {"rbytes", RL_OFF(rl_rbytes)}, {"local", RL_OFF(rl_lclpackets)}, {"localbytes", RL_OFF(rl_lclbytes)}, {"intrs", RL_OFF(rl_intrs)}, {"intrbytes", RL_OFF(rl_intrbytes)}, {"rxsdrops", RL_OFF(rl_sdrops)} }; #define RX_SWLANE_STAT_SIZE A_CNT(rx_swlane_stats_list) static stat_info_t rx_lane_stats_list[] = { {"ipackets", RL_OFF(rl_ipackets)}, {"rbytes", RL_OFF(rl_rbytes)}, {"local", RL_OFF(rl_lclpackets)}, {"localbytes", RL_OFF(rl_lclbytes)}, {"intrs", RL_OFF(rl_intrs)}, {"intrbytes", RL_OFF(rl_intrbytes)}, {"polls", RL_OFF(rl_polls)}, {"rxsdrops", RL_OFF(rl_sdrops)}, {"pollbytes", RL_OFF(rl_pollbytes)}, {"chainunder10", RL_OFF(rl_chl10)}, {"chain10to50", RL_OFF(rl_ch10_50)}, {"chainover50", RL_OFF(rl_chg50)} }; #define RX_LANE_STAT_SIZE A_CNT(rx_lane_stats_list) /* Definitions for tx lane stats */ #define TL_OFF(f) (offsetof(tx_lane_stat_t, f)) static stat_info_t tx_lane_stats_list[] = { {"opackets", TL_OFF(tl_opackets)}, {"obytes", TL_OFF(tl_obytes)}, {"blockcnt", TL_OFF(tl_blockcnt)}, {"unblockcnt", TL_OFF(tl_unblockcnt)}, {"txsdrops", TL_OFF(tl_sdrops)} }; #define TX_LANE_STAT_SIZE A_CNT(tx_lane_stats_list) /* Definitions for tx/rx misc stats */ #define M_OFF(f) (offsetof(misc_stat_t, f)) static stat_info_t misc_stats_list[] = { {"multircv", M_OFF(ms_multircv)}, {"brdcstrcv", M_OFF(ms_brdcstrcv)}, {"multixmt", M_OFF(ms_multixmt)}, {"brdcstxmt", M_OFF(ms_brdcstxmt)}, {"multircvbytes", M_OFF(ms_multircvbytes)}, {"brdcstrcvbytes", M_OFF(ms_brdcstrcvbytes)}, {"multixmtbytes", M_OFF(ms_multixmtbytes)}, {"brdcstxmtbytes", M_OFF(ms_brdcstxmtbytes)}, {"txerrors", M_OFF(ms_txerrors)}, {"macspoofed", M_OFF(ms_macspoofed)}, {"ipspoofed", M_OFF(ms_ipspoofed)}, {"dhcpspoofed", M_OFF(ms_dhcpspoofed)}, {"restricted", M_OFF(ms_restricted)}, {"ipackets", M_OFF(ms_ipackets)}, {"rbytes", M_OFF(ms_rbytes)}, {"local", M_OFF(ms_local)}, {"localbytes", M_OFF(ms_localbytes)}, {"intrs", M_OFF(ms_intrs)}, {"intrbytes", M_OFF(ms_intrbytes)}, {"polls", M_OFF(ms_polls)}, {"pollbytes", M_OFF(ms_pollbytes)}, {"rxsdrops", M_OFF(ms_rxsdrops)}, {"chainunder10", M_OFF(ms_chainunder10)}, {"chain10to50", M_OFF(ms_chain10to50)}, {"chainover50", M_OFF(ms_chainover50)}, {"obytes", M_OFF(ms_obytes)}, {"opackets", M_OFF(ms_opackets)}, {"blockcnt", M_OFF(ms_blockcnt)}, {"unblockcnt", M_OFF(ms_unblockcnt)}, {"txsdrops", M_OFF(ms_txsdrops)} }; #define MISC_STAT_SIZE A_CNT(misc_stats_list) /* Definitions for rx ring stats */ #define R_OFF(f) (offsetof(ring_stat_t, f)) static stat_info_t rx_ring_stats_list[] = { {"ipackets", R_OFF(r_packets)}, {"rbytes", R_OFF(r_bytes)} }; #define RX_RING_STAT_SIZE A_CNT(rx_ring_stats_list) /* Definitions for tx ring stats */ static stat_info_t tx_ring_stats_list[] = { {"opackets", R_OFF(r_packets)}, {"obytes", R_OFF(r_bytes)} }; #define TX_RING_STAT_SIZE A_CNT(tx_ring_stats_list) /* Definitions for fanout stats */ #define F_OFF(f) (offsetof(fanout_stat_t, f)) static stat_info_t fanout_stats_list[] = { {"ipackets", F_OFF(f_ipackets)}, {"rbytes", F_OFF(f_rbytes)}, }; #define FANOUT_STAT_SIZE A_CNT(fanout_stats_list) /* Definitions for total stats */ #define T_OFF(f) (offsetof(total_stat_t, f)) static stat_info_t total_stats_list[] = { {"ipackets", T_OFF(ts_ipackets)}, {"rbytes", T_OFF(ts_rbytes)}, {"opackets", T_OFF(ts_opackets)}, {"obytes", T_OFF(ts_obytes)} }; #define TOTAL_STAT_SIZE A_CNT(total_stats_list) /* Definitions for aggr stats */ #define AP_OFF(f) (offsetof(aggr_port_stat_t, f)) static stat_info_t aggr_port_stats_list[] = { {"ipackets64", AP_OFF(ap_ipackets)}, {"rbytes64", AP_OFF(ap_rbytes)}, {"opackets64", AP_OFF(ap_opackets)}, {"obytes64", AP_OFF(ap_obytes)} }; #define AGGR_PORT_STAT_SIZE A_CNT(aggr_port_stats_list) /* Definitions for flow stats */ #define FL_OFF(f) (offsetof(flow_stat_t, f)) static stat_info_t flow_stats_list[] = { {"ipackets", FL_OFF(fl_ipackets)}, {"rbytes", FL_OFF(fl_rbytes)}, {"opackets", FL_OFF(fl_opackets)}, {"obytes", FL_OFF(fl_obytes)} }; #define FLOW_STAT_SIZE A_CNT(flow_stats_list) /* Rx lane specific functions */ void * dlstat_rx_lane_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_rx_lane_match(void *, void *); static void * i_dlstat_rx_lane_stat_entry_diff(void *, void *); /* Tx lane specific functions */ void * dlstat_tx_lane_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_tx_lane_match(void *, void *); static void * i_dlstat_tx_lane_stat_entry_diff(void *, void *); /* Rx lane total specific functions */ void * dlstat_rx_lane_total_stats(dladm_handle_t, datalink_id_t); /* Tx lane total specific functions */ void * dlstat_tx_lane_total_stats(dladm_handle_t, datalink_id_t); /* Fanout specific functions */ void * dlstat_fanout_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_fanout_match(void *, void *); static void * i_dlstat_fanout_stat_entry_diff(void *, void *); /* Rx ring specific functions */ void * dlstat_rx_ring_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_rx_ring_match(void *, void *); static void * i_dlstat_rx_ring_stat_entry_diff(void *, void *); /* Tx ring specific functions */ void * dlstat_tx_ring_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_tx_ring_match(void *, void *); static void * i_dlstat_tx_ring_stat_entry_diff(void *, void *); /* Rx ring total specific functions */ void * dlstat_rx_ring_total_stats(dladm_handle_t, datalink_id_t); /* Tx ring total specific functions */ void * dlstat_tx_ring_total_stats(dladm_handle_t, datalink_id_t); /* Summary specific functions */ void * dlstat_total_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_total_match(void *, void *); static void * i_dlstat_total_stat_entry_diff(void *, void *); /* Aggr port specific functions */ void * dlstat_aggr_port_stats(dladm_handle_t, datalink_id_t); static boolean_t i_dlstat_aggr_port_match(void *, void *); static void * i_dlstat_aggr_port_stat_entry_diff(void *, void *); /* Misc stat specific functions */ void * dlstat_misc_stats(dladm_handle_t, datalink_id_t); typedef void * dladm_stat_query_t(dladm_handle_t, datalink_id_t); typedef boolean_t dladm_stat_match_t(void *, void *); typedef void * dladm_stat_diff_t(void *, void *); typedef struct dladm_stat_desc_s { dladm_stat_type_t ds_stattype; dladm_stat_query_t *ds_querystat; dladm_stat_match_t *ds_matchstat; dladm_stat_diff_t *ds_diffstat; uint_t ds_offset; stat_info_t *ds_statlist; uint_t ds_statsize; } dladm_stat_desc_t; /* * dladm_stat_table has one entry for each supported stat. ds_querystat returns * a chain of 'stat entries' for the queried stat. * Each stat entry has set of identifiers (ids) and an object containing actual * stat values. These stat entry objects are chained together in a linked list * of datatype dladm_stat_chain_t. Head of this list is returned to the caller * of dladm_link_stat_query. * * One node in the chain is shown below: * * ------------------------- * | dc_statentry | * | -------------- | * | | ids | | * | -------------- | * | | stat fields | | * | -------------- | * ------------------------- * | dc_next ---------|------> to next stat entry * ------------------------- * * In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to * object of type rx_lane_stat_entry_t. * * dladm_link_stat_query_all returns similar chain. However, instead of storing * stat fields as raw numbers, it stores those as chain of pairs. * The resulting structure is depicted below: * * ------------------------- * | dc_statentry | * | -------------- | --------------- * | | nv_header | | | name, val | * | -------------- | --------------- * | | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair * | -------------- | --------------- * ------------------------- * | dc_next ---------|------> to next stat entry * ------------------------- */ static dladm_stat_desc_t dladm_stat_table[] = { { DLADM_STAT_RX_LANE, dlstat_rx_lane_stats, i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff, offsetof(rx_lane_stat_entry_t, rle_stats), rx_lane_stats_list, RX_LANE_STAT_SIZE}, { DLADM_STAT_TX_LANE, dlstat_tx_lane_stats, i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff, offsetof(tx_lane_stat_entry_t, tle_stats), tx_lane_stats_list, TX_LANE_STAT_SIZE}, { DLADM_STAT_RX_LANE_TOTAL, dlstat_rx_lane_total_stats, i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff, offsetof(rx_lane_stat_entry_t, rle_stats), rx_lane_stats_list, RX_LANE_STAT_SIZE}, { DLADM_STAT_TX_LANE_TOTAL, dlstat_tx_lane_total_stats, i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff, offsetof(tx_lane_stat_entry_t, tle_stats), tx_lane_stats_list, TX_LANE_STAT_SIZE}, { DLADM_STAT_RX_LANE_FOUT, dlstat_fanout_stats, i_dlstat_fanout_match, i_dlstat_fanout_stat_entry_diff, offsetof(fanout_stat_entry_t, fe_stats), fanout_stats_list, FANOUT_STAT_SIZE}, { DLADM_STAT_RX_RING, dlstat_rx_ring_stats, i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff, offsetof(ring_stat_entry_t, re_stats), rx_ring_stats_list, RX_RING_STAT_SIZE}, { DLADM_STAT_TX_RING, dlstat_tx_ring_stats, i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff, offsetof(ring_stat_entry_t, re_stats), tx_ring_stats_list, TX_RING_STAT_SIZE}, { DLADM_STAT_RX_RING_TOTAL, dlstat_rx_ring_total_stats, i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff, offsetof(ring_stat_entry_t, re_stats), rx_ring_stats_list, RX_RING_STAT_SIZE}, { DLADM_STAT_TX_RING_TOTAL, dlstat_tx_ring_total_stats, i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff, offsetof(ring_stat_entry_t, re_stats), tx_ring_stats_list, TX_RING_STAT_SIZE}, { DLADM_STAT_TOTAL, dlstat_total_stats, i_dlstat_total_match, i_dlstat_total_stat_entry_diff, offsetof(total_stat_entry_t, tse_stats), total_stats_list, TOTAL_STAT_SIZE}, { DLADM_STAT_AGGR_PORT, dlstat_aggr_port_stats, i_dlstat_aggr_port_match, i_dlstat_aggr_port_stat_entry_diff, offsetof(aggr_port_stat_entry_t, ape_stats), aggr_port_stats_list, AGGR_PORT_STAT_SIZE}, /* * We don't support -i query with misc stats. Several table fields * are left uninitialized thus. */ { DLADM_STAT_MISC, dlstat_misc_stats, NULL, NULL, 0, misc_stats_list, MISC_STAT_SIZE} }; /* Internal functions */ static void * dlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype) { return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2)); } static boolean_t dlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype) { return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2)); } /* Diff between two stats */ static void i_dlstat_diff_stats(void *diff, void *op1, void *op2, stat_info_t stats_list[], uint_t size) { int i; for (i = 0; i < size; i++) { uint64_t *op1_val = (void *) ((uchar_t *)op1 + stats_list[i].si_offset); uint64_t *op2_val = (void *) ((uchar_t *)op2 + stats_list[i].si_offset); uint64_t *diff_val = (void *) ((uchar_t *)diff + stats_list[i].si_offset); *diff_val = DIFF_STAT(*op1_val, *op2_val); } } /* * Perform diff = s1 - s2, where diff, s1, s2 are structure objects of same * datatype. slist is list of offsets of the fields within the structure. */ #define DLSTAT_DIFF_STAT(s1, s2, diff, f, slist, sz) { \ if (s2 == NULL) { \ bcopy(&s1->f, &diff->f, sizeof (s1->f)); \ } else { \ i_dlstat_diff_stats(&diff->f, &s1->f, \ &s2->f, slist, sz); \ } \ } /* Sum two stats */ static void i_dlstat_sum_stats(void *sum, void *op1, void *op2, stat_info_t stats_list[], uint_t size) { int i; for (i = 0; i < size; i++) { uint64_t *op1_val = (void *) ((uchar_t *)op1 + stats_list[i].si_offset); uint64_t *op2_val = (void *) ((uchar_t *)op2 + stats_list[i].si_offset); uint64_t *sum_val = (void *) ((uchar_t *)sum + stats_list[i].si_offset); *sum_val = *op1_val + *op2_val; } } /* Look up kstat value */ static void i_dlstat_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, void *stats, stat_info_t stats_list[], uint_t size) { int i; if (kstat_read(kcp, ksp, NULL) == -1) return; for (i = 0; i < size; i++) { uint64_t *val = (void *) ((uchar_t *)stats + stats_list[i].si_offset); if (dladm_kstat_value(ksp, stats_list[i].si_name, KSTAT_DATA_UINT64, val) < 0) return; } } /* Append linked list list1 to linked list list2 and return resulting list */ static dladm_stat_chain_t * i_dlstat_join_lists(dladm_stat_chain_t *list1, dladm_stat_chain_t *list2) { dladm_stat_chain_t *curr; if (list1 == NULL) return (list2); /* list1 has at least one element, find last element in list1 */ curr = list1; while (curr->dc_next != NULL) curr = curr->dc_next; curr->dc_next = list2; return (list1); } uint_t default_idlist[] = {0}; uint_t default_idlist_size = 1; typedef enum { DLSTAT_RX_RING_IDLIST, DLSTAT_TX_RING_IDLIST, DLSTAT_RX_HWLANE_IDLIST, DLSTAT_TX_HWLANE_IDLIST, DLSTAT_FANOUT_IDLIST } dlstat_idlist_type_t; void dladm_sort_index_list(uint_t idlist[], uint_t size) { int i, j; for (j = 1; j < size; j++) { int key = idlist[j]; for (i = j - 1; (i >= 0) && (idlist[i] > key); i--) idlist[i + 1] = idlist[i]; idlist[i + 1] = key; } } /* Support for legacy drivers */ void i_query_legacy_stats(const char *linkname, pktsum_t *stats) { kstat_ctl_t *kcp; kstat_t *ksp; bzero(stats, sizeof (*stats)); if ((kcp = kstat_open()) == NULL) return; ksp = dladm_kstat_lookup(kcp, "link", 0, linkname, NULL); if (ksp != NULL) dladm_get_stats(kcp, ksp, stats); (void) kstat_close(kcp); } void * i_dlstat_legacy_rx_lane_stats(const char *linkname) { dladm_stat_chain_t *head = NULL; pktsum_t stats; rx_lane_stat_entry_t *rx_lane_stat_entry; bzero(&stats, sizeof (pktsum_t)); /* Query for dls stats */ i_query_legacy_stats(linkname, &stats); /* Convert to desired data type */ rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY; rx_lane_stat_entry->rle_id = L_SWLANE; rx_lane_stat_entry->rle_stats.rl_ipackets = stats.ipackets; rx_lane_stat_entry->rle_stats.rl_intrs = stats.ipackets; rx_lane_stat_entry->rle_stats.rl_rbytes = stats.rbytes; /* Allocate memory for wrapper */ head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(rx_lane_stat_entry); goto done; } head->dc_statentry = rx_lane_stat_entry; head->dc_next = NULL; done: return (head); } void * i_dlstat_legacy_tx_lane_stats(const char *linkname) { dladm_stat_chain_t *head = NULL; pktsum_t stats; tx_lane_stat_entry_t *tx_lane_stat_entry; bzero(&stats, sizeof (pktsum_t)); /* Query for dls stats */ i_query_legacy_stats(linkname, &stats); /* Convert to desired data type */ tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t)); if (tx_lane_stat_entry == NULL) goto done; tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY; tx_lane_stat_entry->tle_id = L_SWLANE; tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets; tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes; /* Allocate memory for wrapper */ head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(tx_lane_stat_entry); goto done; } head->dc_statentry = tx_lane_stat_entry; head->dc_next = NULL; done: return (head); } /* * Ideally, we would want an ioctl to return list of ring-ids (or lane-ids) * for a given data-link (or mac client). We could then query for specific * kstats based on these ring-ids (lane-ids). * Ring-ids (or lane-ids) could be returned like any other link properties * queried by dladm show-linkprop. However, non-global zones do not have * access to this information today. * We thus opt for an implementation that relies heavily on kstat internals: * i_dlstat_*search routines and i_dlstat_get_idlist. */ /* rx hwlane specific */ static boolean_t i_dlstat_rx_hwlane_search(kstat_t *ksp) { return (ksp->ks_instance == 0 && strstr(ksp->ks_name, "mac_rx") != 0 && strstr(ksp->ks_name, "hwlane") != 0 && strstr(ksp->ks_name, "fanout") == 0 && strcmp(ksp->ks_class, "net") == 0); } /* tx hwlane specific */ static boolean_t i_dlstat_tx_hwlane_search(kstat_t *ksp) { return (ksp->ks_instance == 0 && strstr(ksp->ks_name, "mac_tx") != 0 && strstr(ksp->ks_name, "hwlane") != 0 && strcmp(ksp->ks_class, "net") == 0); } /* rx fanout specific */ static boolean_t i_dlstat_fanout_search(kstat_t *ksp) { return (ksp->ks_instance == 0 && strstr(ksp->ks_name, "mac_rx") != 0 && strstr(ksp->ks_name, "swlane") != 0 && strstr(ksp->ks_name, "fanout") != 0 && strcmp(ksp->ks_class, "net") == 0); } /* rx ring specific */ static boolean_t i_dlstat_rx_ring_search(kstat_t *ksp) { return (ksp->ks_instance == 0 && strstr(ksp->ks_name, "mac_rx") != 0 && strstr(ksp->ks_name, "ring") != 0 && strcmp(ksp->ks_class, "net") == 0); } /* tx ring specific */ static boolean_t i_dlstat_tx_ring_search(kstat_t *ksp) { return (ksp->ks_instance == 0) && strstr(ksp->ks_name, "mac_tx") != 0 && strstr(ksp->ks_name, "ring") != 0 && strcmp(ksp->ks_class, "net") == 0; } typedef boolean_t dladm_search_kstat_t(kstat_t *); typedef struct dladm_extract_idlist_s { dlstat_idlist_type_t di_type; char *di_prefix; dladm_search_kstat_t *di_searchkstat; } dladm_extract_idlist_t; static dladm_extract_idlist_t dladm_extract_idlist[] = { { DLSTAT_RX_RING_IDLIST, DLSTAT_MAC_RX_RING, i_dlstat_rx_ring_search}, { DLSTAT_TX_RING_IDLIST, DLSTAT_MAC_TX_RING, i_dlstat_tx_ring_search}, { DLSTAT_RX_HWLANE_IDLIST, DLSTAT_MAC_RX_HWLANE, i_dlstat_rx_hwlane_search}, { DLSTAT_TX_HWLANE_IDLIST, DLSTAT_MAC_TX_HWLANE, i_dlstat_tx_hwlane_search}, { DLSTAT_FANOUT_IDLIST, DLSTAT_MAC_FANOUT, i_dlstat_fanout_search} }; static void i_dlstat_get_idlist(const char *modname, dlstat_idlist_type_t idlist_type, uint_t idlist[], uint_t *size) { kstat_ctl_t *kcp; kstat_t *ksp; char *prefix; int prefixlen; boolean_t (*fptr_searchkstat)(kstat_t *); *size = 0; if ((kcp = kstat_open()) == NULL) { warn("kstat_open operation failed"); goto done; } prefix = dladm_extract_idlist[idlist_type].di_prefix; fptr_searchkstat = dladm_extract_idlist[idlist_type].di_searchkstat; prefixlen = strlen(prefix); for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) { if ((strcmp(ksp->ks_module, modname) == 0) && fptr_searchkstat(ksp)) { idlist[(*size)++] = atoi(&ksp->ks_name[prefixlen]); } } dladm_sort_index_list(idlist, *size); done: (void) kstat_close(kcp); } 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)) { kstat_ctl_t *kcp; kstat_t *ksp; char statname[MAXLINKNAMELEN]; int i = 0; dladm_stat_chain_t *head = NULL, *prev = NULL; dladm_stat_chain_t *curr; if ((kcp = kstat_open()) == NULL) { warn("kstat_open operation failed"); return (NULL); } for (i = 0; i < idlist_size; i++) { uint_t index = idlist[i]; (void) snprintf(statname, sizeof (statname), "%s%d", prefix, index); ksp = dladm_kstat_lookup(kcp, modname, 0, statname, NULL); if (ksp == NULL) continue; curr = malloc(sizeof (dladm_stat_chain_t)); if (curr == NULL) break; curr->dc_statentry = fn(kcp, ksp, index); if (curr->dc_statentry == NULL) { free(curr); break; } (void) strlcpy(curr->dc_statheader, statname, sizeof (curr->dc_statheader)); curr->dc_next = NULL; if (head == NULL) /* First node */ head = curr; else prev->dc_next = curr; prev = curr; } done: (void) kstat_close(kcp); return (head); } static misc_stat_entry_t * i_dlstat_misc_stats(const char *linkname) { kstat_ctl_t *kcp; kstat_t *ksp; misc_stat_entry_t *misc_stat_entry = NULL; if ((kcp = kstat_open()) == NULL) return (NULL); ksp = dladm_kstat_lookup(kcp, linkname, 0, DLSTAT_MAC_MISC_STAT, NULL); if (ksp == NULL) goto done; misc_stat_entry = calloc(1, sizeof (misc_stat_entry_t)); if (misc_stat_entry == NULL) goto done; i_dlstat_get_stats(kcp, ksp, &misc_stat_entry->mse_stats, misc_stats_list, MISC_STAT_SIZE); done: (void) kstat_close(kcp); return (misc_stat_entry); } /* Rx lane statistic specific functions */ static boolean_t i_dlstat_rx_lane_match(void *arg1, void *arg2) { rx_lane_stat_entry_t *s1 = arg1; rx_lane_stat_entry_t *s2 = arg2; return (s1->rle_index == s2->rle_index && s1->rle_id == s2->rle_id); } static void * i_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2) { rx_lane_stat_entry_t *s1 = arg1; rx_lane_stat_entry_t *s2 = arg2; rx_lane_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (rx_lane_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->rle_index = s1->rle_index; diff_entry->rle_id = s1->rle_id; DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list, RX_LANE_STAT_SIZE); done: return (diff_entry); } static void * i_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { rx_lane_stat_entry_t *rx_lane_stat_entry; rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; rx_lane_stat_entry->rle_index = i; rx_lane_stat_entry->rle_id = L_HWLANE; i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats, rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE); done: return (rx_lane_stat_entry); } /*ARGSUSED*/ static void * i_dlstat_rx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { rx_lane_stat_entry_t *rx_lane_stat_entry; rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY; rx_lane_stat_entry->rle_id = L_SWLANE; i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats, rx_swlane_stats_list, RX_SWLANE_STAT_SIZE); rx_lane_stat_entry->rle_stats.rl_ipackets = rx_lane_stat_entry->rle_stats.rl_intrs; rx_lane_stat_entry->rle_stats.rl_rbytes = rx_lane_stat_entry->rle_stats.rl_intrbytes; done: return (rx_lane_stat_entry); } /*ARGSUSED*/ static void * i_dlstat_rx_local_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { rx_lane_stat_entry_t *local_stat_entry; rx_lane_stat_entry_t *rx_lane_stat_entry; rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; local_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (local_stat_entry == NULL) goto done; local_stat_entry->rle_index = DLSTAT_INVALID_ENTRY; local_stat_entry->rle_id = L_LOCAL; i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats, rx_swlane_stats_list, RX_SWLANE_STAT_SIZE); local_stat_entry->rle_stats.rl_ipackets = rx_lane_stat_entry->rle_stats.rl_lclpackets; local_stat_entry->rle_stats.rl_rbytes = rx_lane_stat_entry->rle_stats.rl_lclbytes; done: free(rx_lane_stat_entry); return (local_stat_entry); } static dladm_stat_chain_t * i_dlstat_rx_local_stats(const char *linkname) { dladm_stat_chain_t *local_stats = NULL; local_stats = i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE, default_idlist, default_idlist_size, i_dlstat_rx_local_retrieve_stat); if (local_stats != NULL) { (void) strlcpy(local_stats->dc_statheader, "mac_rx_local", sizeof (local_stats->dc_statheader)); } return (local_stats); } static dladm_stat_chain_t * i_dlstat_rx_bcast_stats(const char *linkname) { misc_stat_entry_t *misc_stat_entry; dladm_stat_chain_t *head = NULL; rx_lane_stat_entry_t *rx_lane_stat_entry; misc_stat_entry = i_dlstat_misc_stats(linkname); if (misc_stat_entry == NULL) goto done; rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY; rx_lane_stat_entry->rle_id = L_BCAST; rx_lane_stat_entry->rle_stats.rl_ipackets = misc_stat_entry->mse_stats.ms_brdcstrcv + misc_stat_entry->mse_stats.ms_multircv; rx_lane_stat_entry->rle_stats.rl_intrs = misc_stat_entry->mse_stats.ms_brdcstrcv + misc_stat_entry->mse_stats.ms_multircv; rx_lane_stat_entry->rle_stats.rl_rbytes = misc_stat_entry->mse_stats.ms_brdcstrcvbytes + misc_stat_entry->mse_stats.ms_multircvbytes; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(rx_lane_stat_entry); goto done; } head->dc_statentry = rx_lane_stat_entry; head->dc_next = NULL; free(misc_stat_entry); done: return (head); } static dladm_stat_chain_t * i_dlstat_rx_defunctlane_stats(const char *linkname) { misc_stat_entry_t *misc_stat_entry; dladm_stat_chain_t *head = NULL; rx_lane_stat_entry_t *rx_lane_stat_entry; misc_stat_entry = i_dlstat_misc_stats(linkname); if (misc_stat_entry == NULL) goto done; rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t)); if (rx_lane_stat_entry == NULL) goto done; rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY; rx_lane_stat_entry->rle_id = L_DFNCT; rx_lane_stat_entry->rle_stats.rl_ipackets = misc_stat_entry->mse_stats.ms_ipackets; rx_lane_stat_entry->rle_stats.rl_rbytes = misc_stat_entry->mse_stats.ms_rbytes; rx_lane_stat_entry->rle_stats.rl_intrs = misc_stat_entry->mse_stats.ms_intrs; rx_lane_stat_entry->rle_stats.rl_polls = misc_stat_entry->mse_stats.ms_polls; rx_lane_stat_entry->rle_stats.rl_sdrops = misc_stat_entry->mse_stats.ms_rxsdrops; rx_lane_stat_entry->rle_stats.rl_chl10 = misc_stat_entry->mse_stats.ms_chainunder10; rx_lane_stat_entry->rle_stats.rl_ch10_50 = misc_stat_entry->mse_stats.ms_chain10to50; rx_lane_stat_entry->rle_stats.rl_chg50 = misc_stat_entry->mse_stats.ms_chainover50; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(rx_lane_stat_entry); goto done; } head->dc_statentry = rx_lane_stat_entry; head->dc_next = NULL; done: return (head); } static dladm_stat_chain_t * i_dlstat_rx_hwlane_stats(const char *linkname) { uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP]; uint_t rx_hwlane_idlist_size; i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST, rx_hwlane_idlist, &rx_hwlane_idlist_size); return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_HWLANE, rx_hwlane_idlist, rx_hwlane_idlist_size, i_dlstat_rx_hwlane_retrieve_stat)); } /*ARGSUSED*/ static dladm_stat_chain_t * i_dlstat_rx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid, const char *linkname) { return (i_dlstat_query_stats(linkname, DLSTAT_MAC_RX_SWLANE, default_idlist, default_idlist_size, i_dlstat_rx_swlane_retrieve_stat)); } void * dlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *head = NULL; dladm_stat_chain_t *local_stats = NULL; dladm_stat_chain_t *bcast_stats = NULL; dladm_stat_chain_t *defunctlane_stats = NULL; dladm_stat_chain_t *lane_stats = NULL; char linkname[MAXLINKNAMELEN]; boolean_t is_legacy_driver; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } /* Check if it is legacy driver */ if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT, "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) { goto done; } if (is_legacy_driver) { head = i_dlstat_legacy_rx_lane_stats(linkname); goto done; } local_stats = i_dlstat_rx_local_stats(linkname); bcast_stats = i_dlstat_rx_bcast_stats(linkname); defunctlane_stats = i_dlstat_rx_defunctlane_stats(linkname); lane_stats = i_dlstat_rx_hwlane_stats(linkname); if (lane_stats == NULL) lane_stats = i_dlstat_rx_swlane_stats(dh, linkid, linkname); head = i_dlstat_join_lists(local_stats, bcast_stats); head = i_dlstat_join_lists(head, defunctlane_stats); head = i_dlstat_join_lists(head, lane_stats); done: return (head); } /* Tx lane statistic specific functions */ static boolean_t i_dlstat_tx_lane_match(void *arg1, void *arg2) { tx_lane_stat_entry_t *s1 = arg1; tx_lane_stat_entry_t *s2 = arg2; return (s1->tle_index == s2->tle_index && s1->tle_id == s2->tle_id); } static void * i_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2) { tx_lane_stat_entry_t *s1 = arg1; tx_lane_stat_entry_t *s2 = arg2; tx_lane_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (tx_lane_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->tle_index = s1->tle_index; diff_entry->tle_id = s1->tle_id; DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list, TX_LANE_STAT_SIZE); done: return (diff_entry); } static void * i_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { tx_lane_stat_entry_t *tx_lane_stat_entry; tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t)); if (tx_lane_stat_entry == NULL) goto done; tx_lane_stat_entry->tle_index = i; tx_lane_stat_entry->tle_id = L_HWLANE; i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats, tx_lane_stats_list, TX_LANE_STAT_SIZE); done: return (tx_lane_stat_entry); } /*ARGSUSED*/ static void * i_dlstat_tx_swlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { tx_lane_stat_entry_t *tx_lane_stat_entry; tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t)); if (tx_lane_stat_entry == NULL) goto done; tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY; tx_lane_stat_entry->tle_id = L_SWLANE; i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats, tx_lane_stats_list, TX_LANE_STAT_SIZE); done: return (tx_lane_stat_entry); } static dladm_stat_chain_t * i_dlstat_tx_bcast_stats(const char *linkname) { misc_stat_entry_t *misc_stat_entry; dladm_stat_chain_t *head = NULL; tx_lane_stat_entry_t *tx_lane_stat_entry; misc_stat_entry = i_dlstat_misc_stats(linkname); if (misc_stat_entry == NULL) goto done; tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t)); if (tx_lane_stat_entry == NULL) goto done; tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY; tx_lane_stat_entry->tle_id = L_BCAST; tx_lane_stat_entry->tle_stats.tl_opackets = misc_stat_entry->mse_stats.ms_brdcstxmt + misc_stat_entry->mse_stats.ms_multixmt; tx_lane_stat_entry->tle_stats.tl_obytes = misc_stat_entry->mse_stats.ms_brdcstxmtbytes + misc_stat_entry->mse_stats.ms_multixmtbytes; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(tx_lane_stat_entry); goto done; } head->dc_statentry = tx_lane_stat_entry; head->dc_next = NULL; free(misc_stat_entry); done: return (head); } static dladm_stat_chain_t * i_dlstat_tx_defunctlane_stats(const char *linkname) { misc_stat_entry_t *misc_stat_entry; dladm_stat_chain_t *head = NULL; tx_lane_stat_entry_t *tx_lane_stat_entry; misc_stat_entry = i_dlstat_misc_stats(linkname); if (misc_stat_entry == NULL) goto done; tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t)); if (tx_lane_stat_entry == NULL) goto done; tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY; tx_lane_stat_entry->tle_id = L_DFNCT; tx_lane_stat_entry->tle_stats.tl_opackets = misc_stat_entry->mse_stats.ms_opackets; tx_lane_stat_entry->tle_stats.tl_obytes = misc_stat_entry->mse_stats.ms_obytes; tx_lane_stat_entry->tle_stats.tl_sdrops = misc_stat_entry->mse_stats.ms_txsdrops; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(tx_lane_stat_entry); goto done; } head->dc_statentry = tx_lane_stat_entry; head->dc_next = NULL; done: return (head); } static dladm_stat_chain_t * i_dlstat_tx_hwlane_stats(const char *linkname) { uint_t tx_hwlane_idlist[MAX_RINGS_PER_GROUP]; uint_t tx_hwlane_idlist_size; i_dlstat_get_idlist(linkname, DLSTAT_TX_HWLANE_IDLIST, tx_hwlane_idlist, &tx_hwlane_idlist_size); return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_HWLANE, tx_hwlane_idlist, tx_hwlane_idlist_size, i_dlstat_tx_hwlane_retrieve_stat)); } /*ARGSUSED*/ static dladm_stat_chain_t * i_dlstat_tx_swlane_stats(dladm_handle_t dh, datalink_id_t linkid, const char *linkname) { return (i_dlstat_query_stats(linkname, DLSTAT_MAC_TX_SWLANE, default_idlist, default_idlist_size, i_dlstat_tx_swlane_retrieve_stat)); } void * dlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *head = NULL; dladm_stat_chain_t *bcast_stats = NULL; dladm_stat_chain_t *defunctlane_stats = NULL; dladm_stat_chain_t *lane_stats; char linkname[MAXLINKNAMELEN]; boolean_t is_legacy_driver; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } /* Check if it is legacy driver */ if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT, "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) { goto done; } if (is_legacy_driver) { head = i_dlstat_legacy_tx_lane_stats(linkname); goto done; } bcast_stats = i_dlstat_tx_bcast_stats(linkname); defunctlane_stats = i_dlstat_tx_defunctlane_stats(linkname); lane_stats = i_dlstat_tx_hwlane_stats(linkname); if (lane_stats == NULL) lane_stats = i_dlstat_tx_swlane_stats(dh, linkid, linkname); head = i_dlstat_join_lists(bcast_stats, defunctlane_stats); head = i_dlstat_join_lists(head, lane_stats); done: return (head); } /* Rx lane total statistic specific functions */ void * dlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *total_head = NULL; dladm_stat_chain_t *rx_lane_head, *curr; rx_lane_stat_entry_t *total_stats; /* Get per rx lane stats */ rx_lane_head = dlstat_rx_lane_stats(dh, linkid); if (rx_lane_head == NULL) goto done; total_stats = calloc(1, sizeof (rx_lane_stat_entry_t)); if (total_stats == NULL) goto done; total_stats->rle_index = DLSTAT_INVALID_ENTRY; total_stats->rle_id = DLSTAT_INVALID_ENTRY; for (curr = rx_lane_head; curr != NULL; curr = curr->dc_next) { rx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry; i_dlstat_sum_stats(&total_stats->rle_stats, &curr_lane_stats->rle_stats, &total_stats->rle_stats, rx_lane_stats_list, RX_LANE_STAT_SIZE); } total_head = malloc(sizeof (dladm_stat_chain_t)); if (total_head == NULL) { free(total_stats); goto done; } total_head->dc_statentry = total_stats; (void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total", sizeof (total_head->dc_statheader)); total_head->dc_next = NULL; free(rx_lane_head); done: return (total_head); } /* Tx lane total statistic specific functions */ void * dlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *total_head = NULL; dladm_stat_chain_t *tx_lane_head, *curr; tx_lane_stat_entry_t *total_stats; /* Get per tx lane stats */ tx_lane_head = dlstat_tx_lane_stats(dh, linkid); if (tx_lane_head == NULL) goto done; total_stats = calloc(1, sizeof (tx_lane_stat_entry_t)); if (total_stats == NULL) goto done; total_stats->tle_index = DLSTAT_INVALID_ENTRY; total_stats->tle_id = DLSTAT_INVALID_ENTRY; for (curr = tx_lane_head; curr != NULL; curr = curr->dc_next) { tx_lane_stat_entry_t *curr_lane_stats = curr->dc_statentry; i_dlstat_sum_stats(&total_stats->tle_stats, &curr_lane_stats->tle_stats, &total_stats->tle_stats, tx_lane_stats_list, TX_LANE_STAT_SIZE); } total_head = malloc(sizeof (dladm_stat_chain_t)); if (total_head == NULL) { free(total_stats); goto done; } total_head->dc_statentry = total_stats; (void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total", sizeof (total_head->dc_statheader)); total_head->dc_next = NULL; free(tx_lane_head); done: return (total_head); } /* Fanout specific functions */ static boolean_t i_dlstat_fanout_match(void *arg1, void *arg2) { fanout_stat_entry_t *s1 = arg1; fanout_stat_entry_t *s2 = arg2; return (s1->fe_index == s2->fe_index && s1->fe_id == s2->fe_id && s1->fe_foutindex == s2->fe_foutindex); } static void * i_dlstat_fanout_stat_entry_diff(void *arg1, void *arg2) { fanout_stat_entry_t *s1 = arg1; fanout_stat_entry_t *s2 = arg2; fanout_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (fanout_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->fe_index = s1->fe_index; diff_entry->fe_id = s1->fe_id; diff_entry->fe_foutindex = s1->fe_foutindex; DLSTAT_DIFF_STAT(s1, s2, diff_entry, fe_stats, fanout_stats_list, FANOUT_STAT_SIZE); done: return (diff_entry); } static void * i_dlstat_fanout_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { fanout_stat_entry_t *fanout_stat_entry; fanout_stat_entry = calloc(1, sizeof (fanout_stat_entry_t)); if (fanout_stat_entry == NULL) goto done; /* Set by the caller later */ fanout_stat_entry->fe_index = DLSTAT_INVALID_ENTRY; fanout_stat_entry->fe_id = DLSTAT_INVALID_ENTRY; fanout_stat_entry->fe_foutindex = i; i_dlstat_get_stats(kcp, ksp, &fanout_stat_entry->fe_stats, fanout_stats_list, FANOUT_STAT_SIZE); done: return (fanout_stat_entry); } 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) { int i; char statprefix[MAXLINKNAMELEN]; char linkname[MAXLINKNAMELEN]; dladm_stat_chain_t *curr, *curr_head; dladm_stat_chain_t *head = NULL, *prev = NULL; uint_t fanout_idlist[MAX_RINGS_PER_GROUP]; uint_t fanout_idlist_size; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { return (NULL); } i_dlstat_get_idlist(linkname, DLSTAT_FANOUT_IDLIST, fanout_idlist, &fanout_idlist_size); for (i = 0; i < idlist_size; i++) { uint_t index = idlist[i]; (void) snprintf(statprefix, sizeof (statprefix), "%s%d_fanout", prefix, index); curr_head = i_dlstat_query_stats(modname, statprefix, fanout_idlist, fanout_idlist_size, i_dlstat_fanout_retrieve_stat); if (curr_head == NULL) /* Last lane */ break; if (head == NULL) /* First lane */ head = curr_head; else /* Link new lane list to end of previous lane list */ prev->dc_next = curr_head; /* Walk new lane list and set ids */ for (curr = curr_head; curr != NULL; curr = curr->dc_next) { fanout_stat_entry_t *curr_stats = curr->dc_statentry; curr_stats->fe_index = index; curr_stats->fe_id = L_HWLANE; /* * Save last pointer of previous linked list. * This pointer is used to chain linked lists * generated in each iteration. */ prev = curr; } } return (head); } void * dlstat_fanout_swlane_and_local_stats(dladm_handle_t dh, datalink_id_t linkid, const char *linkname) { return (i_dlstat_query_fanout_stats(dh, linkid, default_idlist, default_idlist_size, linkname, DLSTAT_MAC_RX_SWLANE)); } void * dlstat_fanout_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid, const char *linkname) { uint_t rx_hwlane_idlist[MAX_RINGS_PER_GROUP]; uint_t rx_hwlane_idlist_size; i_dlstat_get_idlist(linkname, DLSTAT_RX_HWLANE_IDLIST, rx_hwlane_idlist, &rx_hwlane_idlist_size); return (i_dlstat_query_fanout_stats(dh, linkid, rx_hwlane_idlist, rx_hwlane_idlist_size, linkname, DLSTAT_MAC_RX_HWLANE)); } void * dlstat_fanout_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *head = NULL; dladm_stat_chain_t *fout_hwlane_stats; dladm_stat_chain_t *fout_swlane_and_local_stats; fanout_stat_entry_t *fout_stats; char linkname[MAXLINKNAMELEN]; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } fout_swlane_and_local_stats = dlstat_fanout_swlane_and_local_stats(dh, linkid, linkname); fout_hwlane_stats = dlstat_fanout_hwlane_stats(dh, linkid, linkname); if (fout_swlane_and_local_stats == NULL) { head = fout_hwlane_stats; goto done; } fout_stats = fout_swlane_and_local_stats->dc_statentry; if (fout_hwlane_stats != NULL) { /* hwlane(s), only local traffic */ fout_stats->fe_id = L_LOCAL; fout_stats->fe_index = DLSTAT_INVALID_ENTRY; } else { /* no hwlane, mix of local+sw classified */ fout_stats->fe_id = L_LCLSWLANE; fout_stats->fe_index = DLSTAT_INVALID_ENTRY; } fout_swlane_and_local_stats->dc_next = fout_hwlane_stats; head = fout_swlane_and_local_stats; done: return (head); } /* Rx ring statistic specific functions */ static boolean_t i_dlstat_rx_ring_match(void *arg1, void *arg2) { rx_lane_stat_entry_t *s1 = arg1; rx_lane_stat_entry_t *s2 = arg2; return (s1->rle_index == s2->rle_index); } static void * i_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2) { ring_stat_entry_t *s1 = arg1; ring_stat_entry_t *s2 = arg2; ring_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (ring_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->re_index = s1->re_index; DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list, RX_RING_STAT_SIZE); done: return (diff_entry); } static void * i_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { ring_stat_entry_t *rx_ring_stat_entry; rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t)); if (rx_ring_stat_entry == NULL) goto done; rx_ring_stat_entry->re_index = i; i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats, rx_ring_stats_list, RX_RING_STAT_SIZE); done: return (rx_ring_stat_entry); } void * dlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid) { uint_t rx_ring_idlist[MAX_RINGS_PER_GROUP]; uint_t rx_ring_idlist_size; dladm_phys_attr_t dpa; char linkname[MAXLINKNAMELEN]; char *modname; datalink_class_t class; /* * kstats corresponding to physical device rings continue to use * device names even if the link is renamed using dladm rename-link. * Thus, given a linkid, we lookup the physical device name. * However, if an aggr is renamed, kstats corresponding to its * pseudo rings are renamed as well. */ if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { return (NULL); } if (class != DATALINK_CLASS_AGGR) { if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) { return (NULL); } modname = dpa.dp_dev; } else modname = linkname; i_dlstat_get_idlist(modname, DLSTAT_RX_RING_IDLIST, rx_ring_idlist, &rx_ring_idlist_size); return (i_dlstat_query_stats(modname, DLSTAT_MAC_RX_RING, rx_ring_idlist, rx_ring_idlist_size, i_dlstat_rx_ring_retrieve_stat)); } /* Tx ring statistic specific functions */ static boolean_t i_dlstat_tx_ring_match(void *arg1, void *arg2) { tx_lane_stat_entry_t *s1 = arg1; tx_lane_stat_entry_t *s2 = arg2; return (s1->tle_index == s2->tle_index); } static void * i_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2) { ring_stat_entry_t *s1 = arg1; ring_stat_entry_t *s2 = arg2; ring_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (ring_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->re_index = s1->re_index; DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list, TX_RING_STAT_SIZE); done: return (diff_entry); } static void * i_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i) { ring_stat_entry_t *tx_ring_stat_entry; tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t)); if (tx_ring_stat_entry == NULL) goto done; tx_ring_stat_entry->re_index = i; i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats, tx_ring_stats_list, TX_RING_STAT_SIZE); done: return (tx_ring_stat_entry); } void * dlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid) { uint_t tx_ring_idlist[MAX_RINGS_PER_GROUP]; uint_t tx_ring_idlist_size; dladm_phys_attr_t dpa; char linkname[MAXLINKNAMELEN]; char *modname; datalink_class_t class; /* * kstats corresponding to physical device rings continue to use * device names even if the link is renamed using dladm rename-link. * Thus, given a linkid, we lookup the physical device name. * However, if an aggr is renamed, kstats corresponding to its * pseudo rings are renamed as well. */ if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { return (NULL); } if (class != DATALINK_CLASS_AGGR) { if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) { return (NULL); } modname = dpa.dp_dev; } else modname = linkname; i_dlstat_get_idlist(modname, DLSTAT_TX_RING_IDLIST, tx_ring_idlist, &tx_ring_idlist_size); return (i_dlstat_query_stats(modname, DLSTAT_MAC_TX_RING, tx_ring_idlist, tx_ring_idlist_size, i_dlstat_tx_ring_retrieve_stat)); } /* Rx ring total statistic specific functions */ void * dlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *total_head = NULL; dladm_stat_chain_t *rx_ring_head, *curr; ring_stat_entry_t *total_stats; /* Get per rx ring stats */ rx_ring_head = dlstat_rx_ring_stats(dh, linkid); if (rx_ring_head == NULL) goto done; total_stats = calloc(1, sizeof (ring_stat_entry_t)); if (total_stats == NULL) goto done; total_stats->re_index = DLSTAT_INVALID_ENTRY; for (curr = rx_ring_head; curr != NULL; curr = curr->dc_next) { ring_stat_entry_t *curr_ring_stats = curr->dc_statentry; i_dlstat_sum_stats(&total_stats->re_stats, &curr_ring_stats->re_stats, &total_stats->re_stats, rx_ring_stats_list, RX_RING_STAT_SIZE); } total_head = malloc(sizeof (dladm_stat_chain_t)); if (total_head == NULL) { free(total_stats); goto done; } total_head->dc_statentry = total_stats; (void) strlcpy(total_head->dc_statheader, "mac_rx_ring_total", sizeof (total_head->dc_statheader)); total_head->dc_next = NULL; free(rx_ring_head); done: return (total_head); } /* Tx ring total statistic specific functions */ void * dlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *total_head = NULL; dladm_stat_chain_t *tx_ring_head, *curr; ring_stat_entry_t *total_stats; /* Get per tx ring stats */ tx_ring_head = dlstat_tx_ring_stats(dh, linkid); if (tx_ring_head == NULL) goto done; total_stats = calloc(1, sizeof (ring_stat_entry_t)); if (total_stats == NULL) goto done; total_stats->re_index = DLSTAT_INVALID_ENTRY; for (curr = tx_ring_head; curr != NULL; curr = curr->dc_next) { ring_stat_entry_t *curr_ring_stats = curr->dc_statentry; i_dlstat_sum_stats(&total_stats->re_stats, &curr_ring_stats->re_stats, &total_stats->re_stats, tx_ring_stats_list, TX_RING_STAT_SIZE); } total_head = malloc(sizeof (dladm_stat_chain_t)); if (total_head == NULL) { free(total_stats); goto done; } total_head->dc_statentry = total_stats; (void) strlcpy(total_head->dc_statheader, "mac_tx_ring_total", sizeof (total_head->dc_statheader)); total_head->dc_next = NULL; free(tx_ring_head); done: return (total_head); } /* Summary statistic specific functions */ /*ARGSUSED*/ static boolean_t i_dlstat_total_match(void *arg1, void *arg2) { /* Always single entry for total */ return (B_TRUE); } static void * i_dlstat_total_stat_entry_diff(void *arg1, void *arg2) { total_stat_entry_t *s1 = arg1; total_stat_entry_t *s2 = arg2; total_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (total_stat_entry_t)); if (diff_entry == NULL) goto done; DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list, TOTAL_STAT_SIZE); done: return (diff_entry); } void * dlstat_total_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_stat_chain_t *head = NULL; dladm_stat_chain_t *rx_total; dladm_stat_chain_t *tx_total; total_stat_entry_t *total_stat_entry; rx_lane_stat_entry_t *rx_lane_stat_entry; tx_lane_stat_entry_t *tx_lane_stat_entry; /* Get total rx lane stats */ rx_total = dlstat_rx_lane_total_stats(dh, linkid); if (rx_total == NULL) goto done; /* Get total tx lane stats */ tx_total = dlstat_tx_lane_total_stats(dh, linkid); if (tx_total == NULL) goto done; /* Build total stat */ total_stat_entry = calloc(1, sizeof (total_stat_entry_t)); if (total_stat_entry == NULL) goto done; rx_lane_stat_entry = rx_total->dc_statentry; tx_lane_stat_entry = tx_total->dc_statentry; /* Extract total rx ipackets, rbytes */ total_stat_entry->tse_stats.ts_ipackets = rx_lane_stat_entry->rle_stats.rl_ipackets; total_stat_entry->tse_stats.ts_rbytes = rx_lane_stat_entry->rle_stats.rl_rbytes; /* Extract total tx opackets, obytes */ total_stat_entry->tse_stats.ts_opackets = tx_lane_stat_entry->tle_stats.tl_opackets; total_stat_entry->tse_stats.ts_obytes = tx_lane_stat_entry->tle_stats.tl_obytes; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(total_stat_entry); goto done; } head->dc_statentry = total_stat_entry; (void) strlcpy(head->dc_statheader, "mac_lane_total", sizeof (head->dc_statheader)); head->dc_next = NULL; free(rx_total); free(tx_total); done: return (head); } /* Aggr total statistic(summed across all component ports) specific functions */ void * dlstat_aggr_total_stats(dladm_stat_chain_t *head) { dladm_stat_chain_t *curr; dladm_stat_chain_t *total_head; aggr_port_stat_entry_t *total_stats; total_stats = calloc(1, sizeof (aggr_port_stat_entry_t)); if (total_stats == NULL) goto done; total_stats->ape_portlinkid = DATALINK_INVALID_LINKID; for (curr = head; curr != NULL; curr = curr->dc_next) { aggr_port_stat_entry_t *curr_aggr_port_stats; curr_aggr_port_stats = curr->dc_statentry; i_dlstat_sum_stats(&total_stats->ape_stats, &curr_aggr_port_stats->ape_stats, &total_stats->ape_stats, aggr_port_stats_list, AGGR_PORT_STAT_SIZE); } total_head = malloc(sizeof (dladm_stat_chain_t)); if (total_head == NULL) { free(total_stats); goto done; } total_head->dc_statentry = total_stats; total_head->dc_next = NULL; done: return (total_head); } /* Aggr port statistic specific functions */ static boolean_t i_dlstat_aggr_port_match(void *arg1, void *arg2) { aggr_port_stat_entry_t *s1 = arg1; aggr_port_stat_entry_t *s2 = arg2; return (s1->ape_portlinkid == s2->ape_portlinkid); } static void * i_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2) { aggr_port_stat_entry_t *s1 = arg1; aggr_port_stat_entry_t *s2 = arg2; aggr_port_stat_entry_t *diff_entry; diff_entry = malloc(sizeof (aggr_port_stat_entry_t)); if (diff_entry == NULL) goto done; diff_entry->ape_portlinkid = s1->ape_portlinkid; DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list, AGGR_PORT_STAT_SIZE); done: return (diff_entry); } /* * Query dls stats for the aggr port. This results in query for stats into * the corresponding device driver. */ static aggr_port_stat_entry_t * i_dlstat_single_port_stats(const char *portname, datalink_id_t linkid) { kstat_ctl_t *kcp; kstat_t *ksp; char module[DLPI_LINKNAME_MAX]; uint_t instance; aggr_port_stat_entry_t *aggr_port_stat_entry = NULL; if (dladm_parselink(portname, module, &instance) != DLADM_STATUS_OK) goto done; if ((kcp = kstat_open()) == NULL) { warn("kstat open operation failed"); return (NULL); } ksp = dladm_kstat_lookup(kcp, module, instance, "mac", NULL); if (ksp == NULL) goto done; aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t)); if (aggr_port_stat_entry == NULL) goto done; /* Save port's linkid */ aggr_port_stat_entry->ape_portlinkid = linkid; i_dlstat_get_stats(kcp, ksp, &aggr_port_stat_entry->ape_stats, aggr_port_stats_list, AGGR_PORT_STAT_SIZE); done: (void) kstat_close(kcp); return (aggr_port_stat_entry); } void * dlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid) { dladm_aggr_grp_attr_t ginfo; int i; dladm_aggr_port_attr_t *portp; dladm_phys_attr_t dpa; aggr_port_stat_entry_t *aggr_port_stat_entry; dladm_stat_chain_t *head = NULL, *prev = NULL, *curr; dladm_stat_chain_t *total_stats; /* Get aggr info */ bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t)); if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) goto done; /* For every port that is member of this aggr do */ for (i = 0; i < ginfo.lg_nports; i++) { portp = &(ginfo.lg_ports[i]); if (dladm_phys_info(dh, portp->lp_linkid, &dpa, DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) { goto done; } aggr_port_stat_entry = i_dlstat_single_port_stats(dpa.dp_dev, portp->lp_linkid); /* Create dladm_stat_chain_t object for this stat */ curr = malloc(sizeof (dladm_stat_chain_t)); if (curr == NULL) { free(aggr_port_stat_entry); goto done; } (void) strlcpy(curr->dc_statheader, dpa.dp_dev, sizeof (curr->dc_statheader)); curr->dc_statentry = aggr_port_stat_entry; curr->dc_next = NULL; /* Chain this aggr port stat entry */ /* head of the stat list */ if (prev == NULL) head = curr; else prev->dc_next = curr; prev = curr; } /* * Prepend the stat list with cumulative aggr stats i.e. summed over all * component ports */ total_stats = dlstat_aggr_total_stats(head); if (total_stats != NULL) { total_stats->dc_next = head; head = total_stats; } done: free(ginfo.lg_ports); return (head); } /* Misc stat specific functions */ void * dlstat_misc_stats(dladm_handle_t dh, datalink_id_t linkid) { misc_stat_entry_t *misc_stat_entry; dladm_stat_chain_t *head = NULL; char linkname[MAXLINKNAMELEN]; if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) { goto done; } misc_stat_entry = i_dlstat_misc_stats(linkname); if (misc_stat_entry == NULL) goto done; head = malloc(sizeof (dladm_stat_chain_t)); if (head == NULL) { free(misc_stat_entry); goto done; } head->dc_statentry = misc_stat_entry; (void) strlcpy(head->dc_statheader, "mac_misc_stat", sizeof (head->dc_statheader)); head->dc_next = NULL; done: return (head); } /* Exported functions */ dladm_stat_chain_t * dladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid, dladm_stat_type_t stattype) { return (dladm_stat_table[stattype].ds_querystat(dh, linkid)); } dladm_stat_chain_t * dladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2, dladm_stat_type_t stattype) { dladm_stat_chain_t *op1_curr, *op2_curr; dladm_stat_chain_t *diff_curr; dladm_stat_chain_t *diff_prev = NULL, *diff_head = NULL; /* Perform op1 - op2, store result in diff */ for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) { for (op2_curr = op2; op2_curr != NULL; op2_curr = op2_curr->dc_next) { if (dlstat_match_stats(op1_curr->dc_statentry, op2_curr->dc_statentry, stattype)) { break; } } diff_curr = malloc(sizeof (dladm_stat_chain_t)); if (diff_curr == NULL) goto done; diff_curr->dc_next = NULL; if (op2_curr == NULL) { /* prev iteration did not have this stat entry */ diff_curr->dc_statentry = dlstat_diff_stats(op1_curr->dc_statentry, NULL, stattype); } else { diff_curr->dc_statentry = dlstat_diff_stats(op1_curr->dc_statentry, op2_curr->dc_statentry, stattype); } if (diff_curr->dc_statentry == NULL) { free(diff_curr); goto done; } if (diff_prev == NULL) /* head of the diff stat list */ diff_head = diff_curr; else diff_prev->dc_next = diff_curr; diff_prev = diff_curr; } done: return (diff_head); } void dladm_link_stat_free(dladm_stat_chain_t *curr) { while (curr != NULL) { dladm_stat_chain_t *tofree = curr; curr = curr->dc_next; free(tofree->dc_statentry); free(tofree); } } /* Query all link stats */ static name_value_stat_t * i_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size) { int i; name_value_stat_t *head_stat = NULL, *prev_stat = NULL; name_value_stat_t *curr_stat; for (i = 0; i < size; i++) { uint64_t *val = (void *) ((uchar_t *)stats + stats_list[i].si_offset); curr_stat = calloc(1, sizeof (name_value_stat_t)); if (curr_stat == NULL) break; (void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name, sizeof (curr_stat->nv_statname)); curr_stat->nv_statval = *val; curr_stat->nv_nextstat = NULL; if (head_stat == NULL) /* First node */ head_stat = curr_stat; else prev_stat->nv_nextstat = curr_stat; prev_stat = curr_stat; } return (head_stat); } void * build_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype) { name_value_stat_entry_t *name_value_stat_entry; dladm_stat_desc_t *stattbl_ptr; void *statfields; stattbl_ptr = &dladm_stat_table[stattype]; /* Allocate memory for query all stat entry */ name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t)); if (name_value_stat_entry == NULL) goto done; /* Header for these stat fields */ (void) strlcpy(name_value_stat_entry->nve_header, statheader, sizeof (name_value_stat_entry->nve_header)); /* Extract stat fields from the statentry */ statfields = (uchar_t *)statentry + dladm_stat_table[stattype].ds_offset; /* Convert curr_stat to pair */ name_value_stat_entry->nve_stats = i_dlstat_convert_stats(statfields, stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize); done: return (name_value_stat_entry); } void * i_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype) { dladm_stat_chain_t *curr; dladm_stat_chain_t *nvstat_head = NULL, *nvstat_prev = NULL; dladm_stat_chain_t *nvstat_curr; /* * For every stat in the chain, build header and convert all * its stat fields */ for (curr = stat_head; curr != NULL; curr = curr->dc_next) { nvstat_curr = malloc(sizeof (dladm_stat_chain_t)); if (nvstat_curr == NULL) break; nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader, curr->dc_statentry, stattype); if (nvstat_curr->dc_statentry == NULL) { free(nvstat_curr); break; } nvstat_curr->dc_next = NULL; if (nvstat_head == NULL) /* First node */ nvstat_head = nvstat_curr; else nvstat_prev->dc_next = nvstat_curr; nvstat_prev = nvstat_curr; } done: return (nvstat_head); } dladm_stat_chain_t * dladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid, dladm_stat_type_t stattype) { dladm_stat_chain_t *stat_head; dladm_stat_chain_t *nvstat_head = NULL; /* Query the requested stat */ stat_head = dladm_link_stat_query(dh, linkid, stattype); if (stat_head == NULL) goto done; /* * Convert every statfield in every stat-entry of stat chain to * pair */ nvstat_head = i_walk_dlstat_chain(stat_head, stattype); /* Free stat_head */ dladm_link_stat_free(stat_head); done: return (nvstat_head); } void dladm_link_stat_query_all_free(dladm_stat_chain_t *curr) { while (curr != NULL) { dladm_stat_chain_t *tofree = curr; name_value_stat_entry_t *nv_entry = curr->dc_statentry; name_value_stat_t *nv_curr = nv_entry->nve_stats; while (nv_curr != NULL) { name_value_stat_t *nv_tofree = nv_curr; nv_curr = nv_curr->nv_nextstat; free(nv_tofree); } curr = curr->dc_next; free(nv_entry); free(tofree); } } /* flow stats specific routines */ flow_stat_t * dladm_flow_stat_query(const char *flowname) { kstat_ctl_t *kcp; kstat_t *ksp; flow_stat_t *flow_stat = NULL; if ((kcp = kstat_open()) == NULL) return (NULL); flow_stat = calloc(1, sizeof (flow_stat_t)); if (flow_stat == NULL) goto done; ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow"); if (ksp != NULL) { i_dlstat_get_stats(kcp, ksp, flow_stat, flow_stats_list, FLOW_STAT_SIZE); } done: (void) kstat_close(kcp); return (flow_stat); } flow_stat_t * dladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2) { flow_stat_t *diff_stat; diff_stat = calloc(1, sizeof (flow_stat_t)); if (diff_stat == NULL) goto done; if (op2 == NULL) { bcopy(op1, diff_stat, sizeof (flow_stat_t)); } else { i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list, FLOW_STAT_SIZE); } done: return (diff_stat); } void dladm_flow_stat_free(flow_stat_t *curr) { free(curr); } /* Query all flow stats */ name_value_stat_entry_t * dladm_flow_stat_query_all(const char *flowname) { flow_stat_t *flow_stat; name_value_stat_entry_t *name_value_stat_entry = NULL; /* Query flow stats */ flow_stat = dladm_flow_stat_query(flowname); if (flow_stat == NULL) goto done; /* Allocate memory for query all stat entry */ name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t)); if (name_value_stat_entry == NULL) { dladm_flow_stat_free(flow_stat); goto done; } /* Header for these stat fields */ (void) strncpy(name_value_stat_entry->nve_header, flowname, MAXFLOWNAMELEN); /* Convert every statfield in flow_stat to pair */ name_value_stat_entry->nve_stats = i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE); /* Free flow_stat */ dladm_flow_stat_free(flow_stat); done: return (name_value_stat_entry); } void dladm_flow_stat_query_all_free(name_value_stat_entry_t *curr) { name_value_stat_t *nv_curr = curr->nve_stats; while (nv_curr != NULL) { name_value_stat_t *nv_tofree = nv_curr; nv_curr = nv_curr->nv_nextstat; free(nv_tofree); } }