2N/A/*
2N/A * CDDL HEADER START
2N/A *
2N/A * The contents of this file are subject to the terms of the
2N/A * Common Development and Distribution License (the "License").
2N/A * You may not use this file except in compliance with the License.
2N/A *
2N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
2N/A * or http://www.opensolaris.org/os/licensing.
2N/A * See the License for the specific language governing permissions
2N/A * and limitations under the License.
2N/A *
2N/A * When distributing Covered Code, include this CDDL HEADER in each
2N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
2N/A * If applicable, add the following below this CDDL HEADER, with the
2N/A * fields enclosed by brackets "[]" replaced with your own identifying
2N/A * information: Portions Copyright [yyyy] [name of copyright owner]
2N/A *
2N/A * CDDL HEADER END
2N/A */
2N/A/*
2N/A * Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A#include <stdio.h>
2N/A#include <stdlib.h>
2N/A#include <strings.h>
2N/A#include <err.h>
2N/A#include <errno.h>
2N/A#include <fcntl.h>
2N/A#include <kstat.h>
2N/A#include <limits.h>
2N/A#include <unistd.h>
2N/A#include <signal.h>
2N/A#include <sys/dld.h>
2N/A#include <sys/ddi.h>
2N/A#include <zone.h>
2N/A#include <vdp.h>
2N/A#include <vdp_vdpd.h>
2N/A#include <door.h>
2N/A
2N/A#include <libdllink.h>
2N/A#include <libdlflow.h>
2N/A#include <libdlstat.h>
2N/A#include <libdlaggr.h>
2N/A#include <libinetutil.h>
2N/A#include <libdlvnic.h>
2N/A
2N/A/*
2N/A * x86 <sys/regs> ERR conflicts with <curses.h> ERR.
2N/A * Include curses.h last.
2N/A */
2N/A#if defined(ERR)
2N/A#undef ERR
2N/A#endif
2N/A#include <curses.h>
2N/A
2N/A#define DLSTAT_MAC_RX_HWLANE "mac_rx_hwlane"
2N/A#define DLSTAT_MAC_TX_HWLANE "mac_tx_hwlane"
2N/A#define DLSTAT_MAC_LINK_STAT "link"
2N/A#define DLSTAT_MAC_PHYS_STAT "phys"
2N/A#define DLSTAT_MAC_RX_RING "mac_rx_ring"
2N/A#define DLSTAT_MAC_TX_RING "mac_tx_ring"
2N/A
2N/Astruct flowlist {
2N/A char flowname[MAXFLOWNAMELEN];
2N/A char linkname[MAXLINKNAMELEN];
2N/A datalink_id_t linkid;
2N/A int fd;
2N/A uint64_t ifspeed;
2N/A boolean_t first;
2N/A boolean_t display;
2N/A pktsum_t prevstats;
2N/A pktsum_t diffstats;
2N/A};
2N/A
2N/Astatic int maxx, maxy, redraw = 0;
2N/Astatic volatile uint_t handle_resize = 0, handle_break = 0;
2N/A
2N/Apktsum_t totalstats;
2N/Astruct flowlist *stattable = NULL;
2N/Astatic int statentry = -1, maxstatentries = 0;
2N/A
2N/A#define STATGROWSIZE 16
2N/A
2N/A/*
2N/A * Search for flowlist entry in stattable which matches
2N/A * the flowname and linkid. If no match is found, use
2N/A * next available slot. If no slots are available,
2N/A * reallocate table with more slots.
2N/A *
2N/A * Return: *flowlist of matching flow
2N/A * NULL if realloc fails
2N/A */
2N/A
2N/Astatic struct flowlist *
2N/Afindstat(const char *flowname, datalink_id_t linkid)
2N/A{
2N/A int match = 0;
2N/A struct flowlist *flist;
2N/A
2N/A /* Look for match in the stattable */
2N/A for (match = 0, flist = stattable;
2N/A match <= statentry;
2N/A match++, flist++) {
2N/A
2N/A if (flist == NULL)
2N/A break;
2N/A /* match the flowname */
2N/A if (flowname != NULL) {
2N/A if (strncmp(flowname, flist->flowname, MAXFLOWNAMELEN)
2N/A == NULL)
2N/A return (flist);
2N/A /* match the linkid */
2N/A } else {
2N/A if (linkid == flist->linkid)
2N/A return (flist);
2N/A }
2N/A }
2N/A
2N/A /*
2N/A * No match found in the table. Store statistics in the next slot.
2N/A * If necessary, make room for this entry.
2N/A */
2N/A statentry++;
2N/A if ((maxstatentries == 0) || (maxstatentries == statentry)) {
2N/A maxstatentries += STATGROWSIZE;
2N/A stattable = realloc(stattable,
2N/A maxstatentries * sizeof (struct flowlist));
2N/A if (stattable == NULL) {
2N/A perror("realloc");
2N/A return (struct flowlist *)(NULL);
2N/A }
2N/A }
2N/A flist = &stattable[statentry];
2N/A bzero(flist, sizeof (struct flowlist));
2N/A
2N/A if (flowname != NULL)
2N/A (void) strncpy(flist->flowname, flowname, MAXFLOWNAMELEN);
2N/A flist->linkid = linkid;
2N/A flist->fd = INT32_MAX;
2N/A
2N/A return (flist);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Aprint_flow_stats(dladm_handle_t handle, struct flowlist *flist)
2N/A{
2N/A struct flowlist *fcurr;
2N/A double ikbs, okbs;
2N/A double ipks, opks;
2N/A double dlt;
2N/A int fcount;
2N/A static boolean_t first = B_TRUE;
2N/A
2N/A if (first) {
2N/A first = B_FALSE;
2N/A (void) printw("please wait...\n");
2N/A return;
2N/A }
2N/A
2N/A for (fcount = 0, fcurr = flist;
2N/A fcount <= statentry;
2N/A fcount++, fcurr++) {
2N/A if (fcurr->flowname && fcurr->display) {
2N/A dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
2N/A ikbs = fcurr->diffstats.rbytes * 8 / dlt / 1024;
2N/A okbs = fcurr->diffstats.obytes * 8 / dlt / 1024;
2N/A ipks = fcurr->diffstats.ipackets / dlt;
2N/A opks = fcurr->diffstats.opackets / dlt;
2N/A (void) printw("%-15.15s", fcurr->flowname);
2N/A (void) printw("%-10.10s", fcurr->linkname);
2N/A (void) printw("%9.2f %9.2f %9.2f %9.2f ",
2N/A ikbs, okbs, ipks, opks);
2N/A (void) printw("\n");
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Aflow_kstats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
2N/A{
2N/A kstat_ctl_t *kcp = (kstat_ctl_t *)arg;
2N/A kstat_t *ksp;
2N/A struct flowlist *flist;
2N/A pktsum_t currstats, *prevstats, *diffstats;
2N/A char flowname[MAXFLOWNAMELEN], zonename[ZONENAME_MAX];
2N/A zoneid_t flowzoneid = attr->fa_flow_desc.fd_zoneid;
2N/A
2N/A if (flowzoneid == getzoneid()) {
2N/A (void) snprintf(flowname, sizeof (flowname), "%s",
2N/A attr->fa_flowname);
2N/A } else {
2N/A ssize_t s;
2N/A /* need to check err code, if zone is halt */
2N/A s = getzonenamebyid(flowzoneid, zonename, sizeof (zonename));
2N/A if (s < 0)
2N/A return (DLADM_STATUS_NOTFOUND);
2N/A
2N/A (void) snprintf(flowname, sizeof (flowname), "%s/%s",
2N/A zonename, attr->fa_flowname);
2N/A }
2N/A
2N/A flist = findstat(flowname, attr->fa_linkid);
2N/A if (flist == NULL)
2N/A return (DLADM_WALK_CONTINUE);
2N/A
2N/A flist->display = B_FALSE;
2N/A prevstats = &flist->prevstats;
2N/A diffstats = &flist->diffstats;
2N/A
2N/A (void) dladm_datalink_id2info(handle, attr->fa_linkid, NULL, NULL,
2N/A NULL, flist->linkname, sizeof (flist->linkname));
2N/A
2N/A /* lookup kstat entry */
2N/A ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
2N/A
2N/A if (ksp == NULL)
2N/A return (DLADM_WALK_CONTINUE);
2N/A
2N/A /* read packet and byte stats */
2N/A dladm_get_stats(kcp, ksp, &currstats);
2N/A
2N/A if (flist->ifspeed == 0)
2N/A (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
2N/A &flist->ifspeed);
2N/A
2N/A if (flist->first) {
2N/A flist->first = B_FALSE;
2N/A } else {
2N/A dladm_stats_diff(diffstats, &currstats, prevstats);
2N/A if (diffstats->snaptime == 0)
2N/A return (DLADM_WALK_CONTINUE);
2N/A dladm_stats_total(&totalstats, diffstats, &totalstats);
2N/A }
2N/A
2N/A bcopy(&currstats, prevstats, sizeof (pktsum_t));
2N/A flist->display = B_TRUE;
2N/A
2N/A return (DLADM_WALK_CONTINUE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Aprint_link_stats(dladm_handle_t handle, struct flowlist *flist)
2N/A{
2N/A struct flowlist *fcurr;
2N/A double ikbs, okbs;
2N/A double ipks, opks;
2N/A double util;
2N/A double dlt;
2N/A int fcount;
2N/A static boolean_t first = B_TRUE;
2N/A
2N/A if (first) {
2N/A first = B_FALSE;
2N/A (void) printw("please wait...\n");
2N/A return;
2N/A }
2N/A
2N/A for (fcount = 0, fcurr = flist;
2N/A fcount <= statentry;
2N/A fcount++, fcurr++) {
2N/A if ((fcurr->linkid != DATALINK_INVALID_LINKID) &&
2N/A fcurr->display) {
2N/A dlt = (double)fcurr->diffstats.snaptime/(double)NANOSEC;
2N/A ikbs = (double)fcurr->diffstats.rbytes * 8 / dlt / 1024;
2N/A okbs = (double)fcurr->diffstats.obytes * 8 / dlt / 1024;
2N/A ipks = (double)fcurr->diffstats.ipackets / dlt;
2N/A opks = (double)fcurr->diffstats.opackets / dlt;
2N/A (void) printw("%-10.10s", fcurr->linkname);
2N/A (void) printw("%9.2f %9.2f %9.2f %9.2f ",
2N/A ikbs, okbs, ipks, opks);
2N/A if (fcurr->ifspeed != 0)
2N/A util = ((ikbs + okbs) * 1024) *
2N/A 100/ fcurr->ifspeed;
2N/A else
2N/A util = (double)0;
2N/A (void) attron(A_BOLD);
2N/A (void) printw(" %6.2f", util);
2N/A (void) attroff(A_BOLD);
2N/A (void) printw("\n");
2N/A }
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * This function is called through the dladm_walk_datalink_id() walker and
2N/A * calls the dladm_walk_flow() walker.
2N/A */
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Alink_flowstats(dladm_handle_t handle, datalink_id_t linkid, void *arg)
2N/A{
2N/A dladm_status_t status;
2N/A
2N/A status = dladm_walk_flow(flow_kstats, handle, linkid, arg, B_FALSE);
2N/A if (status == DLADM_STATUS_OK)
2N/A return (DLADM_WALK_CONTINUE);
2N/A else
2N/A return (DLADM_WALK_TERMINATE);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic int
2N/Alink_kstats(dladm_handle_t handle, datalink_id_t linkid, void *arg)
2N/A{
2N/A kstat_ctl_t *kcp = (kstat_ctl_t *)arg;
2N/A struct flowlist *flist;
2N/A pktsum_t currstats, *prevstats, *diffstats;
2N/A datalink_class_t class;
2N/A kstat_t *ksp;
2N/A char dnlink[MAXPATHLEN];
2N/A
2N/A /* find the flist entry */
2N/A flist = findstat(NULL, linkid);
2N/A if (flist == NULL)
2N/A return (DLADM_WALK_CONTINUE);
2N/A
2N/A flist->display = B_FALSE;
2N/A prevstats = &flist->prevstats;
2N/A diffstats = &flist->diffstats;
2N/A
2N/A if (dladm_datalink_id2info(handle, linkid, NULL, &class, NULL,
2N/A flist->linkname, sizeof (flist->linkname)) != DLADM_STATUS_OK)
2N/A return (DLADM_WALK_CONTINUE);
2N/A
2N/A if (flist->fd == INT32_MAX) {
2N/A if (class == DATALINK_CLASS_PHYS) {
2N/A (void) snprintf(dnlink, MAXPATHLEN, "/dev/net/%s",
2N/A flist->linkname);
2N/A if ((flist->fd = open(dnlink, O_RDWR)) < 0)
2N/A return (DLADM_WALK_CONTINUE);
2N/A } else {
2N/A flist->fd = -1;
2N/A }
2N/A (void) kstat_chain_update(kcp);
2N/A }
2N/A
2N/A /* lookup kstat entry */
2N/A ksp = dladm_kstat_lookup(kcp, NULL, -1, flist->linkname, "net");
2N/A
2N/A if (ksp == NULL)
2N/A return (DLADM_WALK_CONTINUE);
2N/A
2N/A /* read packet and byte stats */
2N/A dladm_get_stats(kcp, ksp, &currstats);
2N/A
2N/A if (flist->ifspeed == 0)
2N/A (void) dladm_kstat_value(ksp, "ifspeed", KSTAT_DATA_UINT64,
2N/A &flist->ifspeed);
2N/A
2N/A if (flist->first) {
2N/A flist->first = B_FALSE;
2N/A } else {
2N/A dladm_stats_diff(diffstats, &currstats, prevstats);
2N/A if (diffstats->snaptime == 0)
2N/A return (DLADM_WALK_CONTINUE);
2N/A }
2N/A
2N/A bcopy(&currstats, prevstats, sizeof (*prevstats));
2N/A flist->display = B_TRUE;
2N/A
2N/A return (DLADM_WALK_CONTINUE);
2N/A}
2N/A
2N/Astatic void
2N/Aclosedevnet()
2N/A{
2N/A int index = 0;
2N/A struct flowlist *flist;
2N/A
2N/A /* Close all open /dev/net/ files */
2N/A
2N/A for (flist = stattable; index < maxstatentries; index++, flist++) {
2N/A if (flist->linkid == DATALINK_INVALID_LINKID)
2N/A break;
2N/A if (flist->fd != -1 && flist->fd != INT32_MAX)
2N/A (void) close(flist->fd);
2N/A }
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Asig_break(int s)
2N/A{
2N/A handle_break = 1;
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic void
2N/Asig_resize(int s)
2N/A{
2N/A handle_resize = 1;
2N/A}
2N/A
2N/Astatic void
2N/Acurses_init()
2N/A{
2N/A maxx = maxx; /* lint */
2N/A maxy = maxy; /* lint */
2N/A
2N/A /* Install signal handlers */
2N/A (void) signal(SIGINT, sig_break);
2N/A (void) signal(SIGQUIT, sig_break);
2N/A (void) signal(SIGTERM, sig_break);
2N/A (void) signal(SIGWINCH, sig_resize);
2N/A
2N/A /* Initialize ncurses */
2N/A (void) initscr();
2N/A (void) cbreak();
2N/A (void) noecho();
2N/A (void) curs_set(0);
2N/A timeout(0);
2N/A getmaxyx(stdscr, maxy, maxx);
2N/A}
2N/A
2N/Astatic void
2N/Acurses_fin()
2N/A{
2N/A (void) printw("\n");
2N/A (void) curs_set(1);
2N/A (void) nocbreak();
2N/A (void) endwin();
2N/A
2N/A free(stattable);
2N/A}
2N/A
2N/Astatic void
2N/Astat_report(dladm_handle_t handle, kstat_ctl_t *kcp, datalink_id_t linkid,
2N/A const char *flowname, int opt)
2N/A{
2N/A
2N/A double dlt, ikbs, okbs, ipks, opks;
2N/A
2N/A struct flowlist *fstable = stattable;
2N/A
2N/A if ((opt != LINK_REPORT) && (opt != FLOW_REPORT))
2N/A return;
2N/A
2N/A /* Handle window resizes */
2N/A if (handle_resize) {
2N/A (void) endwin();
2N/A (void) initscr();
2N/A (void) cbreak();
2N/A (void) noecho();
2N/A (void) curs_set(0);
2N/A timeout(0);
2N/A getmaxyx(stdscr, maxy, maxx);
2N/A redraw = 1;
2N/A handle_resize = 0;
2N/A }
2N/A
2N/A /* Print title */
2N/A (void) erase();
2N/A (void) attron(A_BOLD);
2N/A (void) move(0, 0);
2N/A if (opt == FLOW_REPORT)
2N/A (void) printw("%-15.15s", "Flow");
2N/A (void) printw("%-10.10s", "Link");
2N/A (void) printw("%9.9s %9.9s %9.9s %9.9s ",
2N/A "iKb/s", "oKb/s", "iPk/s", "oPk/s");
2N/A if (opt == LINK_REPORT)
2N/A (void) printw(" %6.6s", "%Util");
2N/A (void) printw("\n");
2N/A (void) attroff(A_BOLD);
2N/A
2N/A (void) move(2, 0);
2N/A
2N/A /* Print stats for each link or flow */
2N/A bzero(&totalstats, sizeof (totalstats));
2N/A if (opt == LINK_REPORT) {
2N/A /* Display all links */
2N/A if (linkid == DATALINK_ALL_LINKID) {
2N/A (void) dladm_walk_datalink_id(link_kstats, handle,
2N/A (void *)kcp, DATALINK_CLASS_ALL,
2N/A DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
2N/A /* Display 1 link */
2N/A } else {
2N/A (void) link_kstats(handle, linkid, kcp);
2N/A }
2N/A print_link_stats(handle, fstable);
2N/A
2N/A } else if (opt == FLOW_REPORT) {
2N/A /* Display 1 flow */
2N/A if (flowname != NULL) {
2N/A dladm_flow_attr_t fattr;
2N/A if (dladm_flow_info(handle, flowname, &fattr) !=
2N/A DLADM_STATUS_OK)
2N/A return;
2N/A (void) flow_kstats(handle, &fattr, kcp);
2N/A /* Display all flows on all links */
2N/A } else if (linkid == DATALINK_ALL_LINKID) {
2N/A (void) dladm_walk_datalink_id(link_flowstats, handle,
2N/A (void *)kcp, DATALINK_CLASS_ALL,
2N/A DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
2N/A /* Display all flows on a link */
2N/A } else if (linkid != DATALINK_INVALID_LINKID) {
2N/A (void) dladm_walk_flow(flow_kstats, handle, linkid, kcp,
2N/A B_FALSE);
2N/A }
2N/A print_flow_stats(handle, fstable);
2N/A
2N/A /* Print totals */
2N/A (void) attron(A_BOLD);
2N/A dlt = (double)totalstats.snaptime / (double)NANOSEC;
2N/A ikbs = totalstats.rbytes / dlt / 1024;
2N/A okbs = totalstats.obytes / dlt / 1024;
2N/A ipks = totalstats.ipackets / dlt;
2N/A opks = totalstats.opackets / dlt;
2N/A (void) printw("\n%-25.25s", "Totals");
2N/A (void) printw("%9.2f %9.2f %9.2f %9.2f ",
2N/A ikbs, okbs, ipks, opks);
2N/A (void) attroff(A_BOLD);
2N/A }
2N/A
2N/A if (redraw)
2N/A (void) clearok(stdscr, 1);
2N/A
2N/A if (refresh() == ERR)
2N/A return;
2N/A
2N/A if (redraw) {
2N/A (void) clearok(stdscr, 0);
2N/A redraw = 0;
2N/A }
2N/A}
2N/A
2N/A/* Exported functions */
2N/A
2N/A/*
2N/A * Continuously display link or flow statstics using a libcurses
2N/A * based display.
2N/A */
2N/A
2N/Avoid
2N/Adladm_continuous(dladm_handle_t handle, datalink_id_t linkid,
2N/A const char *flowname, int interval, int opt)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A
2N/A if ((kcp = kstat_open()) == NULL) {
2N/A warn("kstat open operation failed");
2N/A return;
2N/A }
2N/A
2N/A curses_init();
2N/A
2N/A for (;;) {
2N/A
2N/A if (handle_break)
2N/A break;
2N/A
2N/A stat_report(handle, kcp, linkid, flowname, opt);
2N/A
2N/A (void) sleep(max(1, interval));
2N/A }
2N/A
2N/A closedevnet();
2N/A curses_fin();
2N/A (void) kstat_close(kcp);
2N/A}
2N/A
2N/A/*
2N/A * dladm_kstat_lookup() is a modified version of kstat_lookup which
2N/A * adds the class as a selector.
2N/A */
2N/A
2N/Akstat_t *
2N/Adladm_kstat_lookup(kstat_ctl_t *kcp, const char *module, int instance,
2N/A const char *name, const char *class)
2N/A{
2N/A kstat_t *ksp = NULL;
2N/A char kstat_name[MAXLINKNAMELEN];
2N/A char kstat_module[MAXLINKNAMELEN];
2N/A const char *ksname = name;
2N/A const char *ksmodule = module;
2N/A zoneid_t zid;
2N/A
2N/A /*
2N/A * For zonename prefixed linknames retrieve the linkname
2N/A * and set kstats instance to the zone ID. zonename prefixed
2N/A * linknames refer to NGZ datalinks and their kstats are
2N/A * exposed in the GZ under instance set to the zone ID.
2N/A * Callers can pass the linkname in either the module
2N/A * or name argument, the instance can be 0 or -1.
2N/A */
2N/A if (class == NULL || strcmp(class, "flow") != 0) {
2N/A if (instance <= 0 && name != NULL && strchr(name, '/') !=
2N/A NULL) {
2N/A (void) dlparse_zonelinkname(name, kstat_name, &zid);
2N/A ksname = kstat_name;
2N/A instance = zid;
2N/A } else if (instance <= 0 && module != NULL &&
2N/A strchr(module, '/') != NULL) {
2N/A (void) dlparse_zonelinkname(module, kstat_module, &zid);
2N/A ksmodule = kstat_module;
2N/A instance = zid;
2N/A }
2N/A }
2N/A
2N/A for (ksp = kcp->kc_chain; ksp != NULL; ksp = ksp->ks_next) {
2N/A if ((ksmodule == NULL || strcmp(ksp->ks_module, ksmodule) ==
2N/A 0) && (instance == -1 || ksp->ks_instance == instance) &&
2N/A (ksname == NULL || strcmp(ksp->ks_name, ksname) == 0) &&
2N/A (class == NULL || strcmp(ksp->ks_class, class) == 0))
2N/A return (ksp);
2N/A }
2N/A
2N/A errno = ENOENT;
2N/A return (NULL);
2N/A}
2N/A
2N/A/*
2N/A * dladm_get_stats() populates the supplied pktsum_t structure with
2N/A * the input and output packet and byte kstats from the kstat_t
2N/A * found with dladm_kstat_lookup.
2N/A */
2N/Avoid
2N/Adladm_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, pktsum_t *stats)
2N/A{
2N/A
2N/A if (kstat_read(kcp, ksp, NULL) == -1)
2N/A return;
2N/A
2N/A stats->snaptime = gethrtime();
2N/A
2N/A if (dladm_kstat_value(ksp, "ipackets64", KSTAT_DATA_UINT64,
2N/A &stats->ipackets) < 0) {
2N/A if (dladm_kstat_value(ksp, "ipackets", KSTAT_DATA_UINT64,
2N/A &stats->ipackets) < 0)
2N/A return;
2N/A }
2N/A
2N/A if (dladm_kstat_value(ksp, "opackets64", KSTAT_DATA_UINT64,
2N/A &stats->opackets) < 0) {
2N/A if (dladm_kstat_value(ksp, "opackets", KSTAT_DATA_UINT64,
2N/A &stats->opackets) < 0)
2N/A return;
2N/A }
2N/A
2N/A if (dladm_kstat_value(ksp, "rbytes64", KSTAT_DATA_UINT64,
2N/A &stats->rbytes) < 0) {
2N/A if (dladm_kstat_value(ksp, "rbytes", KSTAT_DATA_UINT64,
2N/A &stats->rbytes) < 0)
2N/A return;
2N/A }
2N/A
2N/A if (dladm_kstat_value(ksp, "obytes64", KSTAT_DATA_UINT64,
2N/A &stats->obytes) < 0) {
2N/A if (dladm_kstat_value(ksp, "obytes", KSTAT_DATA_UINT64,
2N/A &stats->obytes) < 0)
2N/A return;
2N/A }
2N/A
2N/A if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT32,
2N/A &stats->ierrors) < 0) {
2N/A if (dladm_kstat_value(ksp, "ierrors", KSTAT_DATA_UINT64,
2N/A &stats->ierrors) < 0)
2N/A return;
2N/A }
2N/A
2N/A if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT32,
2N/A &stats->oerrors) < 0) {
2N/A if (dladm_kstat_value(ksp, "oerrors", KSTAT_DATA_UINT64,
2N/A &stats->oerrors) < 0)
2N/A return;
2N/A }
2N/A}
2N/A
2N/Aint
2N/Adladm_kstat_value(kstat_t *ksp, const char *name, uint8_t type, void *buf)
2N/A{
2N/A kstat_named_t *knp;
2N/A
2N/A if ((knp = kstat_data_lookup(ksp, (char *)name)) == NULL)
2N/A return (-1);
2N/A
2N/A if (knp->data_type != type)
2N/A return (-1);
2N/A
2N/A switch (type) {
2N/A case KSTAT_DATA_UINT64:
2N/A *(uint64_t *)buf = knp->value.ui64;
2N/A break;
2N/A case KSTAT_DATA_UINT32:
2N/A *(uint32_t *)buf = knp->value.ui32;
2N/A break;
2N/A default:
2N/A return (-1);
2N/A }
2N/A
2N/A return (0);
2N/A}
2N/A
2N/Adladm_status_t
2N/Adladm_get_single_mac_stat(dladm_handle_t handle, datalink_id_t linkid,
2N/A const char *name, uint8_t type, void *val)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A char module[DLPI_LINKNAME_MAX];
2N/A uint_t instance;
2N/A char link[DLPI_LINKNAME_MAX];
2N/A dladm_status_t status;
2N/A uint32_t flags, media;
2N/A kstat_t *ksp;
2N/A dladm_phys_attr_t dpap;
2N/A
2N/A if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL,
2N/A &media, link, DLPI_LINKNAME_MAX)) != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A if (media != DL_ETHER)
2N/A return (DLADM_STATUS_LINKINVAL);
2N/A
2N/A status = dladm_phys_info(handle, linkid, &dpap, DLADM_OPT_PERSIST);
2N/A
2N/A if (status != DLADM_STATUS_OK)
2N/A return (status);
2N/A
2N/A if (!dlparse_drvppa(dpap.dp_dev, module, sizeof (module), &instance))
2N/A return (DLADM_STATUS_LINKINVAL);
2N/A
2N/A if ((kcp = kstat_open()) == NULL) {
2N/A warn("kstat_open operation failed");
2N/A return (-1);
2N/A }
2N/A
2N/A /*
2N/A * The kstat query could fail if the underlying MAC
2N/A * driver was already detached.
2N/A */
2N/A if ((ksp = kstat_lookup(kcp, module, instance, DLSTAT_MAC_PHYS_STAT))
2N/A == NULL && (ksp = kstat_lookup(kcp, module, instance, NULL))
2N/A == NULL) {
2N/A goto bail;
2N/A }
2N/A
2N/A if (kstat_read(kcp, ksp, NULL) == -1)
2N/A goto bail;
2N/A
2N/A if (dladm_kstat_value(ksp, name, type, val) < 0)
2N/A goto bail;
2N/A
2N/A (void) kstat_close(kcp);
2N/A return (DLADM_STATUS_OK);
2N/A
2N/Abail:
2N/A (void) kstat_close(kcp);
2N/A return (dladm_errno2status(errno));
2N/A}
2N/A
2N/A/* Compute sum of 2 pktsums (s1 = s2 + s3) */
2N/Avoid
2N/Adladm_stats_total(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2N/A{
2N/A s1->rbytes = s2->rbytes + s3->rbytes;
2N/A s1->ipackets = s2->ipackets + s3->ipackets;
2N/A s1->ierrors = s2->ierrors + s3->ierrors;
2N/A s1->obytes = s2->obytes + s3->obytes;
2N/A s1->opackets = s2->opackets + s3->opackets;
2N/A s1->oerrors = s2->oerrors + s3->oerrors;
2N/A s1->snaptime = s2->snaptime;
2N/A}
2N/A
2N/A#define DIFF_STAT(s2, s3) ((s2) > (s3) ? ((s2) - (s3)) : 0)
2N/A
2N/A
2N/A/* Compute differences between 2 pktsums (s1 = s2 - s3) */
2N/Avoid
2N/Adladm_stats_diff(pktsum_t *s1, pktsum_t *s2, pktsum_t *s3)
2N/A{
2N/A s1->rbytes = DIFF_STAT(s2->rbytes, s3->rbytes);
2N/A s1->ipackets = DIFF_STAT(s2->ipackets, s3->ipackets);
2N/A s1->ierrors = DIFF_STAT(s2->ierrors, s3->ierrors);
2N/A s1->obytes = DIFF_STAT(s2->obytes, s3->obytes);
2N/A s1->opackets = DIFF_STAT(s2->opackets, s3->opackets);
2N/A s1->oerrors = DIFF_STAT(s2->oerrors, s3->oerrors);
2N/A s1->snaptime = DIFF_STAT(s2->snaptime, s3->snaptime);
2N/A}
2N/A
2N/Atypedef struct {
2N/A const char *si_name;
2N/A uint_t si_offset;
2N/A} stat_info_t;
2N/A
2N/A#define A_CNT(arr) (sizeof (arr) / sizeof (arr[0]))
2N/A
2N/A/* Definitions for rx lane stats */
2N/A#define RL_OFF(f) (offsetof(rx_lane_stat_t, f))
2N/A
2N/Astatic stat_info_t rx_hwlane_stats_list[] = {
2N/A {"ipackets", RL_OFF(rl_ipackets)},
2N/A {"rbytes", RL_OFF(rl_rbytes)},
2N/A {"intrs", RL_OFF(rl_intrs)},
2N/A {"intrbytes", RL_OFF(rl_intrbytes)},
2N/A {"polls", RL_OFF(rl_polls)},
2N/A {"pollbytes", RL_OFF(rl_pollbytes)},
2N/A {"idrops", RL_OFF(rl_idrops)},
2N/A {"idropbytes", RL_OFF(rl_idropbytes)}
2N/A};
2N/A#define RX_HWLANE_STAT_SIZE A_CNT(rx_hwlane_stats_list)
2N/A
2N/Astatic stat_info_t rx_lane_stats_list[] = {
2N/A {"ipackets", RL_OFF(rl_ipackets)},
2N/A {"rbytes", RL_OFF(rl_rbytes)},
2N/A {"rxlocal", RL_OFF(rl_lclpackets)},
2N/A {"rxlocalbytes", RL_OFF(rl_lclbytes)},
2N/A {"intrs", RL_OFF(rl_intrs)},
2N/A {"intrbytes", RL_OFF(rl_intrbytes)},
2N/A {"polls", RL_OFF(rl_polls)},
2N/A {"pollbytes", RL_OFF(rl_pollbytes)},
2N/A {"idrops", RL_OFF(rl_idrops)},
2N/A {"idropbytes", RL_OFF(rl_idropbytes)},
2N/A};
2N/A#define RX_LANE_STAT_SIZE A_CNT(rx_lane_stats_list)
2N/A
2N/A/* Definitions for tx lane stats */
2N/A#define TL_OFF(f) (offsetof(tx_lane_stat_t, f))
2N/A
2N/Astatic stat_info_t tx_lane_stats_list[] = {
2N/A {"opackets", TL_OFF(tl_opackets)},
2N/A {"obytes", TL_OFF(tl_obytes)},
2N/A {"blockcnt", TL_OFF(tl_blockcnt)},
2N/A {"unblockcnt", TL_OFF(tl_unblockcnt)},
2N/A {"odrops", TL_OFF(tl_odrops)},
2N/A {"odropbytes", TL_OFF(tl_odropbytes)}
2N/A};
2N/A#define TX_LANE_STAT_SIZE A_CNT(tx_lane_stats_list)
2N/A
2N/A/* Definitions for tx/rx link stats */
2N/A#define L_OFF(f) (offsetof(link_stat_t, f))
2N/A
2N/Astatic stat_info_t link_stats_list[] = {
2N/A {"multircv", L_OFF(ls_multircv)},
2N/A {"brdcstrcv", L_OFF(ls_brdcstrcv)},
2N/A {"multixmt", L_OFF(ls_multixmt)},
2N/A {"brdcstxmt", L_OFF(ls_brdcstxmt)},
2N/A {"multircvbytes", L_OFF(ls_multircvbytes)},
2N/A {"bcstrcvbytes", L_OFF(ls_brdcstrcvbytes)},
2N/A {"multixmtbytes", L_OFF(ls_multixmtbytes)},
2N/A {"bcstxmtbytes", L_OFF(ls_brdcstxmtbytes)},
2N/A {"txerrors", L_OFF(ls_txerrors)},
2N/A {"macspoofed", L_OFF(ls_macspoofed)},
2N/A {"ipspoofed", L_OFF(ls_ipspoofed)},
2N/A {"dhcpspoofed", L_OFF(ls_dhcpspoofed)},
2N/A {"restricted", L_OFF(ls_restricted)},
2N/A {"ipackets", L_OFF(ls_ipackets)},
2N/A {"rbytes", L_OFF(ls_rbytes)},
2N/A {"rxlocal", L_OFF(ls_rxlocal)},
2N/A {"rxlocalbytes", L_OFF(ls_rxlocalbytes)},
2N/A {"txlocal", L_OFF(ls_txlocal)},
2N/A {"txlocalbytes", L_OFF(ls_txlocalbytes)},
2N/A {"intrs", L_OFF(ls_intrs)},
2N/A {"intrbytes", L_OFF(ls_intrbytes)},
2N/A {"polls", L_OFF(ls_polls)},
2N/A {"pollbytes", L_OFF(ls_pollbytes)},
2N/A {"idrops", L_OFF(ls_idrops)},
2N/A {"idropbytes", L_OFF(ls_idropbytes)},
2N/A {"obytes", L_OFF(ls_obytes)},
2N/A {"opackets", L_OFF(ls_opackets)},
2N/A {"blockcnt", L_OFF(ls_blockcnt)},
2N/A {"unblockcnt", L_OFF(ls_unblockcnt)},
2N/A {"odrops", L_OFF(ls_odrops)},
2N/A {"odropbytes", L_OFF(ls_odropbytes)}
2N/A};
2N/A#define LINK_STAT_SIZE A_CNT(link_stats_list)
2N/A
2N/A/* Definitions for rx ring stats */
2N/A#define R_OFF(f) (offsetof(ring_stat_t, f))
2N/A
2N/Astatic stat_info_t rx_ring_stats_list[] = {
2N/A {"ipackets", R_OFF(r_packets)},
2N/A {"rbytes", R_OFF(r_bytes)}
2N/A};
2N/A#define RX_RING_STAT_SIZE A_CNT(rx_ring_stats_list)
2N/A
2N/A/* Definitions for tx ring stats */
2N/Astatic stat_info_t tx_ring_stats_list[] = {
2N/A {"opackets", R_OFF(r_packets)},
2N/A {"obytes", R_OFF(r_bytes)}
2N/A};
2N/A#define TX_RING_STAT_SIZE A_CNT(tx_ring_stats_list)
2N/A
2N/A/* Definitions for total stats */
2N/A#define T_OFF(f) (offsetof(total_stat_t, f))
2N/A
2N/Astatic stat_info_t total_stats_list[] = {
2N/A {"ipackets64", T_OFF(ts_ipackets)},
2N/A {"rbytes64", T_OFF(ts_rbytes)},
2N/A {"opackets64", T_OFF(ts_opackets)},
2N/A {"obytes64", T_OFF(ts_obytes)}
2N/A};
2N/A#define TOTAL_STAT_SIZE A_CNT(total_stats_list)
2N/A
2N/A/* Definitions for aggr stats */
2N/A#define AP_OFF(f) (offsetof(aggr_port_stat_t, f))
2N/A
2N/Astatic stat_info_t aggr_port_stats_list[] = {
2N/A {"ipackets64", AP_OFF(ap_ipackets)},
2N/A {"rbytes64", AP_OFF(ap_rbytes)},
2N/A {"opackets64", AP_OFF(ap_opackets)},
2N/A {"obytes64", AP_OFF(ap_obytes)}
2N/A};
2N/A#define AGGR_PORT_STAT_SIZE A_CNT(aggr_port_stats_list)
2N/A
2N/A/* Definitions for VDP stats */
2N/A#define VS_OFF(f) (offsetof(vdp_stat_t, f))
2N/A
2N/Astatic stat_info_t vdp_stats_list[] = {
2N/A {"ipackets64", VS_OFF(vs_ipkts)},
2N/A {"opackets64", VS_OFF(vs_opkts)},
2N/A {"keepalives64", VS_OFF(vs_kas)}
2N/A};
2N/A#define VDP_STAT_SIZE A_CNT(vdp_stats_list)
2N/A
2N/A/* Definitions for ECP stats */
2N/A#define ES_OFF(f) (offsetof(ecp_stat_t, f))
2N/A
2N/Astatic stat_info_t ecp_stats_list[] = {
2N/A {"ipackets64", ES_OFF(es_ipkts)},
2N/A {"opackets64", ES_OFF(es_opkts)},
2N/A {"ierrors64", ES_OFF(es_ierrors)},
2N/A {"oerrors64", ES_OFF(es_oerrors)},
2N/A {"rexmits64", ES_OFF(es_rexmits)},
2N/A {"timeout64", ES_OFF(es_timeouts)}
2N/A};
2N/A#define ECP_STAT_SIZE A_CNT(ecp_stats_list)
2N/A
2N/A/* Definitions for flow stats */
2N/A#define FL_OFF(f) (offsetof(flow_stat_t, f))
2N/A
2N/Astatic stat_info_t flow_stats_list[] = {
2N/A {"ipackets", FL_OFF(fl_ipackets)},
2N/A {"rbytes", FL_OFF(fl_rbytes)},
2N/A {"idrops", FL_OFF(fl_idrops)},
2N/A {"idropbytes", FL_OFF(fl_idropbytes)},
2N/A {"opackets", FL_OFF(fl_opackets)},
2N/A {"obytes", FL_OFF(fl_obytes)},
2N/A {"odrops", FL_OFF(fl_odrops)},
2N/A {"odropbytes", FL_OFF(fl_odropbytes)},
2N/A};
2N/A#define FLOW_STAT_SIZE A_CNT(flow_stats_list)
2N/A
2N/A/* Rx lane specific functions */
2N/Avoid * dlstat_rx_lane_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_rx_lane_match(void *, void *);
2N/Astatic void * i_dlstat_rx_lane_stat_entry_diff(void *, void *);
2N/A
2N/A/* Tx lane specific functions */
2N/Avoid * dlstat_tx_lane_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_tx_lane_match(void *, void *);
2N/Astatic void * i_dlstat_tx_lane_stat_entry_diff(void *, void *);
2N/A
2N/A/* Rx lane total specific functions */
2N/Avoid * dlstat_rx_lane_total_stats(dladm_handle_t,
2N/A datalink_id_t);
2N/A
2N/A/* Tx lane total specific functions */
2N/Avoid * dlstat_tx_lane_total_stats(dladm_handle_t,
2N/A datalink_id_t);
2N/A
2N/A/* Rx ring specific functions */
2N/Avoid * dlstat_rx_ring_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_rx_ring_match(void *, void *);
2N/Astatic void * i_dlstat_rx_ring_stat_entry_diff(void *, void *);
2N/A
2N/A/* Tx ring specific functions */
2N/Avoid * dlstat_tx_ring_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_tx_ring_match(void *, void *);
2N/Astatic void * i_dlstat_tx_ring_stat_entry_diff(void *, void *);
2N/A
2N/A/* Rx ring total specific functions */
2N/Avoid * dlstat_rx_ring_total_stats(dladm_handle_t,
2N/A datalink_id_t);
2N/A
2N/A/* Tx ring total specific functions */
2N/Avoid * dlstat_tx_ring_total_stats(dladm_handle_t,
2N/A datalink_id_t);
2N/A
2N/A/* Summary specific functions */
2N/Avoid * dlstat_link_total_stats(dladm_handle_t, datalink_id_t);
2N/Avoid * dlstat_phys_total_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_total_match(void *, void *);
2N/Astatic void * i_dlstat_total_stat_entry_diff(void *, void *);
2N/A
2N/A/* Aggr port specific functions */
2N/Avoid * dlstat_aggr_port_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_aggr_port_match(void *, void *);
2N/Astatic void * i_dlstat_aggr_port_stat_entry_diff(void *, void *);
2N/A
2N/A/* VDP specific functions */
2N/Avoid * dlstat_vdp_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_vdp_match(void *, void *);
2N/Astatic void * i_dlstat_vdp_stat_entry_diff(void *, void *);
2N/A
2N/A/* ECP specific functions */
2N/Avoid * dlstat_ecp_stats(dladm_handle_t, datalink_id_t);
2N/Astatic boolean_t i_dlstat_ecp_match(void *, void *);
2N/Astatic void * i_dlstat_ecp_stat_entry_diff(void *, void *);
2N/A
2N/A/* Link stat specific functions */
2N/Avoid * dlstat_link_stats(dladm_handle_t, datalink_id_t);
2N/A
2N/Atypedef void * dladm_stat_query_t(dladm_handle_t, datalink_id_t);
2N/Atypedef boolean_t dladm_stat_match_t(void *, void *);
2N/Atypedef void * dladm_stat_diff_t(void *, void *);
2N/A
2N/Atypedef struct dladm_stat_desc_s {
2N/A dladm_stat_type_t ds_stattype;
2N/A dladm_stat_query_t *ds_querystat;
2N/A dladm_stat_match_t *ds_matchstat;
2N/A dladm_stat_diff_t *ds_diffstat;
2N/A uint_t ds_offset;
2N/A stat_info_t *ds_statlist;
2N/A uint_t ds_statsize;
2N/A} dladm_stat_desc_t;
2N/A
2N/A/*
2N/A * dladm_stat_table has one entry for each supported stat. ds_querystat returns
2N/A * a chain of 'stat entries' for the queried stat.
2N/A * Each stat entry has set of identifiers (ids) and an object containing actual
2N/A * stat values. These stat entry objects are chained together in a linked list
2N/A * of datatype dladm_stat_chain_t. Head of this list is returned to the caller
2N/A * of dladm_link_stat_query.
2N/A *
2N/A * One node in the chain is shown below:
2N/A *
2N/A * -------------------------
2N/A * | dc_statentry |
2N/A * | -------------- |
2N/A * | | ids | |
2N/A * | -------------- |
2N/A * | | stat fields | |
2N/A * | -------------- |
2N/A * -------------------------
2N/A * | dc_next ---------|------> to next stat entry
2N/A * -------------------------
2N/A *
2N/A * In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
2N/A * object of type rx_lane_stat_entry_t.
2N/A *
2N/A * dladm_link_stat_query_all returns similar chain. However, instead of storing
2N/A * stat fields as raw numbers, it stores those as chain of <name, value> pairs.
2N/A * The resulting structure is depicted below:
2N/A *
2N/A * -------------------------
2N/A * | dc_statentry |
2N/A * | -------------- | ---------------
2N/A * | | nv_header | | | name, val |
2N/A * | -------------- | ---------------
2N/A * | | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair
2N/A * | -------------- | ---------------
2N/A * -------------------------
2N/A * | dc_next ---------|------> to next stat entry
2N/A * -------------------------
2N/A */
2N/Astatic dladm_stat_desc_t dladm_stat_table[] = {
2N/A{ DLADM_STAT_RX_LANE, dlstat_rx_lane_stats,
2N/A i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
2N/A offsetof(rx_lane_stat_entry_t, rle_stats),
2N/A rx_lane_stats_list, RX_LANE_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_TX_LANE, dlstat_tx_lane_stats,
2N/A i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
2N/A offsetof(tx_lane_stat_entry_t, tle_stats),
2N/A tx_lane_stats_list, TX_LANE_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_RX_LANE_TOTAL, dlstat_rx_lane_total_stats,
2N/A i_dlstat_rx_lane_match, i_dlstat_rx_lane_stat_entry_diff,
2N/A offsetof(rx_lane_stat_entry_t, rle_stats),
2N/A rx_lane_stats_list, RX_LANE_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_TX_LANE_TOTAL, dlstat_tx_lane_total_stats,
2N/A i_dlstat_tx_lane_match, i_dlstat_tx_lane_stat_entry_diff,
2N/A offsetof(tx_lane_stat_entry_t, tle_stats),
2N/A tx_lane_stats_list, TX_LANE_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_RX_RING, dlstat_rx_ring_stats,
2N/A i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
2N/A offsetof(ring_stat_entry_t, re_stats),
2N/A rx_ring_stats_list, RX_RING_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_TX_RING, dlstat_tx_ring_stats,
2N/A i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
2N/A offsetof(ring_stat_entry_t, re_stats),
2N/A tx_ring_stats_list, TX_RING_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_RX_RING_TOTAL, dlstat_rx_ring_total_stats,
2N/A i_dlstat_rx_ring_match, i_dlstat_rx_ring_stat_entry_diff,
2N/A offsetof(ring_stat_entry_t, re_stats),
2N/A rx_ring_stats_list, RX_RING_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_TX_RING_TOTAL, dlstat_tx_ring_total_stats,
2N/A i_dlstat_tx_ring_match, i_dlstat_tx_ring_stat_entry_diff,
2N/A offsetof(ring_stat_entry_t, re_stats),
2N/A tx_ring_stats_list, TX_RING_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_LINK_TOTAL, dlstat_link_total_stats,
2N/A i_dlstat_total_match, i_dlstat_total_stat_entry_diff,
2N/A offsetof(total_stat_entry_t, tse_stats),
2N/A total_stats_list, TOTAL_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_PHYS_TOTAL, dlstat_phys_total_stats,
2N/A i_dlstat_total_match, i_dlstat_total_stat_entry_diff,
2N/A offsetof(total_stat_entry_t, tse_stats),
2N/A total_stats_list, TOTAL_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_AGGR_PORT, dlstat_aggr_port_stats,
2N/A i_dlstat_aggr_port_match, i_dlstat_aggr_port_stat_entry_diff,
2N/A offsetof(aggr_port_stat_entry_t, ape_stats),
2N/A aggr_port_stats_list, AGGR_PORT_STAT_SIZE},
2N/A
2N/A/*
2N/A * We don't support -i <interval> for all link stat query. Several table fields
2N/A * are left uninitialized thus.
2N/A */
2N/A{ DLADM_STAT_LINK, dlstat_link_stats,
2N/A NULL, NULL,
2N/A 0,
2N/A link_stats_list, LINK_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_VDP, dlstat_vdp_stats,
2N/A i_dlstat_vdp_match, i_dlstat_vdp_stat_entry_diff,
2N/A offsetof(vdp_stat_entry_t, vse_stats),
2N/A vdp_stats_list, VDP_STAT_SIZE},
2N/A
2N/A{ DLADM_STAT_ECP, dlstat_ecp_stats,
2N/A i_dlstat_ecp_match, i_dlstat_ecp_stat_entry_diff,
2N/A offsetof(ecp_stat_entry_t, ese_stats),
2N/A ecp_stats_list, ECP_STAT_SIZE}
2N/A};
2N/A
2N/A/* Internal functions */
2N/Astatic void *
2N/Adlstat_diff_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
2N/A{
2N/A return (dladm_stat_table[stattype].ds_diffstat(arg1, arg2));
2N/A}
2N/A
2N/Astatic boolean_t
2N/Adlstat_match_stats(void *arg1, void *arg2, dladm_stat_type_t stattype)
2N/A{
2N/A return (dladm_stat_table[stattype].ds_matchstat(arg1, arg2));
2N/A}
2N/A
2N/A/* Diff between two stats */
2N/Astatic void
2N/Ai_dlstat_diff_stats(void *diff, void *op1, void *op2,
2N/A stat_info_t stats_list[], uint_t size)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < size; i++) {
2N/A uint64_t *op1_val = (void *)
2N/A ((uchar_t *)op1 + stats_list[i].si_offset);
2N/A uint64_t *op2_val = (void *)
2N/A ((uchar_t *)op2 + stats_list[i].si_offset);
2N/A uint64_t *diff_val = (void *)
2N/A ((uchar_t *)diff + stats_list[i].si_offset);
2N/A
2N/A *diff_val = DIFF_STAT(*op1_val, *op2_val);
2N/A }
2N/A}
2N/A
2N/A/*
2N/A * Perform diff = s1 - s2, where diff, s1, s2 are structure objects of same
2N/A * datatype. slist is list of offsets of the fields within the structure.
2N/A */
2N/A#define DLSTAT_DIFF_STAT(s1, s2, diff, f, slist, sz) { \
2N/A if (s2 == NULL) { \
2N/A bcopy(&s1->f, &diff->f, sizeof (s1->f)); \
2N/A } else { \
2N/A i_dlstat_diff_stats(&diff->f, &s1->f, \
2N/A &s2->f, slist, sz); \
2N/A } \
2N/A}
2N/A
2N/A/* Sum two stats */
2N/Astatic void
2N/Ai_dlstat_sum_stats(void *sum, void *op1, void *op2,
2N/A stat_info_t stats_list[], uint_t size)
2N/A{
2N/A int i;
2N/A
2N/A for (i = 0; i < size; i++) {
2N/A uint64_t *op1_val = (void *)
2N/A ((uchar_t *)op1 + stats_list[i].si_offset);
2N/A uint64_t *op2_val = (void *)
2N/A ((uchar_t *)op2 + stats_list[i].si_offset);
2N/A uint64_t *sum_val = (void *)
2N/A ((uchar_t *)sum + stats_list[i].si_offset);
2N/A
2N/A *sum_val = *op1_val + *op2_val;
2N/A }
2N/A}
2N/A
2N/A/* Given a linkid, resolve kstat name for querying link level stats */
2N/Astatic boolean_t
2N/Ai_dlstat_resolve_kstat_link_names(dladm_handle_t dh, datalink_id_t linkid,
2N/A char *modname)
2N/A{
2N/A datalink_id_t ulinkid;
2N/A datalink_class_t class;
2N/A dladm_phys_attr_t dpa;
2N/A
2N/A /* Query underlying datalink's (if applicable) link id */
2N/A dladm_query_primary_linkid(dh, linkid, &ulinkid);
2N/A
2N/A /* Query underlying datalink's name (could be vanity name) */
2N/A if (dladm_datalink_id2info(dh, ulinkid, NULL, &class, NULL, modname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A if (class == DATALINK_CLASS_ETHERSTUB) {
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL,
2N/A modname, DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (B_FALSE);
2N/A }
2N/A } else if (class != DATALINK_CLASS_AGGR) {
2N/A /* Resolve vanity name to phys name */
2N/A if (dladm_phys_info(dh, ulinkid, &dpa, DLADM_OPT_ACTIVE) !=
2N/A DLADM_STATUS_OK) {
2N/A return (B_FALSE);
2N/A }
2N/A (void) strlcpy(modname, dpa.dp_dev, MAXLINKNAMELEN);
2N/A }
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* Given a linkid, resolve kstat name for querying phys level stats */
2N/Astatic boolean_t
2N/Ai_dlstat_resolve_kstat_phys_names(dladm_handle_t dh, datalink_id_t linkid,
2N/A char *modname)
2N/A{
2N/A datalink_class_t class;
2N/A dladm_phys_attr_t dpa;
2N/A
2N/A /*
2N/A * kstats corresponding to physical device rings continue to use
2N/A * device names even if the link is renamed using dladm rename-link.
2N/A * Thus, given a linkid, we lookup the physical device name.
2N/A * However, if an aggr is renamed, kstats corresponding to its
2N/A * pseudo rings are renamed as well.
2N/A */
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, modname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (B_FALSE);
2N/A }
2N/A
2N/A if (class != DATALINK_CLASS_AGGR) {
2N/A /* Resolve vanity name to phys name */
2N/A if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
2N/A DLADM_STATUS_OK) {
2N/A return (B_FALSE);
2N/A }
2N/A (void) strlcpy(modname, dpa.dp_dev, MAXLINKNAMELEN);
2N/A }
2N/A return (B_TRUE);
2N/A}
2N/A
2N/A/* Look up kstat value */
2N/Astatic void
2N/Ai_dlstat_get_stats(kstat_ctl_t *kcp, kstat_t *ksp, void *stats,
2N/A stat_info_t stats_list[], uint_t size)
2N/A{
2N/A int i;
2N/A
2N/A if (kstat_read(kcp, ksp, NULL) == -1)
2N/A return;
2N/A
2N/A for (i = 0; i < size; i++) {
2N/A uint64_t *val = (void *)
2N/A ((uchar_t *)stats + stats_list[i].si_offset);
2N/A
2N/A if (dladm_kstat_value(ksp, stats_list[i].si_name,
2N/A KSTAT_DATA_UINT64, val) < 0)
2N/A return;
2N/A }
2N/A}
2N/A
2N/A/* Append linked list list1 to linked list list2 and return resulting list */
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_join_lists(dladm_stat_chain_t *list1, dladm_stat_chain_t *list2)
2N/A{
2N/A dladm_stat_chain_t *curr;
2N/A
2N/A if (list1 == NULL)
2N/A return (list2);
2N/A
2N/A /* list1 has at least one element, find last element in list1 */
2N/A curr = list1;
2N/A while (curr->dc_next != NULL)
2N/A curr = curr->dc_next;
2N/A
2N/A curr->dc_next = list2;
2N/A return (list1);
2N/A}
2N/A
2N/Atypedef enum {
2N/A DLSTAT_RX_RING_IDLIST,
2N/A DLSTAT_TX_RING_IDLIST,
2N/A DLSTAT_RX_HWLANE_IDLIST,
2N/A DLSTAT_TX_HWLANE_IDLIST
2N/A} dlstat_idlist_type_t;
2N/A
2N/Avoid
2N/Adladm_sort_index_list(uint_t *idlist, uint_t size)
2N/A{
2N/A int i, j;
2N/A
2N/A if (idlist == NULL)
2N/A return;
2N/A
2N/A for (j = 1; j < size; j++) {
2N/A int key = idlist[j];
2N/A for (i = j - 1; (i >= 0) && (idlist[i] > key); i--)
2N/A idlist[i + 1] = idlist[i];
2N/A idlist[i + 1] = key;
2N/A }
2N/A}
2N/A
2N/A/* Support for legacy drivers */
2N/Avoid
2N/Ai_query_legacy_stats(const char *linkname, pktsum_t *stats)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A
2N/A bzero(stats, sizeof (*stats));
2N/A
2N/A if ((kcp = kstat_open()) == NULL)
2N/A return;
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, DLSTAT_MAC_LINK_STAT, 0, linkname, NULL);
2N/A
2N/A if (ksp != NULL)
2N/A dladm_get_stats(kcp, ksp, stats);
2N/A
2N/A (void) kstat_close(kcp);
2N/A}
2N/A
2N/Avoid *
2N/Ai_dlstat_legacy_rx_lane_stats(const char *linkname)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A pktsum_t stats;
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A
2N/A bzero(&stats, sizeof (pktsum_t));
2N/A
2N/A /* Query for dls stats */
2N/A i_query_legacy_stats(linkname, &stats);
2N/A
2N/A /* Convert to desired data type */
2N/A rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (rx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
2N/A rx_lane_stat_entry->rle_id = L_SWLANE;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_ipackets = stats.ipackets;
2N/A rx_lane_stat_entry->rle_stats.rl_intrs = stats.ipackets;
2N/A rx_lane_stat_entry->rle_stats.rl_rbytes = stats.rbytes;
2N/A
2N/A /* Allocate memory for wrapper */
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(rx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A head->dc_statentry = rx_lane_stat_entry;
2N/A head->dc_next = NULL;
2N/Adone:
2N/A return (head);
2N/A}
2N/A
2N/Avoid *
2N/Ai_dlstat_legacy_tx_lane_stats(const char *linkname)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A pktsum_t stats;
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A bzero(&stats, sizeof (pktsum_t));
2N/A
2N/A /* Query for dls stats */
2N/A i_query_legacy_stats(linkname, &stats);
2N/A
2N/A /* Convert to desired data type */
2N/A tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (tx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
2N/A tx_lane_stat_entry->tle_id = L_SWLANE;
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_opackets = stats.opackets;
2N/A tx_lane_stat_entry->tle_stats.tl_obytes = stats.obytes;
2N/A
2N/A /* Allocate memory for wrapper */
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(tx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A head->dc_statentry = tx_lane_stat_entry;
2N/A head->dc_next = NULL;
2N/Adone:
2N/A return (head);
2N/A}
2N/A
2N/Astatic void
2N/Ai_dlstat_get_idlist(dladm_handle_t dh, datalink_id_t linkid,
2N/A dlstat_idlist_type_t idlist_type, uint_t **idlistp, uint_t *psize)
2N/A{
2N/A int i;
2N/A uint_t *idlist = NULL;
2N/A
2N/A *psize = 0;
2N/A
2N/A switch (idlist_type) {
2N/A case DLSTAT_RX_RING_IDLIST:
2N/A case DLSTAT_TX_RING_IDLIST: {
2N/A uint_t nrings;
2N/A
2N/A dladm_get_ring_counts(dh, linkid,
2N/A idlist_type == DLSTAT_RX_RING_IDLIST ? &nrings : NULL,
2N/A idlist_type == DLSTAT_TX_RING_IDLIST ? &nrings : NULL);
2N/A
2N/A *psize = nrings;
2N/A if ((idlist = calloc(1, nrings * sizeof (uint_t))) == NULL)
2N/A break;
2N/A for (i = 0; i < *psize; i++)
2N/A idlist[i] = i;
2N/A break;
2N/A }
2N/A case DLSTAT_RX_HWLANE_IDLIST:
2N/A case DLSTAT_TX_HWLANE_IDLIST: {
2N/A if (idlist_type == DLSTAT_RX_HWLANE_IDLIST) {
2N/A dladm_get_ring_index_list(dh, linkid,
2N/A &idlist, psize, NULL, NULL);
2N/A } else if (idlist_type == DLSTAT_TX_HWLANE_IDLIST) {
2N/A dladm_get_ring_index_list(dh, linkid,
2N/A NULL, NULL, &idlist, psize);
2N/A }
2N/A dladm_sort_index_list(idlist, *psize);
2N/A break;
2N/A }
2N/A default:
2N/A *psize = 0;
2N/A }
2N/A *idlistp = idlist;
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_query_stats(const char *modname, const char *prefix,
2N/A uint_t *idlist, uint_t idlist_size,
2N/A void * (*fn)(kstat_ctl_t *, kstat_t *, int))
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A char statname[MAXLINKNAMELEN];
2N/A int i = 0;
2N/A dladm_stat_chain_t *head = NULL, *prev = NULL;
2N/A dladm_stat_chain_t *curr;
2N/A
2N/A if ((kcp = kstat_open()) == NULL) {
2N/A warn("kstat_open operation failed");
2N/A return (NULL);
2N/A }
2N/A
2N/A for (i = 0; i < idlist_size; i++) {
2N/A uint_t index = idlist[i];
2N/A
2N/A (void) snprintf(statname, sizeof (statname), "%s%d", prefix,
2N/A index);
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, modname, -1, statname, NULL);
2N/A if (ksp == NULL)
2N/A continue;
2N/A
2N/A curr = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (curr == NULL)
2N/A break;
2N/A
2N/A curr->dc_statentry = fn(kcp, ksp, index);
2N/A if (curr->dc_statentry == NULL) {
2N/A free(curr);
2N/A break;
2N/A }
2N/A
2N/A (void) strlcpy(curr->dc_statheader, statname,
2N/A sizeof (curr->dc_statheader));
2N/A curr->dc_next = NULL;
2N/A
2N/A if (head == NULL) /* First node */
2N/A head = curr;
2N/A else
2N/A prev->dc_next = curr;
2N/A
2N/A prev = curr;
2N/A }
2N/Adone:
2N/A (void) kstat_close(kcp);
2N/A return (head);
2N/A}
2N/A
2N/Astatic link_stat_entry_t *
2N/Ai_dlstat_link_stats(const char *modname)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A
2N/A if ((kcp = kstat_open()) == NULL)
2N/A return (NULL);
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, modname, -1, DLSTAT_MAC_LINK_STAT, NULL);
2N/A if (ksp == NULL)
2N/A goto done;
2N/A
2N/A link_stat_entry = calloc(1, sizeof (link_stat_entry_t));
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &link_stat_entry->lse_stats,
2N/A link_stats_list, LINK_STAT_SIZE);
2N/Adone:
2N/A (void) kstat_close(kcp);
2N/A return (link_stat_entry);
2N/A}
2N/A
2N/A/* Rx lane statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_rx_lane_match(void *arg1, void *arg2)
2N/A{
2N/A rx_lane_stat_entry_t *s1 = arg1;
2N/A rx_lane_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->rle_index == s2->rle_index &&
2N/A s1->rle_id == s2->rle_id);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_rx_lane_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A rx_lane_stat_entry_t *s1 = arg1;
2N/A rx_lane_stat_entry_t *s2 = arg2;
2N/A rx_lane_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A diff_entry->rle_index = s1->rle_index;
2N/A diff_entry->rle_id = s1->rle_id;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, rle_stats, rx_lane_stats_list,
2N/A RX_LANE_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_rx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
2N/A{
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A
2N/A rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (rx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry->rle_index = i;
2N/A rx_lane_stat_entry->rle_id = L_HWLANE;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &rx_lane_stat_entry->rle_stats,
2N/A rx_hwlane_stats_list, RX_HWLANE_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (rx_lane_stat_entry);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_rx_local_stats(const char *modname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *local_stats = NULL;
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(modname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (rx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
2N/A rx_lane_stat_entry->rle_id = L_LOCAL;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_ipackets =
2N/A link_stat_entry->lse_stats.ls_rxlocal;
2N/A rx_lane_stat_entry->rle_stats.rl_rbytes =
2N/A link_stat_entry->lse_stats.ls_rxlocalbytes;
2N/A
2N/A local_stats = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (local_stats == NULL) {
2N/A free(rx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(local_stats->dc_statheader, "mac_rx_local",
2N/A sizeof (local_stats->dc_statheader));
2N/A local_stats->dc_statentry = rx_lane_stat_entry;
2N/A local_stats->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (local_stats);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_rx_bcast_stats(const char *modname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *head = NULL;
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(modname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (rx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
2N/A rx_lane_stat_entry->rle_id = L_BCAST;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_ipackets =
2N/A link_stat_entry->lse_stats.ls_brdcstrcv +
2N/A link_stat_entry->lse_stats.ls_multircv;
2N/A rx_lane_stat_entry->rle_stats.rl_rbytes =
2N/A link_stat_entry->lse_stats.ls_brdcstrcvbytes +
2N/A link_stat_entry->lse_stats.ls_multircvbytes;
2N/A
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(rx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(head->dc_statheader, "mac_rx_other",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_statentry = rx_lane_stat_entry;
2N/A head->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (head);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_rx_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A uint_t *rx_hwlane_idlist;
2N/A uint_t rx_hwlane_idlist_size;
2N/A char ulinkname[MAXLINKNAMELEN];
2N/A dladm_stat_chain_t *chain;
2N/A
2N/A /*
2N/A * For every hardware ring dedicated to a datalink, the datalink is said
2N/A * to have a hardware lane (hwlane). During the lifetime of a device
2N/A * instance (from device attach till detach), a device ring could be
2N/A * used by different datalinks (primary, vnics, vlans etc.). However,
2N/A * hwlane kstats are created only once - when the rings are initialized
2N/A * by the device attach. Thus, these kstats are *not* recreated even if
2N/A * a ring is moved from one datalink to another.
2N/A *
2N/A * These "persistent" (for the lifetime of device) kstats are named
2N/A * using the name of the underlying datalink and the id corresponding
2N/A * to the hardware ring. Thus, hwlane stat query on a datalink needs to
2N/A * dig in this information before it can issue a kstat query.
2N/A */
2N/A
2N/A /* Find name of the underlying link */
2N/A if (!i_dlstat_resolve_kstat_link_names(dh, linkid, ulinkname))
2N/A return (NULL);
2N/A
2N/A /* Query for ids of dedicated rings for this datalink */
2N/A i_dlstat_get_idlist(dh, linkid, DLSTAT_RX_HWLANE_IDLIST,
2N/A &rx_hwlane_idlist, &rx_hwlane_idlist_size);
2N/A
2N/A chain = i_dlstat_query_stats(ulinkname, DLSTAT_MAC_RX_HWLANE,
2N/A rx_hwlane_idlist, rx_hwlane_idlist_size,
2N/A i_dlstat_rx_hwlane_retrieve_stat);
2N/A
2N/A free(rx_hwlane_idlist);
2N/A return (chain);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_rx_swlane_stats(const char *linkname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *swlane_stats = NULL;
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(linkname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (rx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry->rle_index = DLSTAT_INVALID_ENTRY;
2N/A rx_lane_stat_entry->rle_id = L_SWLANE;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_ipackets =
2N/A link_stat_entry->lse_stats.ls_intrs +
2N/A link_stat_entry->lse_stats.ls_polls;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_rbytes =
2N/A link_stat_entry->lse_stats.ls_intrbytes +
2N/A link_stat_entry->lse_stats.ls_pollbytes;
2N/A
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_intrs =
2N/A link_stat_entry->lse_stats.ls_intrs;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_intrbytes =
2N/A link_stat_entry->lse_stats.ls_intrbytes;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_pollbytes =
2N/A link_stat_entry->lse_stats.ls_pollbytes;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_polls =
2N/A link_stat_entry->lse_stats.ls_polls;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_idropbytes =
2N/A link_stat_entry->lse_stats.ls_idropbytes;
2N/A
2N/A rx_lane_stat_entry->rle_stats.rl_idrops =
2N/A link_stat_entry->lse_stats.ls_idrops;
2N/A
2N/A
2N/A swlane_stats = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (swlane_stats == NULL) {
2N/A free(rx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A swlane_stats->dc_statentry = rx_lane_stat_entry;
2N/A swlane_stats->dc_next = NULL;
2N/A if (swlane_stats != NULL) {
2N/A (void) strlcpy(swlane_stats->dc_statheader, "mac_rx_swlane",
2N/A sizeof (swlane_stats->dc_statheader));
2N/A }
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (swlane_stats);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_rx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A dladm_stat_chain_t *local_stats = NULL;
2N/A dladm_stat_chain_t *bcast_stats = NULL;
2N/A dladm_stat_chain_t *lane_stats = NULL;
2N/A boolean_t is_legacy_driver;
2N/A char linkname[MAXLINKNAMELEN];
2N/A
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A /* Check if it is legacy driver */
2N/A if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
2N/A "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A if (is_legacy_driver) {
2N/A head = i_dlstat_legacy_rx_lane_stats(linkname);
2N/A goto done;
2N/A }
2N/A
2N/A local_stats = i_dlstat_rx_local_stats(linkname);
2N/A bcast_stats = i_dlstat_rx_bcast_stats(linkname);
2N/A lane_stats = i_dlstat_rx_hwlane_stats(dh, linkid);
2N/A if (lane_stats == NULL)
2N/A lane_stats = i_dlstat_rx_swlane_stats(linkname);
2N/A
2N/A head = i_dlstat_join_lists(local_stats, bcast_stats);
2N/A head = i_dlstat_join_lists(head, lane_stats);
2N/Adone:
2N/A return (head);
2N/A}
2N/A
2N/A/* Tx lane statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_tx_lane_match(void *arg1, void *arg2)
2N/A{
2N/A tx_lane_stat_entry_t *s1 = arg1;
2N/A tx_lane_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->tle_index == s2->tle_index &&
2N/A s1->tle_id == s2->tle_id);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_tx_lane_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A tx_lane_stat_entry_t *s1 = arg1;
2N/A tx_lane_stat_entry_t *s2 = arg2;
2N/A tx_lane_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A diff_entry->tle_index = s1->tle_index;
2N/A diff_entry->tle_id = s1->tle_id;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, tle_stats, tx_lane_stats_list,
2N/A TX_LANE_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_tx_hwlane_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
2N/A{
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (tx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry->tle_index = i;
2N/A tx_lane_stat_entry->tle_id = L_HWLANE;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &tx_lane_stat_entry->tle_stats,
2N/A tx_lane_stats_list, TX_LANE_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (tx_lane_stat_entry);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_tx_local_stats(const char *modname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *local_stats = NULL;
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(modname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (tx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
2N/A tx_lane_stat_entry->tle_id = L_LOCAL;
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_opackets =
2N/A link_stat_entry->lse_stats.ls_txlocal;
2N/A tx_lane_stat_entry->tle_stats.tl_obytes =
2N/A link_stat_entry->lse_stats.ls_txlocalbytes;
2N/A
2N/A local_stats = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (local_stats == NULL) {
2N/A free(tx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(local_stats->dc_statheader, "mac_tx_local",
2N/A sizeof (local_stats->dc_statheader));
2N/A local_stats->dc_statentry = tx_lane_stat_entry;
2N/A local_stats->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (local_stats);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_tx_bcast_stats(const char *modname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *head = NULL;
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(modname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (tx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
2N/A tx_lane_stat_entry->tle_id = L_BCAST;
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_opackets =
2N/A link_stat_entry->lse_stats.ls_brdcstxmt +
2N/A link_stat_entry->lse_stats.ls_multixmt;
2N/A tx_lane_stat_entry->tle_stats.tl_obytes =
2N/A link_stat_entry->lse_stats.ls_brdcstxmtbytes +
2N/A link_stat_entry->lse_stats.ls_multixmtbytes;
2N/A
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(tx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(head->dc_statheader, "mac_tx_other",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_statentry = tx_lane_stat_entry;
2N/A head->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (head);
2N/A}
2N/A
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_tx_hwlane_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A uint_t *tx_hwlane_idlist;
2N/A uint_t tx_hwlane_idlist_size;
2N/A char ulinkname[MAXLINKNAMELEN];
2N/A dladm_stat_chain_t *chain;
2N/A
2N/A /*
2N/A * For every hardware ring dedicated to a datalink, the datalink is said
2N/A * to have a hardware lane (hwlane). During the lifetime of a device
2N/A * instance (from device attach till detach), a device ring could be
2N/A * used by different datalinks (primary, vnics, vlans etc.). However,
2N/A * hwlane kstats are created only once - when the rings are initialized
2N/A * by the device attach. Thus, these kstats are *not* recreated even if
2N/A * a ring is moved from one datalink to another.
2N/A *
2N/A * These "persistent" (for the lifetime of device) kstats are named
2N/A * using the name of the underlying datalink and the id corresponding
2N/A * to the hardware ring. Thus, hwlane stat query on a datalink needs to
2N/A * dig in this information before it can issue a kstat query.
2N/A */
2N/A
2N/A /* Find the name of the underlying link */
2N/A if (!i_dlstat_resolve_kstat_link_names(dh, linkid, ulinkname))
2N/A return (NULL);
2N/A
2N/A /* Query for ids of dedicated rings for this datalink */
2N/A i_dlstat_get_idlist(dh, linkid, DLSTAT_TX_HWLANE_IDLIST,
2N/A &tx_hwlane_idlist, &tx_hwlane_idlist_size);
2N/A
2N/A chain = i_dlstat_query_stats(ulinkname, DLSTAT_MAC_TX_HWLANE,
2N/A tx_hwlane_idlist, tx_hwlane_idlist_size,
2N/A i_dlstat_tx_hwlane_retrieve_stat);
2N/A
2N/A free(tx_hwlane_idlist);
2N/A return (chain);
2N/A}
2N/A
2N/A/*ARGSUSED*/
2N/Astatic dladm_stat_chain_t *
2N/Ai_dlstat_tx_swlane_stats(const char *linkname)
2N/A{
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A dladm_stat_chain_t *swlane_stats = NULL;
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(linkname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (tx_lane_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_lane_stat_entry->tle_index = DLSTAT_INVALID_ENTRY;
2N/A tx_lane_stat_entry->tle_id = L_SWLANE;
2N/A
2N/A /*
2N/A * Swlane stat counts on-wire unicast traffic only. Thus,
2N/A * swlane = total tx traffic - (local + multicast + broadcast).
2N/A */
2N/A tx_lane_stat_entry->tle_stats.tl_opackets =
2N/A link_stat_entry->lse_stats.ls_opackets -
2N/A (link_stat_entry->lse_stats.ls_txlocal +
2N/A link_stat_entry->lse_stats.ls_brdcstxmt +
2N/A link_stat_entry->lse_stats.ls_multixmt);
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_obytes =
2N/A link_stat_entry->lse_stats.ls_obytes -
2N/A (link_stat_entry->lse_stats.ls_txlocalbytes +
2N/A link_stat_entry->lse_stats.ls_brdcstxmtbytes +
2N/A link_stat_entry->lse_stats.ls_multixmtbytes);
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_odrops =
2N/A link_stat_entry->lse_stats.ls_odrops;
2N/A
2N/A
2N/A tx_lane_stat_entry->tle_stats.tl_odropbytes =
2N/A link_stat_entry->lse_stats.ls_odropbytes;
2N/A
2N/A
2N/A swlane_stats = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (swlane_stats == NULL) {
2N/A free(tx_lane_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(swlane_stats->dc_statheader, "mac_tx_swlane",
2N/A sizeof (swlane_stats->dc_statheader));
2N/A swlane_stats->dc_statentry = tx_lane_stat_entry;
2N/A swlane_stats->dc_next = NULL;
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (swlane_stats);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_tx_lane_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A dladm_stat_chain_t *local_stats = NULL;
2N/A dladm_stat_chain_t *bcast_stats = NULL;
2N/A dladm_stat_chain_t *lane_stats = NULL;
2N/A boolean_t is_legacy_driver;
2N/A char linkname[MAXLINKNAMELEN];
2N/A
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A /* Check if it is legacy driver */
2N/A if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
2N/A "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A if (is_legacy_driver) {
2N/A head = i_dlstat_legacy_tx_lane_stats(linkname);
2N/A goto done;
2N/A }
2N/A
2N/A local_stats = i_dlstat_tx_local_stats(linkname);
2N/A bcast_stats = i_dlstat_tx_bcast_stats(linkname);
2N/A lane_stats = i_dlstat_tx_hwlane_stats(dh, linkid);
2N/A if (lane_stats == NULL)
2N/A lane_stats = i_dlstat_tx_swlane_stats(linkname);
2N/A
2N/A head = i_dlstat_join_lists(local_stats, bcast_stats);
2N/A head = i_dlstat_join_lists(head, lane_stats);
2N/Adone:
2N/A return (head);
2N/A}
2N/A
2N/A/* Rx lane total statistic specific functions */
2N/Avoid *
2N/Adlstat_rx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *total_head = NULL;
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A rx_lane_stat_entry_t *total_stats;
2N/A char linkname[MAXLINKNAMELEN];
2N/A boolean_t is_legacy_driver;
2N/A
2N/A /* Query per mcip kstats (don't need to look at the underlying link) */
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
2N/A "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A if (is_legacy_driver) {
2N/A total_head = i_dlstat_legacy_rx_lane_stats(linkname);
2N/A goto done;
2N/A }
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(linkname);
2N/A
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A total_stats = calloc(1, sizeof (rx_lane_stat_entry_t));
2N/A if (total_stats == NULL)
2N/A goto done;
2N/A
2N/A total_stats->rle_index = DLSTAT_INVALID_ENTRY;
2N/A total_stats->rle_id = DLSTAT_INVALID_ENTRY;
2N/A
2N/A total_stats->rle_stats.rl_ipackets =
2N/A link_stat_entry->lse_stats.ls_ipackets;
2N/A total_stats->rle_stats.rl_rbytes =
2N/A link_stat_entry->lse_stats.ls_rbytes;
2N/A
2N/A total_stats->rle_stats.rl_intrs =
2N/A link_stat_entry->lse_stats.ls_intrs;
2N/A total_stats->rle_stats.rl_intrbytes =
2N/A link_stat_entry->lse_stats.ls_intrbytes;
2N/A
2N/A total_stats->rle_stats.rl_polls =
2N/A link_stat_entry->lse_stats.ls_polls;
2N/A total_stats->rle_stats.rl_pollbytes =
2N/A link_stat_entry->lse_stats.ls_pollbytes;
2N/A
2N/A total_stats->rle_stats.rl_idrops =
2N/A link_stat_entry->lse_stats.ls_idrops;
2N/A total_stats->rle_stats.rl_idropbytes =
2N/A link_stat_entry->lse_stats.ls_idropbytes;
2N/A
2N/A total_head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (total_head == NULL) {
2N/A free(total_stats);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(total_head->dc_statheader, "mac_rx_lane_total",
2N/A sizeof (total_head->dc_statheader));
2N/A total_head->dc_statentry = total_stats;
2N/A total_head->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (total_head);
2N/A}
2N/A
2N/A/* Tx lane total statistic specific functions */
2N/Avoid *
2N/Adlstat_tx_lane_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *total_head = NULL;
2N/A link_stat_entry_t *link_stat_entry = NULL;
2N/A tx_lane_stat_entry_t *total_stats;
2N/A char linkname[MAXLINKNAMELEN];
2N/A boolean_t is_legacy_driver;
2N/A
2N/A /* Query per mcip kstats (don't need to look at the underlying link) */
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if (dladm_linkprop_is_set(dh, linkid, DLADM_PROP_VAL_CURRENT,
2N/A "_softmac", &is_legacy_driver) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A if (is_legacy_driver) {
2N/A total_head = i_dlstat_legacy_tx_lane_stats(linkname);
2N/A goto done;
2N/A }
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(linkname);
2N/A
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A total_stats = calloc(1, sizeof (tx_lane_stat_entry_t));
2N/A if (total_stats == NULL)
2N/A goto done;
2N/A
2N/A total_stats->tle_index = DLSTAT_INVALID_ENTRY;
2N/A total_stats->tle_id = DLSTAT_INVALID_ENTRY;
2N/A
2N/A total_stats->tle_stats.tl_opackets =
2N/A link_stat_entry->lse_stats.ls_opackets;
2N/A total_stats->tle_stats.tl_obytes =
2N/A link_stat_entry->lse_stats.ls_obytes;
2N/A
2N/A total_stats->tle_stats.tl_odrops =
2N/A link_stat_entry->lse_stats.ls_odrops;
2N/A total_stats->tle_stats.tl_odropbytes =
2N/A link_stat_entry->lse_stats.ls_odropbytes;
2N/A
2N/A total_head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (total_head == NULL) {
2N/A free(total_stats);
2N/A goto done;
2N/A }
2N/A
2N/A (void) strlcpy(total_head->dc_statheader, "mac_tx_lane_total",
2N/A sizeof (total_head->dc_statheader));
2N/A total_head->dc_statentry = total_stats;
2N/A total_head->dc_next = NULL;
2N/A
2N/Adone:
2N/A free(link_stat_entry);
2N/A return (total_head);
2N/A}
2N/A
2N/A/* Rx ring statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_rx_ring_match(void *arg1, void *arg2)
2N/A{
2N/A rx_lane_stat_entry_t *s1 = arg1;
2N/A rx_lane_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->rle_index == s2->rle_index);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_rx_ring_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A ring_stat_entry_t *s1 = arg1;
2N/A ring_stat_entry_t *s2 = arg2;
2N/A ring_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (ring_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A diff_entry->re_index = s1->re_index;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, rx_ring_stats_list,
2N/A RX_RING_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_rx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
2N/A{
2N/A ring_stat_entry_t *rx_ring_stat_entry;
2N/A
2N/A rx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
2N/A if (rx_ring_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_ring_stat_entry->re_index = i;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &rx_ring_stat_entry->re_stats,
2N/A rx_ring_stats_list, RX_RING_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (rx_ring_stat_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_rx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A uint_t *rx_ring_idlist;
2N/A uint_t rx_ring_idlist_size;
2N/A char modname[MAXLINKNAMELEN];
2N/A void *chain;
2N/A
2N/A if (!i_dlstat_resolve_kstat_phys_names(dh, linkid, modname))
2N/A return (NULL);
2N/A
2N/A i_dlstat_get_idlist(dh, linkid, DLSTAT_RX_RING_IDLIST,
2N/A &rx_ring_idlist, &rx_ring_idlist_size);
2N/A
2N/A chain = i_dlstat_query_stats(modname, DLSTAT_MAC_RX_RING,
2N/A rx_ring_idlist, rx_ring_idlist_size,
2N/A i_dlstat_rx_ring_retrieve_stat);
2N/A
2N/A free(rx_ring_idlist);
2N/A return (chain);
2N/A}
2N/A
2N/A/* Tx ring statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_tx_ring_match(void *arg1, void *arg2)
2N/A{
2N/A tx_lane_stat_entry_t *s1 = arg1;
2N/A tx_lane_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->tle_index == s2->tle_index);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_tx_ring_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A ring_stat_entry_t *s1 = arg1;
2N/A ring_stat_entry_t *s2 = arg2;
2N/A ring_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (ring_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A diff_entry->re_index = s1->re_index;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, re_stats, tx_ring_stats_list,
2N/A TX_RING_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_tx_ring_retrieve_stat(kstat_ctl_t *kcp, kstat_t *ksp, int i)
2N/A{
2N/A ring_stat_entry_t *tx_ring_stat_entry;
2N/A
2N/A tx_ring_stat_entry = calloc(1, sizeof (ring_stat_entry_t));
2N/A if (tx_ring_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A tx_ring_stat_entry->re_index = i;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &tx_ring_stat_entry->re_stats,
2N/A tx_ring_stats_list, TX_RING_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (tx_ring_stat_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_tx_ring_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A uint_t *tx_ring_idlist;
2N/A uint_t tx_ring_idlist_size;
2N/A char modname[MAXLINKNAMELEN];
2N/A void *chain;
2N/A
2N/A if (!i_dlstat_resolve_kstat_phys_names(dh, linkid, modname))
2N/A return (NULL);
2N/A
2N/A i_dlstat_get_idlist(dh, linkid, DLSTAT_TX_RING_IDLIST,
2N/A &tx_ring_idlist, &tx_ring_idlist_size);
2N/A
2N/A chain = i_dlstat_query_stats(modname, DLSTAT_MAC_TX_RING,
2N/A tx_ring_idlist, tx_ring_idlist_size,
2N/A i_dlstat_tx_ring_retrieve_stat);
2N/A
2N/A free(tx_ring_idlist);
2N/A return (chain);
2N/A}
2N/A
2N/Astatic total_stat_entry_t *
2N/Ai_dlstat_ring_stats(const char *linkname)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A total_stat_entry_t *total_stat_entry = NULL;
2N/A char module[DLPI_LINKNAME_MAX];
2N/A uint_t instance;
2N/A
2N/A if (!dlparse_drvppa(linkname, module, sizeof (module), &instance))
2N/A return (NULL);
2N/A
2N/A if ((kcp = kstat_open()) == NULL) {
2N/A warn("kstat open operation failed");
2N/A return (NULL);
2N/A }
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, module, instance, DLSTAT_MAC_PHYS_STAT,
2N/A NULL);
2N/A if (ksp == NULL)
2N/A goto done;
2N/A
2N/A total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2N/A if (total_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &total_stat_entry->tse_stats,
2N/A total_stats_list, TOTAL_STAT_SIZE);
2N/Adone:
2N/A (void) kstat_close(kcp);
2N/A return (total_stat_entry);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid,
2N/A dladm_stat_type_t stattype)
2N/A{
2N/A dladm_stat_chain_t *stat_head = NULL;
2N/A total_stat_entry_t *total_stats = NULL;
2N/A ring_stat_entry_t *ring_stats = NULL;
2N/A dladm_phys_attr_t dpa;
2N/A char linkname[MAXLINKNAMELEN];
2N/A char *modname;
2N/A datalink_class_t class;
2N/A char *statheader;
2N/A
2N/A /*
2N/A * kstats corresponding to physical device rings continue to use
2N/A * device names even if the link is renamed using dladm rename-link.
2N/A * Thus, given a linkid, we lookup the physical device name.
2N/A * However, if an aggr is renamed, kstats corresponding to its
2N/A * pseudo rings are renamed as well.
2N/A */
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if (class != DATALINK_CLASS_AGGR) {
2N/A if (dladm_phys_info(dh, linkid, &dpa, DLADM_OPT_ACTIVE) !=
2N/A DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A modname = dpa.dp_dev;
2N/A } else
2N/A modname = linkname;
2N/A
2N/A total_stats = i_dlstat_ring_stats(modname);
2N/A if (total_stats == NULL)
2N/A return (NULL);
2N/A
2N/A ring_stats = calloc(1, sizeof (ring_stat_entry_t));
2N/A if (ring_stats == NULL)
2N/A goto done;
2N/A
2N/A ring_stats->re_index = DLSTAT_INVALID_ENTRY;
2N/A
2N/A if (stattype == DLADM_STAT_RX_RING_TOTAL) {
2N/A ring_stats->re_stats.r_packets =
2N/A total_stats->tse_stats.ts_ipackets;
2N/A ring_stats->re_stats.r_bytes = total_stats->tse_stats.ts_rbytes;
2N/A } else if (stattype == DLADM_STAT_TX_RING_TOTAL) {
2N/A ring_stats->re_stats.r_packets =
2N/A total_stats->tse_stats.ts_opackets;
2N/A ring_stats->re_stats.r_bytes = total_stats->tse_stats.ts_obytes;
2N/A }
2N/A
2N/A stat_head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (stat_head == NULL)
2N/A goto done;
2N/A
2N/A stat_head->dc_statentry = ring_stats;
2N/A stat_head->dc_next = NULL;
2N/A if (ring_stats != NULL) {
2N/A statheader = (stattype == DLADM_STAT_RX_RING_TOTAL) ?
2N/A "mac_rx_ring_total" : "mac_tx_ring_total";
2N/A
2N/A (void) strlcpy(stat_head->dc_statheader, statheader,
2N/A sizeof (stat_head->dc_statheader));
2N/A }
2N/A
2N/Adone:
2N/A free(total_stats);
2N/A if (stat_head == NULL)
2N/A free(ring_stats);
2N/A return (stat_head);
2N/A}
2N/A
2N/A/* Rx ring total statistic specific functions */
2N/Avoid *
2N/Adlstat_rx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A return (i_dlstat_ring_total_stats(dh, linkid,
2N/A DLADM_STAT_RX_RING_TOTAL));
2N/A}
2N/A
2N/A/* Tx ring total statistic specific functions */
2N/Avoid *
2N/Adlstat_tx_ring_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A return (i_dlstat_ring_total_stats(dh, linkid,
2N/A DLADM_STAT_TX_RING_TOTAL));
2N/A}
2N/A
2N/A/* Summary statistic specific functions */
2N/A/*ARGSUSED*/
2N/Astatic boolean_t
2N/Ai_dlstat_total_match(void *arg1, void *arg2)
2N/A{ /* Always single entry for total */
2N/A return (B_TRUE);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_total_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A total_stat_entry_t *s1 = arg1;
2N/A total_stat_entry_t *s2 = arg2;
2N/A total_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (total_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, tse_stats, total_stats_list,
2N/A TOTAL_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_link_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A dladm_stat_chain_t *rx_total = NULL;
2N/A dladm_stat_chain_t *tx_total = NULL;
2N/A total_stat_entry_t *total_stat_entry;
2N/A rx_lane_stat_entry_t *rx_lane_stat_entry;
2N/A tx_lane_stat_entry_t *tx_lane_stat_entry;
2N/A
2N/A /* Get total rx lane stats */
2N/A rx_total = dlstat_rx_lane_total_stats(dh, linkid);
2N/A if (rx_total == NULL)
2N/A goto done;
2N/A
2N/A /* Get total tx lane stats */
2N/A tx_total = dlstat_tx_lane_total_stats(dh, linkid);
2N/A if (tx_total == NULL)
2N/A goto done;
2N/A
2N/A /* Build total stat */
2N/A total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2N/A if (total_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_lane_stat_entry = rx_total->dc_statentry;
2N/A tx_lane_stat_entry = tx_total->dc_statentry;
2N/A
2N/A /* Extract total rx ipackets, rbytes */
2N/A total_stat_entry->tse_stats.ts_ipackets =
2N/A rx_lane_stat_entry->rle_stats.rl_ipackets;
2N/A total_stat_entry->tse_stats.ts_rbytes =
2N/A rx_lane_stat_entry->rle_stats.rl_rbytes;
2N/A
2N/A /* Extract total tx opackets, obytes */
2N/A total_stat_entry->tse_stats.ts_opackets =
2N/A tx_lane_stat_entry->tle_stats.tl_opackets;
2N/A total_stat_entry->tse_stats.ts_obytes =
2N/A tx_lane_stat_entry->tle_stats.tl_obytes;
2N/A
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(total_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A head->dc_statentry = total_stat_entry;
2N/A (void) strlcpy(head->dc_statheader, "mac_lane_total",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_next = NULL;
2N/A
2N/Adone:
2N/A dladm_link_stat_free(rx_total);
2N/A dladm_link_stat_free(tx_total);
2N/A return (head);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_phys_total_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_stat_chain_t *head = NULL;
2N/A dladm_stat_chain_t *rx_total = NULL;
2N/A dladm_stat_chain_t *tx_total = NULL;
2N/A total_stat_entry_t *total_stat_entry;
2N/A ring_stat_entry_t *rx_ring_stat_entry;
2N/A ring_stat_entry_t *tx_ring_stat_entry;
2N/A
2N/A /* Get total rx ring stats */
2N/A rx_total = dlstat_rx_ring_total_stats(dh, linkid);
2N/A if (rx_total == NULL)
2N/A goto done;
2N/A
2N/A /* Get total tx ring stats */
2N/A tx_total = dlstat_tx_ring_total_stats(dh, linkid);
2N/A if (tx_total == NULL)
2N/A goto done;
2N/A
2N/A /* Build total stat */
2N/A total_stat_entry = calloc(1, sizeof (total_stat_entry_t));
2N/A if (total_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A rx_ring_stat_entry = rx_total->dc_statentry;
2N/A tx_ring_stat_entry = tx_total->dc_statentry;
2N/A
2N/A /* Extract total rx ipackets, rbytes */
2N/A total_stat_entry->tse_stats.ts_ipackets =
2N/A rx_ring_stat_entry->re_stats.r_packets;
2N/A total_stat_entry->tse_stats.ts_rbytes =
2N/A rx_ring_stat_entry->re_stats.r_bytes;
2N/A
2N/A /* Extract total tx opackets, obytes */
2N/A total_stat_entry->tse_stats.ts_opackets =
2N/A tx_ring_stat_entry->re_stats.r_packets;
2N/A total_stat_entry->tse_stats.ts_obytes =
2N/A tx_ring_stat_entry->re_stats.r_bytes;
2N/A
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(total_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A head->dc_statentry = total_stat_entry;
2N/A (void) strlcpy(head->dc_statheader, "mac_phys_total",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_next = NULL;
2N/A
2N/Adone:
2N/A dladm_link_stat_free(rx_total);
2N/A dladm_link_stat_free(tx_total);
2N/A return (head);
2N/A}
2N/A
2N/A/* Aggr total statistic(summed across all component ports) specific functions */
2N/Avoid *
2N/Adlstat_aggr_total_stats(dladm_stat_chain_t *head)
2N/A{
2N/A dladm_stat_chain_t *curr;
2N/A dladm_stat_chain_t *total_head = NULL;
2N/A aggr_port_stat_entry_t *total_stats;
2N/A
2N/A total_stats = calloc(1, sizeof (aggr_port_stat_entry_t));
2N/A if (total_stats == NULL)
2N/A goto done;
2N/A
2N/A total_stats->ape_portlinkid = DATALINK_INVALID_LINKID;
2N/A
2N/A for (curr = head; curr != NULL; curr = curr->dc_next) {
2N/A aggr_port_stat_entry_t *curr_aggr_port_stats;
2N/A
2N/A curr_aggr_port_stats = curr->dc_statentry;
2N/A
2N/A i_dlstat_sum_stats(&total_stats->ape_stats,
2N/A &curr_aggr_port_stats->ape_stats, &total_stats->ape_stats,
2N/A aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2N/A }
2N/A
2N/A total_head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (total_head == NULL) {
2N/A free(total_stats);
2N/A goto done;
2N/A }
2N/A
2N/A total_head->dc_statentry = total_stats;
2N/A total_head->dc_next = NULL;
2N/A
2N/Adone:
2N/A return (total_head);
2N/A}
2N/A
2N/A/* Aggr port statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_aggr_port_match(void *arg1, void *arg2)
2N/A{
2N/A aggr_port_stat_entry_t *s1 = arg1;
2N/A aggr_port_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->ape_portlinkid == s2->ape_portlinkid);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_aggr_port_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A aggr_port_stat_entry_t *s1 = arg1;
2N/A aggr_port_stat_entry_t *s2 = arg2;
2N/A aggr_port_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A goto done;
2N/A
2N/A diff_entry->ape_portlinkid = s1->ape_portlinkid;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, ape_stats, aggr_port_stats_list,
2N/A AGGR_PORT_STAT_SIZE);
2N/A
2N/Adone:
2N/A return (diff_entry);
2N/A}
2N/A
2N/A/*
2N/A * Query dls stats for the aggr port. This results in query for stats into
2N/A * the corresponding device driver.
2N/A */
2N/Astatic aggr_port_stat_entry_t *
2N/Ai_dlstat_single_port_stats(const char *portname, datalink_id_t linkid)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A char module[DLPI_LINKNAME_MAX];
2N/A uint_t instance;
2N/A aggr_port_stat_entry_t *aggr_port_stat_entry = NULL;
2N/A
2N/A if (!dlparse_drvppa(portname, module, sizeof (module), &instance))
2N/A return (NULL);
2N/A
2N/A if ((kcp = kstat_open()) == NULL) {
2N/A warn("kstat open operation failed");
2N/A return (NULL);
2N/A }
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, module, instance, DLSTAT_MAC_PHYS_STAT,
2N/A NULL);
2N/A if (ksp == NULL)
2N/A goto done;
2N/A
2N/A aggr_port_stat_entry = calloc(1, sizeof (aggr_port_stat_entry_t));
2N/A if (aggr_port_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A /* Save port's linkid */
2N/A aggr_port_stat_entry->ape_portlinkid = linkid;
2N/A
2N/A i_dlstat_get_stats(kcp, ksp, &aggr_port_stat_entry->ape_stats,
2N/A aggr_port_stats_list, AGGR_PORT_STAT_SIZE);
2N/Adone:
2N/A (void) kstat_close(kcp);
2N/A return (aggr_port_stat_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_aggr_port_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A dladm_aggr_grp_attr_t ginfo;
2N/A int i;
2N/A dladm_aggr_port_attr_t *portp;
2N/A dladm_phys_attr_t dpa;
2N/A aggr_port_stat_entry_t *aggr_port_stat_entry;
2N/A dladm_stat_chain_t *head = NULL, *prev = NULL, *curr;
2N/A dladm_stat_chain_t *total_stats;
2N/A
2N/A /* Get aggr info */
2N/A bzero(&ginfo, sizeof (dladm_aggr_grp_attr_t));
2N/A if (dladm_aggr_info(dh, linkid, &ginfo, DLADM_OPT_ACTIVE)
2N/A != DLADM_STATUS_OK)
2N/A goto done;
2N/A /* For every port that is member of this aggr do */
2N/A for (i = 0; i < ginfo.lg_nports; i++) {
2N/A portp = &(ginfo.lg_ports[i]);
2N/A if (dladm_phys_info(dh, portp->lp_linkid, &dpa,
2N/A DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A aggr_port_stat_entry = i_dlstat_single_port_stats(dpa.dp_dev,
2N/A portp->lp_linkid);
2N/A if (aggr_port_stat_entry == NULL)
2N/A break;
2N/A
2N/A /* Create dladm_stat_chain_t object for this stat */
2N/A curr = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (curr == NULL) {
2N/A free(aggr_port_stat_entry);
2N/A goto done;
2N/A }
2N/A (void) strlcpy(curr->dc_statheader, dpa.dp_dev,
2N/A sizeof (curr->dc_statheader));
2N/A curr->dc_statentry = aggr_port_stat_entry;
2N/A curr->dc_next = NULL;
2N/A
2N/A /* Chain this aggr port stat entry */
2N/A /* head of the stat list */
2N/A if (prev == NULL)
2N/A head = curr;
2N/A else
2N/A prev->dc_next = curr;
2N/A prev = curr;
2N/A }
2N/A
2N/Adone:
2N/A /*
2N/A * Prepend the stat list with cumulative aggr stats i.e. summed over all
2N/A * component ports
2N/A */
2N/A if (head != NULL) {
2N/A total_stats = dlstat_aggr_total_stats(head);
2N/A if (total_stats != NULL) {
2N/A total_stats->dc_next = head;
2N/A head = total_stats;
2N/A }
2N/A }
2N/A
2N/A free(ginfo.lg_ports);
2N/A return (head);
2N/A}
2N/A
2N/A/* Link stat specific functions */
2N/Avoid *
2N/Adlstat_link_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A link_stat_entry_t *link_stat_entry;
2N/A dladm_stat_chain_t *head = NULL;
2N/A char linkname[MAXLINKNAMELEN];
2N/A
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A goto done;
2N/A }
2N/A
2N/A link_stat_entry = i_dlstat_link_stats(linkname);
2N/A if (link_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A head = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(link_stat_entry);
2N/A goto done;
2N/A }
2N/A
2N/A head->dc_statentry = link_stat_entry;
2N/A (void) strlcpy(head->dc_statheader, "mac_misc_stat",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_next = NULL;
2N/A
2N/Adone:
2N/A return (head);
2N/A}
2N/A
2N/A/* VDP statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_vdp_match(void *arg1, void *arg2)
2N/A{
2N/A vdp_stat_entry_t *s1 = arg1;
2N/A vdp_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->vse_linkid == s2->vse_linkid);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_vdp_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A vdp_stat_entry_t *s1 = arg1;
2N/A vdp_stat_entry_t *s2 = arg2;
2N/A vdp_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = malloc(sizeof (vdp_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A return (NULL);
2N/A
2N/A diff_entry->vse_linkid = s1->vse_linkid;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, vse_stats, vdp_stats_list,
2N/A VDP_STAT_SIZE);
2N/A
2N/A return (diff_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_vdp_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A int dfd;
2N/A door_arg_t door_arg;
2N/A vdpd_vdpstat_ret_t door_ret;
2N/A vdpd_door_arg_t vdp_door_arg;
2N/A vdp_stat_entry_t *vdp_stat_entry;
2N/A vdp_stat_t *vstat;
2N/A dladm_stat_chain_t *head = NULL;
2N/A vdpt_arg_t *vdp_arg;
2N/A char linkname[MAXLINKNAMELEN];
2N/A datalink_class_t class;
2N/A dladm_vnic_attr_t va;
2N/A
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, &class, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A
2N/A if (class != DATALINK_CLASS_PHYS && class != DATALINK_CLASS_VNIC)
2N/A return (NULL);
2N/A
2N/A bzero(&vdp_door_arg, sizeof (vdpd_door_arg_t));
2N/A vdp_arg = &vdp_door_arg.vda_arg;
2N/A if (class == DATALINK_CLASS_VNIC) {
2N/A vdp_arg->vdpt_virt_link_id = linkid;
2N/A if (dladm_vnic_info(dh, linkid, &va, DLADM_OPT_ACTIVE) !=
2N/A DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A vdp_arg->vdpt_phy_link_id = va.va_link_id;
2N/A } else {
2N/A vdp_arg->vdpt_phy_link_id = linkid;
2N/A }
2N/A vdp_door_arg.vda_cmd = VDP_GET_VDPSTAT;
2N/A vdp_arg->vdpt_version = VDPT_ARGS_VERSION;
2N/A door_arg.data_ptr = (char *)&vdp_door_arg;
2N/A door_arg.data_size = sizeof (vdpd_door_arg_t);
2N/A door_arg.desc_ptr = NULL;
2N/A door_arg.desc_num = 0;
2N/A door_arg.rbuf = (void *)&door_ret;
2N/A door_arg.rsize = sizeof (vdpd_vdpstat_ret_t);
2N/A
2N/A dfd = open(VDP_DOOR, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
2N/A if (dfd == -1) {
2N/A return (NULL);
2N/A }
2N/A if (door_call(dfd, &door_arg) == -1) {
2N/A (void) close(dfd);
2N/A return (NULL);
2N/A }
2N/A (void) close(dfd);
2N/A
2N/A if (door_ret.vvr_ret != 0) {
2N/A return (NULL);
2N/A }
2N/A vstat = &door_ret.vvr_stats;
2N/A
2N/A vdp_stat_entry = calloc(1, sizeof (vdp_stat_entry_t));
2N/A if (vdp_stat_entry == NULL)
2N/A return (NULL);
2N/A
2N/A vdp_stat_entry->vse_linkid = linkid;
2N/A vdp_stat_entry->vse_stats.vs_ipkts = vstat->vs_ipkts;
2N/A vdp_stat_entry->vse_stats.vs_opkts = vstat->vs_opkts;
2N/A vdp_stat_entry->vse_stats.vs_kas = vstat->vs_kas;
2N/A
2N/A /* Create dladm_stat_chain_t object for this stat */
2N/A head = malloc(sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(vdp_stat_entry);
2N/A return (NULL);
2N/A }
2N/A (void) strlcpy(head->dc_statheader, "vdp",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_statentry = vdp_stat_entry;
2N/A head->dc_next = NULL;
2N/A return (head);
2N/A}
2N/A
2N/A/* ECP statistic specific functions */
2N/Astatic boolean_t
2N/Ai_dlstat_ecp_match(void *arg1, void *arg2)
2N/A{
2N/A ecp_stat_entry_t *s1 = arg1;
2N/A ecp_stat_entry_t *s2 = arg2;
2N/A
2N/A return (s1->ese_linkid == s2->ese_linkid);
2N/A}
2N/A
2N/Astatic void *
2N/Ai_dlstat_ecp_stat_entry_diff(void *arg1, void *arg2)
2N/A{
2N/A ecp_stat_entry_t *s1 = arg1;
2N/A ecp_stat_entry_t *s2 = arg2;
2N/A ecp_stat_entry_t *diff_entry;
2N/A
2N/A diff_entry = malloc(sizeof (ecp_stat_entry_t));
2N/A if (diff_entry == NULL)
2N/A return (NULL);
2N/A
2N/A diff_entry->ese_linkid = s1->ese_linkid;
2N/A
2N/A DLSTAT_DIFF_STAT(s1, s2, diff_entry, ese_stats, ecp_stats_list,
2N/A ECP_STAT_SIZE);
2N/A
2N/A return (diff_entry);
2N/A}
2N/A
2N/Avoid *
2N/Adlstat_ecp_stats(dladm_handle_t dh, datalink_id_t linkid)
2N/A{
2N/A int dfd;
2N/A door_arg_t door_arg;
2N/A vdpd_ecpstat_ret_t door_ret;
2N/A vdpd_door_arg_t vdp_door_arg;
2N/A ecp_stat_entry_t *ecp_stat_entry;
2N/A ecp_stat_t *ecp_stat;
2N/A dladm_stat_chain_t *head = NULL;
2N/A vdpt_arg_t *vdp_arg;
2N/A char linkname[MAXLINKNAMELEN];
2N/A
2N/A if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
2N/A DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
2N/A return (NULL);
2N/A }
2N/A
2N/A bzero(&vdp_door_arg, sizeof (vdpd_door_arg_t));
2N/A vdp_door_arg.vda_cmd = VDP_GET_ECPSTAT;
2N/A vdp_arg = &vdp_door_arg.vda_arg;
2N/A vdp_arg->vdpt_version = VDPT_ARGS_VERSION;
2N/A vdp_arg->vdpt_phy_link_id = linkid;
2N/A door_arg.data_ptr = (char *)&vdp_door_arg;
2N/A door_arg.data_size = sizeof (vdpd_door_arg_t);
2N/A door_arg.desc_ptr = NULL;
2N/A door_arg.desc_num = 0;
2N/A door_arg.rbuf = (void *)&door_ret;
2N/A door_arg.rsize = sizeof (vdpd_ecpstat_ret_t);
2N/A
2N/A dfd = open(VDP_DOOR, O_RDONLY | O_NOFOLLOW | O_NONBLOCK);
2N/A if (dfd == -1) {
2N/A return (NULL);
2N/A }
2N/A if (door_call(dfd, &door_arg) == -1) {
2N/A (void) close(dfd);
2N/A return (NULL);
2N/A }
2N/A (void) close(dfd);
2N/A
2N/A if (door_ret.ver_ret != 0) {
2N/A return (NULL);
2N/A }
2N/A ecp_stat = &door_ret.ver_stats;
2N/A
2N/A ecp_stat_entry = calloc(1, sizeof (ecp_stat_entry_t));
2N/A if (ecp_stat_entry == NULL)
2N/A return (NULL);
2N/A ecp_stat_entry->ese_linkid = linkid;
2N/A ecp_stat_entry->ese_stats.es_ipkts = ecp_stat->es_ipkts;
2N/A ecp_stat_entry->ese_stats.es_opkts = ecp_stat->es_opkts;
2N/A ecp_stat_entry->ese_stats.es_ierrors = ecp_stat->es_ierrors;
2N/A ecp_stat_entry->ese_stats.es_oerrors = ecp_stat->es_oerrors;
2N/A ecp_stat_entry->ese_stats.es_rexmits = ecp_stat->es_rexmits;
2N/A ecp_stat_entry->ese_stats.es_timeouts = ecp_stat->es_timeouts;
2N/A
2N/A /* Create dladm_stat_chain_t object for this stat */
2N/A head = malloc(sizeof (dladm_stat_chain_t));
2N/A if (head == NULL) {
2N/A free(ecp_stat_entry);
2N/A return (NULL);
2N/A }
2N/A (void) strlcpy(head->dc_statheader, "ecp",
2N/A sizeof (head->dc_statheader));
2N/A head->dc_statentry = ecp_stat_entry;
2N/A head->dc_next = NULL;
2N/A return (head);
2N/A}
2N/A
2N/A/* Exported functions */
2N/Adladm_stat_chain_t *
2N/Adladm_link_stat_query(dladm_handle_t dh, datalink_id_t linkid,
2N/A dladm_stat_type_t stattype)
2N/A{
2N/A return (dladm_stat_table[stattype].ds_querystat(dh, linkid));
2N/A}
2N/A
2N/Adladm_stat_chain_t *
2N/Adladm_link_stat_diffchain(dladm_stat_chain_t *op1, dladm_stat_chain_t *op2,
2N/A dladm_stat_type_t stattype)
2N/A{
2N/A dladm_stat_chain_t *op1_curr, *op2_curr;
2N/A dladm_stat_chain_t *diff_curr;
2N/A dladm_stat_chain_t *diff_prev = NULL, *diff_head = NULL;
2N/A
2N/A /* Perform op1 - op2, store result in diff */
2N/A for (op1_curr = op1; op1_curr != NULL; op1_curr = op1_curr->dc_next) {
2N/A for (op2_curr = op2; op2_curr != NULL;
2N/A op2_curr = op2_curr->dc_next) {
2N/A if (dlstat_match_stats(op1_curr->dc_statentry,
2N/A op2_curr->dc_statentry, stattype)) {
2N/A break;
2N/A }
2N/A }
2N/A diff_curr = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (diff_curr == NULL)
2N/A goto done;
2N/A
2N/A diff_curr->dc_next = NULL;
2N/A
2N/A if (op2_curr == NULL) {
2N/A /* prev iteration did not have this stat entry */
2N/A diff_curr->dc_statentry =
2N/A dlstat_diff_stats(op1_curr->dc_statentry,
2N/A NULL, stattype);
2N/A } else {
2N/A diff_curr->dc_statentry =
2N/A dlstat_diff_stats(op1_curr->dc_statentry,
2N/A op2_curr->dc_statentry, stattype);
2N/A }
2N/A
2N/A if (diff_curr->dc_statentry == NULL) {
2N/A free(diff_curr);
2N/A goto done;
2N/A }
2N/A
2N/A if (diff_prev == NULL) /* head of the diff stat list */
2N/A diff_head = diff_curr;
2N/A else
2N/A diff_prev->dc_next = diff_curr;
2N/A diff_prev = diff_curr;
2N/A }
2N/Adone:
2N/A return (diff_head);
2N/A}
2N/A
2N/Avoid
2N/Adladm_link_stat_free(dladm_stat_chain_t *curr)
2N/A{
2N/A while (curr != NULL) {
2N/A dladm_stat_chain_t *tofree = curr;
2N/A
2N/A curr = curr->dc_next;
2N/A free(tofree->dc_statentry);
2N/A free(tofree);
2N/A }
2N/A}
2N/A
2N/A/* Query all link stats */
2N/Astatic name_value_stat_t *
2N/Ai_dlstat_convert_stats(void *stats, stat_info_t stats_list[], uint_t size)
2N/A{
2N/A int i;
2N/A name_value_stat_t *head_stat = NULL, *prev_stat = NULL;
2N/A name_value_stat_t *curr_stat;
2N/A
2N/A for (i = 0; i < size; i++) {
2N/A uint64_t *val = (void *)
2N/A ((uchar_t *)stats + stats_list[i].si_offset);
2N/A
2N/A curr_stat = calloc(1, sizeof (name_value_stat_t));
2N/A if (curr_stat == NULL)
2N/A break;
2N/A
2N/A (void) strlcpy(curr_stat->nv_statname, stats_list[i].si_name,
2N/A sizeof (curr_stat->nv_statname));
2N/A curr_stat->nv_statval = *val;
2N/A curr_stat->nv_nextstat = NULL;
2N/A
2N/A if (head_stat == NULL) /* First node */
2N/A head_stat = curr_stat;
2N/A else
2N/A prev_stat->nv_nextstat = curr_stat;
2N/A
2N/A prev_stat = curr_stat;
2N/A }
2N/A return (head_stat);
2N/A}
2N/A
2N/Avoid *
2N/Abuild_nvs_entry(char *statheader, void *statentry, dladm_stat_type_t stattype)
2N/A{
2N/A name_value_stat_entry_t *name_value_stat_entry;
2N/A dladm_stat_desc_t *stattbl_ptr;
2N/A void *statfields;
2N/A
2N/A stattbl_ptr = &dladm_stat_table[stattype];
2N/A
2N/A /* Allocate memory for query all stat entry */
2N/A name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2N/A if (name_value_stat_entry == NULL)
2N/A goto done;
2N/A
2N/A /* Header for these stat fields */
2N/A (void) strlcpy(name_value_stat_entry->nve_header, statheader,
2N/A sizeof (name_value_stat_entry->nve_header));
2N/A
2N/A /* Extract stat fields from the statentry */
2N/A statfields = (uchar_t *)statentry +
2N/A dladm_stat_table[stattype].ds_offset;
2N/A
2N/A /* Convert curr_stat to <statname, statval> pair */
2N/A name_value_stat_entry->nve_stats =
2N/A i_dlstat_convert_stats(statfields,
2N/A stattbl_ptr->ds_statlist, stattbl_ptr->ds_statsize);
2N/Adone:
2N/A return (name_value_stat_entry);
2N/A}
2N/A
2N/Avoid *
2N/Ai_walk_dlstat_chain(dladm_stat_chain_t *stat_head, dladm_stat_type_t stattype)
2N/A{
2N/A dladm_stat_chain_t *curr;
2N/A dladm_stat_chain_t *nvstat_head = NULL, *nvstat_prev = NULL;
2N/A dladm_stat_chain_t *nvstat_curr;
2N/A
2N/A /*
2N/A * For every stat in the chain, build header and convert all
2N/A * its stat fields
2N/A */
2N/A for (curr = stat_head; curr != NULL; curr = curr->dc_next) {
2N/A nvstat_curr = calloc(1, sizeof (dladm_stat_chain_t));
2N/A if (nvstat_curr == NULL)
2N/A break;
2N/A
2N/A nvstat_curr->dc_statentry = build_nvs_entry(curr->dc_statheader,
2N/A curr->dc_statentry, stattype);
2N/A
2N/A if (nvstat_curr->dc_statentry == NULL) {
2N/A free(nvstat_curr);
2N/A break;
2N/A }
2N/A
2N/A nvstat_curr->dc_next = NULL;
2N/A
2N/A if (nvstat_head == NULL) /* First node */
2N/A nvstat_head = nvstat_curr;
2N/A else
2N/A nvstat_prev->dc_next = nvstat_curr;
2N/A
2N/A nvstat_prev = nvstat_curr;
2N/A }
2N/Adone:
2N/A return (nvstat_head);
2N/A}
2N/A
2N/Adladm_stat_chain_t *
2N/Adladm_link_stat_query_all(dladm_handle_t dh, datalink_id_t linkid,
2N/A dladm_stat_type_t stattype)
2N/A{
2N/A dladm_stat_chain_t *stat_head;
2N/A dladm_stat_chain_t *nvstat_head = NULL;
2N/A
2N/A /* Query the requested stat */
2N/A stat_head = dladm_link_stat_query(dh, linkid, stattype);
2N/A if (stat_head == NULL)
2N/A goto done;
2N/A
2N/A /*
2N/A * Convert every statfield in every stat-entry of stat chain to
2N/A * <statname, statval> pair
2N/A */
2N/A nvstat_head = i_walk_dlstat_chain(stat_head, stattype);
2N/A
2N/A /* Free stat_head */
2N/A dladm_link_stat_free(stat_head);
2N/A
2N/Adone:
2N/A return (nvstat_head);
2N/A}
2N/A
2N/Avoid
2N/Adladm_link_stat_query_all_free(dladm_stat_chain_t *curr)
2N/A{
2N/A while (curr != NULL) {
2N/A dladm_stat_chain_t *tofree = curr;
2N/A name_value_stat_entry_t *nv_entry = curr->dc_statentry;
2N/A name_value_stat_t *nv_curr = nv_entry->nve_stats;
2N/A
2N/A while (nv_curr != NULL) {
2N/A name_value_stat_t *nv_tofree = nv_curr;
2N/A
2N/A nv_curr = nv_curr->nv_nextstat;
2N/A free(nv_tofree);
2N/A }
2N/A
2N/A curr = curr->dc_next;
2N/A free(nv_entry);
2N/A free(tofree);
2N/A }
2N/A}
2N/A
2N/A/* flow stats specific routines */
2N/Aflow_stat_t *
2N/Adladm_flow_stat_query(const char *flowname)
2N/A{
2N/A kstat_ctl_t *kcp;
2N/A kstat_t *ksp;
2N/A flow_stat_t *flow_stat = NULL;
2N/A
2N/A if ((kcp = kstat_open()) == NULL)
2N/A return (NULL);
2N/A
2N/A flow_stat = calloc(1, sizeof (flow_stat_t));
2N/A if (flow_stat == NULL)
2N/A goto done;
2N/A
2N/A ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
2N/A
2N/A if (ksp != NULL) {
2N/A i_dlstat_get_stats(kcp, ksp, flow_stat, flow_stats_list,
2N/A FLOW_STAT_SIZE);
2N/A }
2N/A
2N/Adone:
2N/A (void) kstat_close(kcp);
2N/A return (flow_stat);
2N/A}
2N/A
2N/Aflow_stat_t *
2N/Adladm_flow_stat_diff(flow_stat_t *op1, flow_stat_t *op2)
2N/A{
2N/A flow_stat_t *diff_stat;
2N/A
2N/A diff_stat = calloc(1, sizeof (flow_stat_t));
2N/A if (diff_stat == NULL)
2N/A goto done;
2N/A
2N/A if (op2 == NULL) {
2N/A bcopy(op1, diff_stat, sizeof (flow_stat_t));
2N/A } else {
2N/A i_dlstat_diff_stats(diff_stat, op1, op2, flow_stats_list,
2N/A FLOW_STAT_SIZE);
2N/A }
2N/Adone:
2N/A return (diff_stat);
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_stat_free(flow_stat_t *curr)
2N/A{
2N/A free(curr);
2N/A}
2N/A
2N/A/* Query all flow stats */
2N/Aname_value_stat_entry_t *
2N/Adladm_flow_stat_query_all(const char *flowname)
2N/A{
2N/A flow_stat_t *flow_stat;
2N/A name_value_stat_entry_t *name_value_stat_entry = NULL;
2N/A
2N/A /* Query flow stats */
2N/A flow_stat = dladm_flow_stat_query(flowname);
2N/A if (flow_stat == NULL)
2N/A goto done;
2N/A
2N/A /* Allocate memory for query all stat entry */
2N/A name_value_stat_entry = calloc(1, sizeof (name_value_stat_entry_t));
2N/A if (name_value_stat_entry == NULL) {
2N/A dladm_flow_stat_free(flow_stat);
2N/A goto done;
2N/A }
2N/A
2N/A /* Header for these stat fields */
2N/A (void) strncpy(name_value_stat_entry->nve_header, flowname,
2N/A MAXFLOWNAMELEN);
2N/A
2N/A /* Convert every statfield in flow_stat to <statname, statval> pair */
2N/A name_value_stat_entry->nve_stats =
2N/A i_dlstat_convert_stats(flow_stat, flow_stats_list, FLOW_STAT_SIZE);
2N/A
2N/A /* Free flow_stat */
2N/A dladm_flow_stat_free(flow_stat);
2N/A
2N/Adone:
2N/A return (name_value_stat_entry);
2N/A}
2N/A
2N/Avoid
2N/Adladm_flow_stat_query_all_free(name_value_stat_entry_t *curr)
2N/A{
2N/A name_value_stat_t *nv_curr = curr->nve_stats;
2N/A
2N/A while (nv_curr != NULL) {
2N/A name_value_stat_t *nv_tofree = nv_curr;
2N/A
2N/A nv_curr = nv_curr->nv_nextstat;
2N/A free(nv_tofree);
2N/A }
2N/A}