summaryrefslogtreecommitdiff
path: root/usr/src/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd')
-rw-r--r--usr/src/cmd/Makefile4
-rw-r--r--usr/src/cmd/acctadm/main.c53
-rw-r--r--usr/src/cmd/dladm/dladm.c104
-rw-r--r--usr/src/cmd/dlstat/Makefile49
-rw-r--r--usr/src/cmd/dlstat/dlstat.c2457
-rw-r--r--usr/src/cmd/dlstat/dlstat.xcl110
-rw-r--r--usr/src/cmd/flowadm/Makefile4
-rw-r--r--usr/src/cmd/flowadm/flowadm.c610
-rw-r--r--usr/src/cmd/flowstat/Makefile70
-rw-r--r--usr/src/cmd/flowstat/flowstat.c1149
-rw-r--r--usr/src/cmd/flowstat/flowstat.xcl73
-rw-r--r--usr/src/cmd/mdb/common/modules/mac/mac.c230
-rw-r--r--usr/src/cmd/zoneadmd/vplat.c111
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.c3
-rw-r--r--usr/src/cmd/zoneadmd/zoneadmd.h3
15 files changed, 4340 insertions, 690 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 36b830bfbf..2d0ce5e6d5 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -126,6 +126,7 @@ COMMON_SUBDIRS= \
diskmgtd \
dispadmin \
dladm \
+ dlstat \
dmesg \
dodatadm \
dtrace \
@@ -157,6 +158,7 @@ COMMON_SUBDIRS= \
filebench \
find \
flowadm \
+ flowstat \
fm \
fmli \
fmt \
@@ -563,6 +565,7 @@ MSGSUBDIRS= \
diff \
diffmk \
dladm \
+ dlstat \
du \
dumpcs \
ed \
@@ -577,6 +580,7 @@ MSGSUBDIRS= \
filesync \
find \
flowadm \
+ flowstat \
fm \
fold \
fs.d \
diff --git a/usr/src/cmd/acctadm/main.c b/usr/src/cmd/acctadm/main.c
index 2c610bdc10..8176214b9e 100644
--- a/usr/src/cmd/acctadm/main.c
+++ b/usr/src/cmd/acctadm/main.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -375,30 +375,7 @@ main(int argc, char *argv[])
}
}
str2buf(buf, disabled, AC_OFF, type);
- }
- if (enabled) {
- /*
- * Lets us get network logging started.
- */
- if (type & AC_NET) {
- /*
- * Default logging interval for AC_NET is
- * ACCTADM_NET_LOG_INTERVAL.
- */
- (void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
- PRIV_SYS_DL_CONFIG, NULL);
- err = dladm_start_usagelog(dld_handle,
- strcmp(enabled, "basic") == 0 ?
- DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW,
- ACCTADM_NET_LOG_INTERVAL);
- (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
- PRIV_SYS_DL_CONFIG, NULL);
- if (err != DLADM_STATUS_OK) {
- die(gettext("failed to start logging "
- "network information, error %d\n"),
- errno);
- }
- }
+ } else if (enabled) {
str2buf(buf, enabled, AC_ON, type);
}
(void) priv_set(PRIV_ON, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
@@ -408,7 +385,6 @@ main(int argc, char *argv[])
"resources\n"), ac_type_name(type));
}
(void) priv_set(PRIV_OFF, PRIV_EFFECTIVE, PRIV_SYS_ACCT, NULL);
-
tracked = buf2str(buf, AC_BUFSIZE, AC_ON, type);
untracked = buf2str(buf, AC_BUFSIZE, AC_OFF, type);
if (aconf_set_string(AC_PROP_TRACKED, tracked) == -1)
@@ -448,6 +424,31 @@ main(int argc, char *argv[])
modified++;
}
+ /*
+ * Let's get network logging started. We do this after turning on
+ * accounting and opening the file so that we can start writing
+ * immediately.
+ */
+ if (enabled && (type & AC_NET)) {
+ /*
+ * Default logging interval for AC_NET is
+ * ACCTADM_NET_LOG_INTERVAL.
+ */
+ (void) priv_set(PRIV_ON, PRIV_EFFECTIVE,
+ PRIV_SYS_DL_CONFIG, NULL);
+ err = dladm_start_usagelog(dld_handle,
+ strcmp(enabled, "basic") == 0 ?
+ DLADM_LOGTYPE_LINK : DLADM_LOGTYPE_FLOW,
+ ACCTADM_NET_LOG_INTERVAL);
+ (void) priv_set(PRIV_OFF, PRIV_EFFECTIVE,
+ PRIV_SYS_DL_CONFIG, NULL);
+ if (err != DLADM_STATUS_OK) {
+ die(gettext("failed to start logging "
+ "network information, error %d\n"),
+ errno);
+ }
+ }
+
if (Dflg) {
/*
* Disable accounting
diff --git a/usr/src/cmd/dladm/dladm.c b/usr/src/cmd/dladm/dladm.c
index a55fa79735..713920767c 100644
--- a/usr/src/cmd/dladm/dladm.c
+++ b/usr/src/cmd/dladm/dladm.c
@@ -320,7 +320,7 @@ static cmd_t cmds[] = {
" create-vnic [-t] -l <link> [-m <value> | auto |\n"
"\t\t {factory [-n <slot-id>]} | {random [-r <prefix>]} |\n"
"\t\t {vrrp -V <vrid> -A {inet | inet6}} [-v <vid> [-f]]\n"
- "\t\t [-H] [-p <prop>=<value>[,...]] <vnic-link>" },
+ "\t\t [-p <prop>=<value>[,...]] <vnic-link>" },
{ "delete-vnic", do_delete_vnic,
" delete-vnic [-t] <vnic-link>" },
{ "show-vnic", do_show_vnic,
@@ -810,18 +810,18 @@ static const ofmt_field_t phys_m_fields[] = {
typedef enum {
PHYS_H_LINK,
- PHYS_H_GROUP,
- PHYS_H_GRPTYPE,
+ PHYS_H_RINGTYPE,
PHYS_H_RINGS,
PHYS_H_CLIENTS
} phys_h_field_index_t;
+#define RINGSTRLEN 21
+
static const ofmt_field_t phys_h_fields[] = {
{ "LINK", 13, PHYS_H_LINK, print_phys_one_hwgrp_cb},
-{ "GROUP", 9, PHYS_H_GROUP, print_phys_one_hwgrp_cb},
-{ "GROUPTYPE", 7, PHYS_H_GRPTYPE, print_phys_one_hwgrp_cb},
-{ "RINGS", 17, PHYS_H_RINGS, print_phys_one_hwgrp_cb},
-{ "CLIENTS", 21, PHYS_H_CLIENTS, print_phys_one_hwgrp_cb},
+{ "RINGTYPE", 9, PHYS_H_RINGTYPE, print_phys_one_hwgrp_cb},
+{ "RINGS", RINGSTRLEN, PHYS_H_RINGS, print_phys_one_hwgrp_cb},
+{ "CLIENTS", 24, PHYS_H_CLIENTS, print_phys_one_hwgrp_cb},
{ NULL, 0, 0, NULL}}
;
@@ -3694,6 +3694,13 @@ typedef struct {
static boolean_t
print_phys_one_hwgrp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
{
+ int i;
+ boolean_t first = B_TRUE;
+ int start = -1;
+ int end = -1;
+ char ringstr[RINGSTRLEN];
+ char ringsubstr[RINGSTRLEN];
+
print_phys_hwgrp_state_t *hg_state = ofarg->ofmt_cbarg;
dladm_hwgrp_attr_t *attr = hg_state->hs_grp_attr;
@@ -3701,15 +3708,78 @@ print_phys_one_hwgrp_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
case PHYS_H_LINK:
(void) snprintf(buf, bufsize, "%s", attr->hg_link_name);
break;
- case PHYS_H_GROUP:
- (void) snprintf(buf, bufsize, "%d", attr->hg_grp_num);
- break;
- case PHYS_H_GRPTYPE:
+ case PHYS_H_RINGTYPE:
(void) snprintf(buf, bufsize, "%s",
attr->hg_grp_type == DLADM_HWGRP_TYPE_RX ? "RX" : "TX");
break;
case PHYS_H_RINGS:
- (void) snprintf(buf, bufsize, "%d", attr->hg_n_rings);
+ ringstr[0] = '\0';
+ for (i = 0; i < attr->hg_n_rings; i++) {
+ uint_t index = attr->hg_rings[i];
+
+ if (start == -1) {
+ start = index;
+ end = index;
+ } else if (index == end + 1) {
+ end = index;
+ } else {
+ if (start == end) {
+ if (first) {
+ (void) snprintf(
+ ringsubstr,
+ RINGSTRLEN, "%d",
+ start);
+ first = B_FALSE;
+ } else {
+ (void) snprintf(
+ ringsubstr,
+ RINGSTRLEN, ",%d",
+ start);
+ }
+ } else {
+ if (first) {
+ (void) snprintf(
+ ringsubstr,
+ RINGSTRLEN,
+ "%d-%d",
+ start, end);
+ first = B_FALSE;
+ } else {
+ (void) snprintf(
+ ringsubstr,
+ RINGSTRLEN,
+ ",%d-%d",
+ start, end);
+ }
+ }
+ (void) strlcat(ringstr, ringsubstr,
+ RINGSTRLEN);
+ start = index;
+ end = index;
+ }
+ }
+ /* The last one */
+ if (start != -1) {
+ if (first) {
+ if (start == end) {
+ (void) snprintf(buf, bufsize, "%d",
+ start);
+ } else {
+ (void) snprintf(buf, bufsize, "%d-%d",
+ start, end);
+ }
+ } else {
+ if (start == end) {
+ (void) snprintf(ringsubstr, RINGSTRLEN,
+ ",%d", start);
+ } else {
+ (void) snprintf(ringsubstr, RINGSTRLEN,
+ ",%d-%d", start, end);
+ }
+ (void) strlcat(ringstr, ringsubstr, RINGSTRLEN);
+ (void) snprintf(buf, bufsize, "%s", ringstr);
+ }
+ }
break;
case PHYS_H_CLIENTS:
if (attr->hg_client_names[0] == '\0') {
@@ -4232,8 +4302,7 @@ do_show_phys(int argc, char *argv[], const char *use)
"link,media,state,speed,duplex,device";
char *all_inactive_fields = "link,device,media,flags";
char *all_mac_fields = "link,slot,address,inuse,client";
- char *all_hwgrp_fields =
- "link,group,grouptype,rings,clients";
+ char *all_hwgrp_fields = "link,ringtype,rings,clients";
const ofmt_field_t *pf;
ofmt_handle_t ofmt;
ofmt_status_t oferr;
@@ -4534,9 +4603,6 @@ do_create_vnic(int argc, char *argv[], const char *use)
case 'f':
flags |= DLADM_OPT_FORCE;
break;
- case 'H':
- flags |= DLADM_OPT_HWRINGS;
- break;
default:
die_opterr(optopt, option, use);
}
@@ -8722,7 +8788,7 @@ warn(const char *format, ...)
(void) vfprintf(stderr, format, alist);
va_end(alist);
- (void) putchar('\n');
+ (void) putc('\n', stderr);
}
/* PRINTFLIKE2 */
@@ -8779,7 +8845,7 @@ die(const char *format, ...)
(void) vfprintf(stderr, format, alist);
va_end(alist);
- (void) putchar('\n');
+ (void) putc('\n', stderr);
/* close dladm handle if it was opened */
if (handle != NULL)
diff --git a/usr/src/cmd/dlstat/Makefile b/usr/src/cmd/dlstat/Makefile
new file mode 100644
index 0000000000..8885dbc5a0
--- /dev/null
+++ b/usr/src/cmd/dlstat/Makefile
@@ -0,0 +1,49 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+PROG= dlstat
+
+ROOTFS_PROG= $(PROG)
+
+include ../Makefile.cmd
+
+XGETFLAGS += -a -x $(PROG).xcl
+LDLIBS += -L$(ROOT)/lib
+LDLIBS += -ldladm -linetutil
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+install: all $(ROOTSBINPROG)
+ $(RM) $(ROOTUSRSBINPROG)
+ -$(SYMLINK) ../../sbin/$(PROG) $(ROOTUSRSBINPROG)
+
+clean:
+
+lint: lint_PROG
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/dlstat/dlstat.c b/usr/src/cmd/dlstat/dlstat.c
new file mode 100644
index 0000000000..a931ba82ff
--- /dev/null
+++ b/usr/src/cmd/dlstat/dlstat.c
@@ -0,0 +1,2457 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <strings.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <priv.h>
+#include <termios.h>
+#include <pwd.h>
+#include <auth_attr.h>
+#include <auth_list.h>
+#include <libintl.h>
+#include <libdevinfo.h>
+#include <libdlpi.h>
+#include <libdladm.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <libdlaggr.h>
+#include <libinetutil.h>
+#include <bsm/adt.h>
+#include <bsm/adt_event.h>
+#include <stddef.h>
+#include <ofmt.h>
+
+typedef struct link_chain_s {
+ datalink_id_t lc_linkid;
+ boolean_t lc_visited;
+ dladm_stat_chain_t *lc_statchain[DLADM_STAT_NUM_STATS];
+ struct link_chain_s *lc_next;
+} link_chain_t;
+
+typedef void * (*stats2str_t)(const char *, void *,
+ char, boolean_t);
+
+typedef struct show_state {
+ link_chain_t *ls_linkchain;
+ boolean_t ls_stattype[DLADM_STAT_NUM_STATS];
+ stats2str_t ls_stats2str[DLADM_STAT_NUM_STATS];
+ ofmt_handle_t ls_ofmt;
+ char ls_unit;
+ boolean_t ls_parsable;
+} show_state_t;
+
+typedef struct show_history_state_s {
+ boolean_t hs_plot;
+ boolean_t hs_parsable;
+ boolean_t hs_printheader;
+ boolean_t hs_first;
+ boolean_t hs_showall;
+ ofmt_handle_t hs_ofmt;
+} show_history_state_t;
+
+/*
+ * callback functions for printing output and error diagnostics.
+ */
+static ofmt_cb_t print_default_cb;
+
+static void dlstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
+
+typedef void cmdfunc_t(int, char **, const char *);
+
+static cmdfunc_t do_show, do_show_history, do_show_phys, do_show_link;
+static cmdfunc_t do_show_aggr;
+
+static void die(const char *, ...);
+static void die_optdup(int);
+static void die_opterr(int, int, const char *);
+static void die_dlerr(dladm_status_t, const char *, ...);
+static void warn(const char *, ...);
+
+typedef struct cmd {
+ char *c_name;
+ cmdfunc_t *c_fn;
+ const char *c_usage;
+} cmd_t;
+
+static cmd_t cmds[] = {
+ { "", do_show,
+ "dlstat [-r | -t] [-i <interval>] [link]\n"
+ " dlstat [-a | -A] [-i <interval>] [-p] [ -o field[,...]]\n"
+ " [-u R|K|M|G|T|P] [link]"},
+ { "show-phys", do_show_phys,
+ "dlstat show-phys [-r | -t] [-i interval] [-a]\n"
+ " [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
+ "[link]"},
+ { "show-link", do_show_link,
+ "dlstat show-link [-r [-F] | -t] [-i interval] [-a]\n"
+ " [-p] [ -o field[,...]] [-u R|K|M|G|T|P] "
+ "[link]\n"
+ " dlstat show-link -h [-a] [-d] [-F <format>]\n"
+ " [-s <DD/MM/YYYY,HH:MM:SS>] "
+ "[-e <DD/MM/YYYY,HH:MM:SS>]\n"
+ " -f <logfile> [<link>]" },
+ { "show-aggr", do_show_aggr,
+ "dlstat show-aggr [-r | -t] [-i interval] [-p]\n"
+ " [ -o field[,...]] [-u R|K|M|G|T|P] "
+ " [link]" }
+};
+
+#define MAXSTATLEN 15
+
+/*
+ * dlstat : total stat fields
+ */
+typedef struct total_fields_buf_s {
+ char t_linkname[MAXLINKNAMELEN];
+ char t_ipackets[MAXSTATLEN];
+ char t_rbytes[MAXSTATLEN];
+ char t_opackets[MAXSTATLEN];
+ char t_obytes[MAXSTATLEN];
+} total_fields_buf_t;
+
+static ofmt_field_t total_s_fields[] = {
+{ "LINK", 15,
+ offsetof(total_fields_buf_t, t_linkname), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(total_fields_buf_t, t_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(total_fields_buf_t, t_rbytes), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(total_fields_buf_t, t_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(total_fields_buf_t, t_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys: both Rx and Tx stat fields
+ */
+typedef struct ring_fields_buf_s {
+ char r_linkname[MAXLINKNAMELEN];
+ char r_type[MAXSTATLEN];
+ char r_id[MAXSTATLEN];
+ char r_index[MAXSTATLEN];
+ char r_packets[MAXSTATLEN];
+ char r_bytes[MAXSTATLEN];
+} ring_fields_buf_t;
+
+static ofmt_field_t ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(ring_fields_buf_t, r_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(ring_fields_buf_t, r_type), print_default_cb},
+{ "ID", 7,
+ offsetof(ring_fields_buf_t, r_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(ring_fields_buf_t, r_index), print_default_cb},
+{ "PKTS", 8,
+ offsetof(ring_fields_buf_t, r_packets), print_default_cb},
+{ "BYTES", 8,
+ offsetof(ring_fields_buf_t, r_bytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys -r: Rx Ring stat fields
+ */
+typedef struct rx_ring_fields_buf_s {
+ char rr_linkname[MAXLINKNAMELEN];
+ char rr_type[MAXSTATLEN];
+ char rr_id[MAXSTATLEN];
+ char rr_index[MAXSTATLEN];
+ char rr_ipackets[MAXSTATLEN];
+ char rr_rbytes[MAXSTATLEN];
+} rx_ring_fields_buf_t;
+
+static ofmt_field_t rx_ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(rx_ring_fields_buf_t, rr_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_ring_fields_buf_t, rr_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_ring_fields_buf_t, rr_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_ring_fields_buf_t, rr_index), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_ring_fields_buf_t, rr_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_ring_fields_buf_t, rr_rbytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-phys -t: Tx Ring stat fields
+ */
+typedef struct tx_ring_fields_buf_s {
+ char tr_linkname[MAXLINKNAMELEN];
+ char tr_type[MAXSTATLEN];
+ char tr_id[MAXSTATLEN];
+ char tr_index[MAXSTATLEN];
+ char tr_opackets[MAXSTATLEN];
+ char tr_obytes[MAXSTATLEN];
+} tx_ring_fields_buf_t;
+
+static ofmt_field_t tx_ring_s_fields[] = {
+{ "LINK", 15,
+ offsetof(tx_ring_fields_buf_t, tr_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(tx_ring_fields_buf_t, tr_type), print_default_cb},
+{ "ID", 7,
+ offsetof(tx_ring_fields_buf_t, tr_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(tx_ring_fields_buf_t, tr_index), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(tx_ring_fields_buf_t, tr_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(tx_ring_fields_buf_t, tr_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link: both Rx and Tx lane fields
+ */
+typedef struct lane_fields_buf_s {
+ char l_linkname[MAXLINKNAMELEN];
+ char l_type[MAXSTATLEN];
+ char l_id[MAXSTATLEN];
+ char l_index[MAXSTATLEN];
+ char l_packets[MAXSTATLEN];
+ char l_bytes[MAXSTATLEN];
+} lane_fields_buf_t;
+
+static ofmt_field_t lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(lane_fields_buf_t, l_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(lane_fields_buf_t, l_type), print_default_cb},
+{ "ID", 7,
+ offsetof(lane_fields_buf_t, l_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(lane_fields_buf_t, l_index), print_default_cb},
+{ "PKTS", 8,
+ offsetof(lane_fields_buf_t, l_packets), print_default_cb},
+{ "BYTES", 8,
+ offsetof(lane_fields_buf_t, l_bytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -r, dlstat -r: Rx Lane stat fields
+ */
+typedef struct rx_lane_fields_buf_s {
+ char rl_linkname[MAXLINKNAMELEN];
+ char rl_type[MAXSTATLEN];
+ char rl_id[MAXSTATLEN];
+ char rl_index[MAXSTATLEN];
+ char rl_ipackets[MAXSTATLEN];
+ char rl_rbytes[MAXSTATLEN];
+ char rl_intrs[MAXSTATLEN];
+ char rl_polls[MAXSTATLEN];
+ char rl_sdrops[MAXSTATLEN];
+ char rl_chl10[MAXSTATLEN];
+ char rl_ch10_50[MAXSTATLEN];
+ char rl_chg50[MAXSTATLEN];
+} rx_lane_fields_buf_t;
+
+static ofmt_field_t rx_lane_s_fields[] = {
+{ "LINK", 10,
+ offsetof(rx_lane_fields_buf_t, rl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_lane_fields_buf_t, rl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_lane_fields_buf_t, rl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_lane_fields_buf_t, rl_index), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_lane_fields_buf_t, rl_rbytes), print_default_cb},
+{ "INTRS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_intrs), print_default_cb},
+{ "POLLS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_polls), print_default_cb},
+{ "SDROPS", 8,
+ offsetof(rx_lane_fields_buf_t, rl_sdrops), print_default_cb},
+{ "CH<10", 8,
+ offsetof(rx_lane_fields_buf_t, rl_chl10), print_default_cb},
+{ "CH10-50", 8,
+ offsetof(rx_lane_fields_buf_t, rl_ch10_50), print_default_cb},
+{ "CH>50", 8,
+ offsetof(rx_lane_fields_buf_t, rl_chg50), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -r -F: Rx fanout stat fields
+ */
+typedef struct rx_fanout_lane_fields_buf_s {
+ char rfl_linkname[MAXLINKNAMELEN];
+ char rfl_type[MAXSTATLEN];
+ char rfl_id[MAXSTATLEN];
+ char rfl_index[MAXSTATLEN];
+ char rfl_fout[MAXSTATLEN];
+ char rfl_ipackets[MAXSTATLEN];
+ char rfl_rbytes[MAXSTATLEN];
+} rx_fanout_lane_fields_buf_t;
+
+static ofmt_field_t rx_fanout_lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_index), print_default_cb},
+{ "FOUT", 6,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_fout), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(rx_fanout_lane_fields_buf_t, rfl_rbytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-link -t: Tx Lane stat fields
+ */
+typedef struct tx_lane_fields_buf_s {
+ char tl_linkname[MAXLINKNAMELEN];
+ char tl_index[MAXSTATLEN];
+ char tl_type[MAXSTATLEN];
+ char tl_id[MAXSTATLEN];
+ char tl_opackets[MAXSTATLEN];
+ char tl_obytes[MAXSTATLEN];
+ char tl_blockcnt[MAXSTATLEN];
+ char tl_unblockcnt[MAXSTATLEN];
+ char tl_sdrops[MAXSTATLEN];
+} tx_lane_fields_buf_t;
+
+static ofmt_field_t tx_lane_s_fields[] = {
+{ "LINK", 15,
+ offsetof(tx_lane_fields_buf_t, tl_linkname), print_default_cb},
+{ "TYPE", 5,
+ offsetof(tx_lane_fields_buf_t, tl_type), print_default_cb},
+{ "ID", 7,
+ offsetof(tx_lane_fields_buf_t, tl_id), print_default_cb},
+{ "INDEX", 6,
+ offsetof(tx_lane_fields_buf_t, tl_index), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(tx_lane_fields_buf_t, tl_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(tx_lane_fields_buf_t, tl_obytes), print_default_cb},
+{ "BLKCNT", 8,
+ offsetof(tx_lane_fields_buf_t, tl_blockcnt), print_default_cb},
+{ "UBLKCNT", 8,
+ offsetof(tx_lane_fields_buf_t, tl_unblockcnt), print_default_cb},
+{ "SDROPS", 8,
+ offsetof(tx_lane_fields_buf_t, tl_sdrops), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * dlstat show-aggr: aggr port stat fields
+ */
+typedef struct aggr_port_fields_buf_s {
+ char ap_linkname[MAXLINKNAMELEN];
+ char ap_portname[MAXLINKNAMELEN];
+ char ap_ipackets[MAXSTATLEN];
+ char ap_rbytes[MAXSTATLEN];
+ char ap_opackets[MAXSTATLEN];
+ char ap_obytes[MAXSTATLEN];
+} aggr_port_fields_buf_t;
+
+static ofmt_field_t aggr_port_s_fields[] = {
+{ "LINK", 15,
+ offsetof(aggr_port_fields_buf_t, ap_linkname), print_default_cb},
+{ "PORT", 15,
+ offsetof(aggr_port_fields_buf_t, ap_portname), print_default_cb},
+{ "IPKTS", 8,
+ offsetof(aggr_port_fields_buf_t, ap_ipackets), print_default_cb},
+{ "RBYTES", 8,
+ offsetof(aggr_port_fields_buf_t, ap_rbytes), print_default_cb},
+{ "OPKTS", 8,
+ offsetof(aggr_port_fields_buf_t, ap_opackets), print_default_cb},
+{ "OBYTES", 8,
+ offsetof(aggr_port_fields_buf_t, ap_obytes), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * structures for 'dlstat show-link -h'
+ */
+typedef struct history_fields_buf_s {
+ char h_link[12];
+ char h_duration[10];
+ char h_ipackets[9];
+ char h_rbytes[10];
+ char h_opackets[9];
+ char h_obytes[10];
+ char h_bandwidth[14];
+} history_fields_buf_t;
+
+static ofmt_field_t history_fields[] = {
+{ "LINK", 13,
+ offsetof(history_fields_buf_t, h_link), print_default_cb},
+{ "DURATION", 11,
+ offsetof(history_fields_buf_t, h_duration), print_default_cb},
+{ "IPKTS", 10,
+ offsetof(history_fields_buf_t, h_ipackets), print_default_cb},
+{ "RBYTES", 11,
+ offsetof(history_fields_buf_t, h_rbytes), print_default_cb},
+{ "OPKTS", 10,
+ offsetof(history_fields_buf_t, h_opackets), print_default_cb},
+{ "OBYTES", 11,
+ offsetof(history_fields_buf_t, h_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_fields_buf_t, h_bandwidth), print_default_cb},
+{ NULL, 0, 0, NULL}};
+
+/*
+ * structures for 'dlstat show-link -h link'
+ */
+typedef struct history_l_fields_buf_s {
+ char hl_link[12];
+ char hl_stime[13];
+ char hl_etime[13];
+ char hl_rbytes[8];
+ char hl_obytes[8];
+ char hl_bandwidth[14];
+} history_l_fields_buf_t;
+
+static ofmt_field_t history_l_fields[] = {
+/* name, field width, offset */
+{ "LINK", 13,
+ offsetof(history_l_fields_buf_t, hl_link), print_default_cb},
+{ "START", 14,
+ offsetof(history_l_fields_buf_t, hl_stime), print_default_cb},
+{ "END", 14,
+ offsetof(history_l_fields_buf_t, hl_etime), print_default_cb},
+{ "RBYTES", 9,
+ offsetof(history_l_fields_buf_t, hl_rbytes), print_default_cb},
+{ "OBYTES", 9,
+ offsetof(history_l_fields_buf_t, hl_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_l_fields_buf_t, hl_bandwidth), print_default_cb},
+{ NULL, 0, 0, NULL}}
+;
+
+static char *progname;
+
+/*
+ * Handle to libdladm. Opened in main() before the sub-command
+ * specific function is called.
+ */
+static dladm_handle_t handle = NULL;
+
+static void
+usage(void)
+{
+ int i;
+ cmd_t *cmdp;
+
+ (void) fprintf(stderr, gettext("usage: "));
+ for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
+ cmdp = &cmds[i];
+ if (cmdp->c_usage != NULL)
+ (void) fprintf(stderr, "%s\n", gettext(cmdp->c_usage));
+ }
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ cmd_t *cmdp;
+ dladm_status_t status;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ progname = argv[0];
+
+ /* Open the libdladm handle */
+ if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
+ die_dlerr(status, "could not open /dev/dld");
+
+ if (argc == 1) {
+ do_show(argc - 1, NULL, cmds[0].c_usage);
+ goto done;
+ }
+
+ for (i = 0; i < sizeof (cmds) / sizeof (cmds[0]); i++) {
+ cmdp = &cmds[i];
+ if (strcmp(argv[1], cmdp->c_name) == 0) {
+ cmdp->c_fn(argc - 1, &argv[1], cmdp->c_usage);
+ goto done;
+ }
+ }
+
+ do_show(argc, &argv[0], cmds[0].c_usage);
+
+done:
+ dladm_close(handle);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+show_history_date(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ time_t stime;
+ char timebuf[20];
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ stime = history->du_stime;
+ (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
+ localtime(&stime));
+ (void) printf("%s\n", timebuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_time(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ char buf[DLADM_STRSIZE];
+ history_l_fields_buf_t ubuf;
+ time_t time;
+ double bw;
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ if (state->hs_plot) {
+ if (!state->hs_printheader) {
+ if (state->hs_first) {
+ (void) printf("# Time");
+ state->hs_first = B_FALSE;
+ }
+ (void) printf(" %s", history->du_name);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->hs_first = B_TRUE;
+ state->hs_printheader = B_TRUE;
+ }
+ } else {
+ if (state->hs_first) {
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T",
+ localtime(&time));
+ state->hs_first = B_FALSE;
+ (void) printf("%s", buf);
+ }
+ bw = (double)history->du_bandwidth/1000;
+ (void) printf(" %.2f", bw);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->hs_first = B_TRUE;
+ }
+ }
+ return (DLADM_STATUS_OK);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.hl_link, sizeof (ubuf.hl_link), "%s",
+ history->du_name);
+ time = history->du_stime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.hl_stime, sizeof (ubuf.hl_stime), "%s",
+ buf);
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.hl_etime, sizeof (ubuf.hl_etime), "%s",
+ buf);
+ (void) snprintf(ubuf.hl_rbytes, sizeof (ubuf.hl_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.hl_obytes, sizeof (ubuf.hl_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.hl_bandwidth, sizeof (ubuf.hl_bandwidth),
+ "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->hs_ofmt, &ubuf);
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_res(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = arg;
+ char buf[DLADM_STRSIZE];
+ history_fields_buf_t ubuf;
+ dladm_status_t status;
+ uint32_t flags;
+
+ /*
+ * Only show history information for existing links unless '-a'
+ * is specified.
+ */
+ if (!state->hs_showall) {
+ if ((status = dladm_name2info(handle, history->du_name,
+ NULL, &flags, NULL, NULL)) != DLADM_STATUS_OK) {
+ return (status);
+ }
+ if ((flags & DLADM_OPT_ACTIVE) == 0)
+ return (DLADM_STATUS_LINKINVAL);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.h_link, sizeof (ubuf.h_link), "%s",
+ history->du_name);
+ (void) snprintf(ubuf.h_duration, sizeof (ubuf.h_duration),
+ "%llu", history->du_duration);
+ (void) snprintf(ubuf.h_ipackets, sizeof (ubuf.h_ipackets),
+ "%llu", history->du_ipackets);
+ (void) snprintf(ubuf.h_rbytes, sizeof (ubuf.h_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.h_opackets, sizeof (ubuf.h_opackets),
+ "%llu", history->du_opackets);
+ (void) snprintf(ubuf.h_obytes, sizeof (ubuf.h_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.h_bandwidth, sizeof (ubuf.h_bandwidth),
+ "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->hs_ofmt, &ubuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static boolean_t
+valid_formatspec(char *formatspec_str)
+{
+ return (strcmp(formatspec_str, "gnuplot") == 0);
+}
+
+/*ARGSUSED*/
+static void
+do_show_history(int argc, char *argv[], const char *use)
+{
+ char *file = NULL;
+ int opt;
+ dladm_status_t status;
+ boolean_t d_arg = B_FALSE;
+ char *stime = NULL;
+ char *etime = NULL;
+ char *resource = NULL;
+ show_history_state_t state;
+ boolean_t o_arg = B_FALSE;
+ boolean_t F_arg = B_FALSE;
+ char *fields_str = NULL;
+ char *formatspec_str = NULL;
+ char *all_l_fields =
+ "link,start,end,rbytes,obytes,bandwidth";
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = 0;
+
+ bzero(&state, sizeof (show_history_state_t));
+ state.hs_parsable = B_FALSE;
+ state.hs_printheader = B_FALSE;
+ state.hs_plot = B_FALSE;
+ state.hs_first = B_TRUE;
+
+ while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
+ switch (opt) {
+ case 'd':
+ d_arg = B_TRUE;
+ break;
+ case 'a':
+ state.hs_showall = B_TRUE;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 's':
+ stime = optarg;
+ break;
+ case 'e':
+ etime = optarg;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ fields_str = optarg;
+ break;
+ case 'F':
+ state.hs_plot = F_arg = B_TRUE;
+ formatspec_str = optarg;
+ break;
+ default:
+ die_opterr(optopt, opt, use);
+ break;
+ }
+ }
+
+ if (file == NULL)
+ die("show-link -h requires a file");
+
+ if (optind == (argc-1)) {
+ uint32_t flags;
+
+ resource = argv[optind];
+ if (!state.hs_showall &&
+ (((status = dladm_name2info(handle, resource, NULL, &flags,
+ NULL, NULL)) != DLADM_STATUS_OK) ||
+ ((flags & DLADM_OPT_ACTIVE) == 0))) {
+ die("invalid link: '%s'", resource);
+ }
+ }
+
+ if (F_arg && d_arg)
+ die("incompatible -d and -F options");
+
+ if (F_arg && !valid_formatspec(formatspec_str))
+ die("Format specifier %s not supported", formatspec_str);
+
+ if (state.hs_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (resource == NULL && stime == NULL && etime == NULL) {
+ oferr = ofmt_open(fields_str, history_fields, ofmtflags, 0,
+ &ofmt);
+ } else {
+ if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
+ fields_str = all_l_fields;
+ oferr = ofmt_open(fields_str, history_l_fields, ofmtflags, 0,
+ &ofmt);
+
+ }
+ dlstat_ofmt_check(oferr, state.hs_parsable, ofmt);
+ state.hs_ofmt = ofmt;
+
+ if (d_arg) {
+ /* Print log dates */
+ status = dladm_usage_dates(show_history_date,
+ DLADM_LOGTYPE_LINK, file, resource, &state);
+ } else if (resource == NULL && stime == NULL && etime == NULL &&
+ !F_arg) {
+ /* Print summary */
+ status = dladm_usage_summary(show_history_res,
+ DLADM_LOGTYPE_LINK, file, &state);
+ } else if (resource != NULL) {
+ /* Print log entries for named resource */
+ status = dladm_walk_usage_res(show_history_time,
+ DLADM_LOGTYPE_LINK, file, resource, stime, etime, &state);
+ } else {
+ /* Print time and information for each link */
+ status = dladm_walk_usage_time(show_history_time,
+ DLADM_LOGTYPE_LINK, file, stime, etime, &state);
+ }
+
+ if (status != DLADM_STATUS_OK)
+ die_dlerr(status, "show-link -h");
+ ofmt_close(ofmt);
+}
+
+boolean_t
+dlstat_unit(char *oarg, char *unit)
+{
+ if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
+ (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
+ (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
+ *unit = oarg[0];
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+void
+map_to_units(char *buf, uint_t bufsize, double num, char unit,
+ boolean_t parsable)
+{
+ if (parsable) {
+ (void) snprintf(buf, bufsize, "%.0lf", num);
+ return;
+ }
+
+ if (unit == '\0') {
+ int index;
+
+ for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
+ ;
+
+ switch (index) {
+ case 0:
+ unit = '\0';
+ break;
+ case 1:
+ unit = 'K';
+ break;
+ case 2:
+ unit = 'M';
+ break;
+ case 3:
+ unit = 'G';
+ break;
+ case 4:
+ unit = 'T';
+ break;
+ case 5:
+ /* Largest unit supported */
+ default:
+ unit = 'P';
+ break;
+ }
+ } else {
+ switch (unit) {
+ case 'R':
+ /* Already raw numbers */
+ unit = '\0';
+ break;
+ case 'K':
+ num /= 1000;
+ break;
+ case 'M':
+ num /= (1000*1000);
+ break;
+ case 'G':
+ num /= (1000*1000*1000);
+ break;
+ case 'T':
+ num /= (1000.0*1000.0*1000.0*1000.0);
+ break;
+ case 'P':
+ /* Largest unit supported */
+ default:
+ num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
+ break;
+ }
+ }
+
+ if (unit == '\0')
+ (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
+ else
+ (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
+}
+
+link_chain_t *
+get_link_prev_stat(datalink_id_t linkid, void *arg)
+{
+ show_state_t *state = (show_state_t *)arg;
+ link_chain_t *link_curr = NULL;
+
+ /* Scan prev linkid list and look for entry matching this entry */
+ for (link_curr = state->ls_linkchain; link_curr;
+ link_curr = link_curr->lc_next) {
+ if (link_curr->lc_linkid == linkid)
+ break;
+ }
+ /* New link, add it */
+ if (link_curr == NULL) {
+ link_curr = (link_chain_t *)malloc(sizeof (link_chain_t));
+ if (link_curr == NULL)
+ goto done;
+ link_curr->lc_linkid = linkid;
+ bzero(&link_curr->lc_statchain,
+ sizeof (link_curr->lc_statchain));
+ link_curr->lc_next = state->ls_linkchain;
+ state->ls_linkchain = link_curr;
+ }
+done:
+ return (link_curr);
+}
+
+/*
+ * Number of links may change while dlstat with -i is executing.
+ * Free memory allocated for links that are no longer there.
+ * Prepare for next iteration by marking visited = false for existing stat
+ * entries.
+ */
+static void
+cleanup_removed_links(show_state_t *state)
+{
+ link_chain_t *lcurr;
+ link_chain_t *lprev;
+ link_chain_t *tofree;
+ int i;
+
+ /* Delete all nodes from the list that have lc_visited marked false */
+ lcurr = state->ls_linkchain;
+ while (lcurr != NULL) {
+ if (lcurr->lc_visited) {
+ lcurr->lc_visited = B_FALSE;
+ lprev = lcurr;
+ lcurr = lcurr->lc_next;
+ continue;
+ }
+ /* Is it head of the list? */
+ if (lcurr == state->ls_linkchain)
+ state->ls_linkchain = lcurr->lc_next;
+ else
+ lprev->lc_next = lcurr->lc_next;
+ /* lprev remains the same */
+ tofree = lcurr;
+ lcurr = lcurr->lc_next;
+
+ /* Free stats memory for the removed link */
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (state->ls_stattype[i])
+ dladm_link_stat_free(tofree->lc_statchain[i]);
+ }
+ free(tofree);
+ }
+}
+
+void *
+print_total_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ total_stat_entry_t *sentry = statentry;
+ total_stat_t *link_stats = &sentry->tse_stats;
+ total_fields_buf_t *buf;
+
+ buf = malloc(sizeof (total_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->t_linkname, sizeof (buf->t_linkname), "%s",
+ linkname);
+
+ map_to_units(buf->t_ipackets, sizeof (buf->t_ipackets),
+ link_stats->ts_ipackets, unit, parsable);
+
+ map_to_units(buf->t_rbytes, sizeof (buf->t_rbytes),
+ link_stats->ts_rbytes, unit, parsable);
+
+ map_to_units(buf->t_opackets, sizeof (buf->t_opackets),
+ link_stats->ts_opackets, unit, parsable);
+
+ map_to_units(buf->t_obytes, sizeof (buf->t_obytes),
+ link_stats->ts_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_generic_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->r_type, sizeof (buf->r_type), "rx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
+ } else {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->r_packets, sizeof (buf->r_packets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_generic_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->r_linkname, sizeof (buf->r_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->r_type, sizeof (buf->r_type), "tx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index), "--");
+ } else {
+ (void) snprintf(buf->r_index, sizeof (buf->r_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->r_packets, sizeof (buf->r_packets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->r_bytes, sizeof (buf->r_bytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ rx_ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (rx_ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rr_linkname, sizeof (buf->rr_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rr_type, sizeof (buf->rr_type), "rx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rr_index, sizeof (buf->rr_index), "--");
+ } else {
+ (void) snprintf(buf->rr_index, sizeof (buf->rr_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->rr_ipackets, sizeof (buf->rr_ipackets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->rr_rbytes, sizeof (buf->rr_rbytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_ring_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ ring_stat_entry_t *sentry = statentry;
+ ring_stat_t *link_stats = &sentry->re_stats;
+ tx_ring_fields_buf_t *buf;
+
+ buf = malloc(sizeof (tx_ring_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->tr_linkname, sizeof (buf->tr_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->tr_type, sizeof (buf->tr_type), "tx");
+
+ if (sentry->re_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->tr_index, sizeof (buf->tr_index), "--");
+ } else {
+ (void) snprintf(buf->tr_index, sizeof (buf->tr_index),
+ "%llu", sentry->re_index);
+ }
+
+ map_to_units(buf->tr_opackets, sizeof (buf->tr_opackets),
+ link_stats->r_packets, unit, parsable);
+
+ map_to_units(buf->tr_obytes, sizeof (buf->tr_obytes),
+ link_stats->r_bytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_generic_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ rx_lane_stat_entry_t *sentry = statentry;
+ rx_lane_stat_t *link_stats = &sentry->rle_stats;
+ lane_fields_buf_t *buf;
+
+ if (sentry->rle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->l_type, sizeof (buf->l_type), "rx");
+
+ if (sentry->rle_id == L_HWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
+ else if (sentry->rle_id == L_SWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
+ else if (sentry->rle_id == L_LOCAL)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "local");
+ else if (sentry->rle_id == L_BCAST)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
+ else
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
+
+ if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
+ } else {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index),
+ "%llu", sentry->rle_index);
+ }
+
+ map_to_units(buf->l_packets, sizeof (buf->l_packets),
+ link_stats->rl_ipackets, unit, parsable);
+
+ map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
+ link_stats->rl_rbytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_generic_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ tx_lane_stat_entry_t *sentry = statentry;
+ tx_lane_stat_t *link_stats = &sentry->tle_stats;
+ lane_fields_buf_t *buf;
+
+ if (sentry->tle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->l_linkname, sizeof (buf->l_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->l_type, sizeof (buf->l_type), "tx");
+
+ if (sentry->tle_id == L_HWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "hw");
+ else if (sentry->tle_id == L_SWLANE)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "sw");
+ else if (sentry->tle_id == L_BCAST)
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "bcast");
+ else
+ (void) snprintf(buf->l_id, sizeof (buf->l_id), "--");
+
+ if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index), "--");
+ } else {
+ (void) snprintf(buf->l_index, sizeof (buf->l_index),
+ "%llu", sentry->tle_index);
+ }
+ map_to_units(buf->l_packets, sizeof (buf->l_packets),
+ link_stats->tl_opackets, unit, parsable);
+
+ map_to_units(buf->l_bytes, sizeof (buf->l_bytes),
+ link_stats->tl_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_rx_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ rx_lane_stat_entry_t *sentry = statentry;
+ rx_lane_stat_t *link_stats = &sentry->rle_stats;
+ rx_lane_fields_buf_t *buf;
+
+ if (sentry->rle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (rx_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rl_linkname, sizeof (buf->rl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rl_type, sizeof (buf->rl_type), "rx");
+
+ if (sentry->rle_id == L_HWLANE)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "hw");
+ else if (sentry->rle_id == L_SWLANE)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "sw");
+ else if (sentry->rle_id == L_LOCAL)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "local");
+ else if (sentry->rle_id == L_BCAST)
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "bcast");
+ else
+ (void) snprintf(buf->rl_id, sizeof (buf->rl_id), "--");
+
+ if (sentry->rle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rl_index, sizeof (buf->rl_index), "--");
+ } else {
+ (void) snprintf(buf->rl_index, sizeof (buf->rl_index),
+ "%llu", sentry->rle_index);
+ }
+
+ map_to_units(buf->rl_ipackets, sizeof (buf->rl_ipackets),
+ link_stats->rl_ipackets, unit, parsable);
+
+ map_to_units(buf->rl_rbytes, sizeof (buf->rl_rbytes),
+ link_stats->rl_rbytes, unit, parsable);
+
+ map_to_units(buf->rl_intrs, sizeof (buf->rl_intrs),
+ link_stats->rl_intrs, unit, parsable);
+
+ map_to_units(buf->rl_polls, sizeof (buf->rl_polls),
+ link_stats->rl_polls, unit, parsable);
+
+ map_to_units(buf->rl_sdrops, sizeof (buf->rl_sdrops),
+ link_stats->rl_sdrops, unit, parsable);
+
+ map_to_units(buf->rl_chl10, sizeof (buf->rl_chl10),
+ link_stats->rl_chl10, unit, parsable);
+
+ map_to_units(buf->rl_ch10_50, sizeof (buf->rl_ch10_50),
+ link_stats->rl_ch10_50, unit, parsable);
+
+ map_to_units(buf->rl_chg50, sizeof (buf->rl_chg50),
+ link_stats->rl_chg50, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_tx_lane_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ tx_lane_stat_entry_t *sentry = statentry;
+ tx_lane_stat_t *link_stats = &sentry->tle_stats;
+ tx_lane_fields_buf_t *buf = NULL;
+
+ if (sentry->tle_id == L_DFNCT)
+ return (NULL);
+
+ buf = malloc(sizeof (tx_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->tl_linkname, sizeof (buf->tl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->tl_type, sizeof (buf->tl_type), "tx");
+
+ if (sentry->tle_id == L_HWLANE)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "hw");
+ else if (sentry->tle_id == L_SWLANE)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "sw");
+ else if (sentry->tle_id == L_BCAST)
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "bcast");
+ else
+ (void) snprintf(buf->tl_id, sizeof (buf->tl_id), "--");
+
+ if (sentry->tle_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->tl_index, sizeof (buf->tl_index), "--");
+ } else {
+ (void) snprintf(buf->tl_index, sizeof (buf->tl_index),
+ "%llu", sentry->tle_index);
+ }
+
+ map_to_units(buf->tl_opackets, sizeof (buf->tl_opackets),
+ link_stats->tl_opackets, unit, parsable);
+
+ map_to_units(buf->tl_obytes, sizeof (buf->tl_obytes),
+ link_stats->tl_obytes, unit, parsable);
+
+ map_to_units(buf->tl_blockcnt, sizeof (buf->tl_blockcnt),
+ link_stats->tl_blockcnt, unit, parsable);
+
+ map_to_units(buf->tl_unblockcnt, sizeof (buf->tl_unblockcnt),
+ link_stats->tl_unblockcnt, unit, parsable);
+
+ map_to_units(buf->tl_sdrops, sizeof (buf->tl_sdrops),
+ link_stats->tl_sdrops, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_fanout_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ fanout_stat_entry_t *sentry = statentry;
+ fanout_stat_t *link_stats = &sentry->fe_stats;
+ rx_fanout_lane_fields_buf_t *buf;
+
+ buf = malloc(sizeof (rx_fanout_lane_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->rfl_linkname, sizeof (buf->rfl_linkname), "%s",
+ linkname);
+
+ (void) snprintf(buf->rfl_type, sizeof (buf->rfl_type), "rx");
+
+ if (sentry->fe_id == L_HWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "hw");
+ else if (sentry->fe_id == L_SWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "sw");
+ else if (sentry->fe_id == L_LCLSWLANE)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "lcl/sw");
+ else if (sentry->fe_id == L_LOCAL)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "local");
+ else if (sentry->fe_id == L_BCAST)
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "bcast");
+ else
+ (void) snprintf(buf->rfl_id, sizeof (buf->rfl_id), "--");
+
+ if (sentry->fe_index == DLSTAT_INVALID_ENTRY) {
+ (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index), "--");
+ } else {
+ (void) snprintf(buf->rfl_index, sizeof (buf->rfl_index),
+ "%llu", sentry->fe_index);
+ }
+
+ if (sentry->fe_foutindex == DLSTAT_INVALID_ENTRY)
+ (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "--");
+ else {
+ (void) snprintf(buf->rfl_fout, sizeof (buf->rfl_fout), "%llu",
+ sentry->fe_foutindex);
+ }
+
+ map_to_units(buf->rfl_ipackets, sizeof (buf->rfl_ipackets),
+ link_stats->f_ipackets, unit, parsable);
+
+ map_to_units(buf->rfl_rbytes, sizeof (buf->rfl_rbytes),
+ link_stats->f_rbytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+void *
+print_aggr_port_stats(const char *linkname, void *statentry, char unit,
+ boolean_t parsable)
+{
+ aggr_port_stat_entry_t *sentry = statentry;
+ aggr_port_stat_t *link_stats = &sentry->ape_stats;
+ aggr_port_fields_buf_t *buf;
+ char portname[MAXLINKNAMELEN];
+
+ buf = malloc(sizeof (aggr_port_fields_buf_t));
+ if (buf == NULL)
+ goto done;
+
+ (void) snprintf(buf->ap_linkname, sizeof (buf->ap_linkname), "%s",
+ linkname);
+
+ if (dladm_datalink_id2info(handle, sentry->ape_portlinkid, NULL,
+ NULL, NULL, portname, DLPI_LINKNAME_MAX)
+ != DLADM_STATUS_OK) {
+ (void) snprintf(buf->ap_portname,
+ sizeof (buf->ap_portname), "--");
+ } else {
+ (void) snprintf(buf->ap_portname,
+ sizeof (buf->ap_portname), "%s", portname);
+ }
+
+ map_to_units(buf->ap_ipackets, sizeof (buf->ap_ipackets),
+ link_stats->ap_ipackets, unit, parsable);
+
+ map_to_units(buf->ap_rbytes, sizeof (buf->ap_rbytes),
+ link_stats->ap_rbytes, unit, parsable);
+
+ map_to_units(buf->ap_opackets, sizeof (buf->ap_opackets),
+ link_stats->ap_opackets, unit, parsable);
+
+ map_to_units(buf->ap_obytes, sizeof (buf->ap_obytes),
+ link_stats->ap_obytes, unit, parsable);
+
+done:
+ return (buf);
+}
+
+dladm_stat_chain_t *
+query_link_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg,
+ dladm_stat_type_t stattype)
+{
+ link_chain_t *link_node;
+ dladm_stat_chain_t *curr_stat;
+ dladm_stat_chain_t *prev_stat = NULL;
+ dladm_stat_chain_t *diff_stat = NULL;
+
+ /* Get prev iteration stat for this link */
+ link_node = get_link_prev_stat(linkid, arg);
+ if (link_node == NULL)
+ goto done;
+
+ link_node->lc_visited = B_TRUE;
+ prev_stat = link_node->lc_statchain[stattype];
+
+ /* Query library for current stats */
+ curr_stat = dladm_link_stat_query(dh, linkid, stattype);
+ if (curr_stat == NULL)
+ goto done;
+
+ /* current stats - prev iteration stats */
+ diff_stat = dladm_link_stat_diffchain(curr_stat, prev_stat, stattype);
+
+ /* Free prev stats */
+ dladm_link_stat_free(prev_stat);
+
+ /* Prev <- curr stats */
+ link_node->lc_statchain[stattype] = curr_stat;
+
+done:
+ return (diff_stat);
+}
+
+void
+walk_dlstat_stats(show_state_t *state, const char *linkname,
+ dladm_stat_type_t stattype, dladm_stat_chain_t *diff_stat)
+{
+ dladm_stat_chain_t *curr;
+
+ /* Unpack invidual stat entry and call library consumer's callback */
+ for (curr = diff_stat; curr != NULL; curr = curr->dc_next) {
+ void *fields_buf;
+
+ /* Format the raw numbers for printing */
+ fields_buf = state->ls_stats2str[stattype](linkname,
+ curr->dc_statentry, state->ls_unit, state->ls_parsable);
+ /* Print the stats */
+ if (fields_buf != NULL)
+ ofmt_print(state->ls_ofmt, fields_buf);
+ free(fields_buf);
+ }
+}
+
+static int
+show_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+ show_state_t *state = arg;
+ int i;
+ dladm_stat_chain_t *diff_stat;
+ char linkname[DLPI_LINKNAME_MAX];
+
+ if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
+ DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
+ goto done;
+ }
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (state->ls_stattype[i]) {
+ /*
+ * Query library for stats
+ * Stats are returned as chain of raw numbers
+ */
+ diff_stat = query_link_stats(handle, linkid, arg, i);
+ walk_dlstat_stats(state, linkname, i, diff_stat);
+ dladm_link_stat_free(diff_stat);
+ }
+ }
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+void
+show_link_stats(datalink_id_t linkid, show_state_t state, uint32_t interval)
+{
+ for (;;) {
+ if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(show_queried_stats,
+ handle, &state, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ (void) show_queried_stats(handle, linkid, &state);
+ }
+
+ if (interval == 0)
+ break;
+
+ cleanup_removed_links(&state);
+ (void) sleep(interval);
+ }
+}
+
+void
+print_all_stats(dladm_handle_t dh, datalink_id_t linkid,
+ dladm_stat_chain_t *stat_chain)
+{
+ dladm_stat_chain_t *curr;
+ name_value_stat_entry_t *stat_entry;
+ name_value_stat_t *curr_stat;
+ boolean_t stat_printed = B_FALSE;
+ char linkname[MAXLINKNAMELEN];
+ char prev_linkname[MAXLINKNAMELEN];
+
+ if (dladm_datalink_id2info(dh, linkid, NULL, NULL, NULL, linkname,
+ DLPI_LINKNAME_MAX) != DLADM_STATUS_OK)
+ return;
+
+ for (curr = stat_chain; curr != NULL; curr = curr->dc_next) {
+ stat_entry = curr->dc_statentry;
+ /*
+ * Print header
+ * If link name is already printed in previous iteration,
+ * don't print again
+ */
+ if (strcmp(prev_linkname, linkname) != 0)
+ printf("%s \n", linkname);
+ printf(" %s \n", stat_entry->nve_header);
+
+ /* Print stat fields */
+ for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
+ curr_stat = curr_stat->nv_nextstat) {
+ printf("\t%15s", curr_stat->nv_statname);
+ printf("\t\t%15llu\n", curr_stat->nv_statval);
+ }
+
+ strncpy(prev_linkname, linkname, MAXLINKNAMELEN);
+ stat_printed = B_TRUE;
+ }
+ if (stat_printed)
+ printf("---------------------------------------------------\n");
+}
+
+static int
+dump_queried_stats(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+ boolean_t *stattype = arg;
+ int i;
+ dladm_stat_chain_t *stat_chain;
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++) {
+ if (stattype[i]) {
+ stat_chain = dladm_link_stat_query_all(dh, linkid, i);
+ print_all_stats(dh, linkid, stat_chain);
+ dladm_link_stat_query_all_free(stat_chain);
+ }
+ }
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+void
+dump_all_link_stats(datalink_id_t linkid, boolean_t *stattype)
+{
+ if (linkid == DATALINK_ALL_LINKID) {
+ (void) dladm_walk_datalink_id(dump_queried_stats,
+ handle, stattype, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ } else {
+ (void) dump_queried_stats(handle, linkid, stattype);
+ }
+}
+
+static void
+do_show(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ boolean_t A_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *total_stat_fields =
+ "link,ipkts,rbytes,opkts,obytes";
+ char *rx_total_stat_fields =
+ "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
+ char *tx_total_stat_fields =
+ "link,opkts,obytes,blkcnt,ublkcnt";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtaApi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'A':
+ if (A_arg)
+ die_optdup(option);
+
+ A_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg && A_arg)
+ die("the options -a and -A are not compatible");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+ if (A_arg &&
+ (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -A is not compatible with "
+ "-r, -t, -p, -o, -u, -i");
+ }
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ if (argc != 0)
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+ if (r_arg) {
+ stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_TOTAL] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ if (A_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+ int i;
+
+ for (i = 0; i < DLADM_STAT_NUM_STATS; i++)
+ stattype[i] = B_TRUE;
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ fields_str = rx_total_stat_fields;
+ oftemplate = rx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE_TOTAL] =
+ print_rx_lane_stats;
+ } else if (t_arg) {
+ fields_str = tx_total_stat_fields;
+ oftemplate = tx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_LANE_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_LANE_TOTAL] =
+ print_tx_lane_stats;
+ } else { /* Display both Rx and Tx lanes total */
+ fields_str = total_stat_fields;
+ oftemplate = total_s_fields;
+ state.ls_stattype[DLADM_STAT_TOTAL] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TOTAL] = print_total_stats;
+ }
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_phys(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ char linkname[MAXLINKNAMELEN];
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+ char *ring_stat_fields =
+ "link,type,index,pkts,bytes";
+ char *rx_ring_stat_fields =
+ "link,type,index,ipkts,rbytes";
+ char *tx_ring_stat_fields =
+ "link,type,index,opkts,obytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtapi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+
+ if (r_arg) {
+ stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ fields_str = rx_ring_stat_fields;
+ oftemplate = rx_ring_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_RING] = print_rx_ring_stats;
+ } else if (t_arg) {
+ fields_str = tx_ring_stat_fields;
+ oftemplate = tx_ring_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_RING] = print_tx_ring_stats;
+ } else { /* Display both Rx and Tx lanes */
+ fields_str = ring_stat_fields;
+ oftemplate = ring_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_RING] = B_TRUE;
+ state.ls_stattype[DLADM_STAT_TX_RING] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_RING] =
+ print_rx_generic_ring_stats;
+ state.ls_stats2str[DLADM_STAT_TX_RING] =
+ print_tx_generic_ring_stats;
+ }
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_link(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t F_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t a_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *lane_stat_fields =
+ "link,type,id,index,pkts,bytes";
+ char *rx_lane_stat_fields =
+ "link,type,id,index,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50";
+ char *tx_lane_stat_fields =
+ "link,type,id,index,opkts,obytes,blkcnt,ublkcnt";
+ char *rx_fanout_stat_fields =
+ "link,id,index,fout,ipkts,rbytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":hrtFapi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'h':
+ if (r_arg || F_arg || t_arg || i_arg || p_arg ||
+ o_arg || u_arg || a_arg) {
+ die("the option -h is not compatible with "
+ "-r, -F, -t, -i, -p, -o, -u, -a");
+ }
+ do_show_history(argc, &argv[0], use);
+ return;
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 'F':
+ if (F_arg)
+ die_optdup(option);
+
+ F_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'a':
+ if (a_arg)
+ die_optdup(option);
+
+ a_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (F_arg && !r_arg)
+ die("-F must be used with -r");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (a_arg &&
+ (p_arg || o_arg || u_arg || i_arg)) {
+ die("the option -a is not compatible with "
+ "-p, -o, -u, -i");
+ }
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ if (a_arg) {
+ boolean_t stattype[DLADM_STAT_NUM_STATS];
+
+ bzero(&stattype, sizeof (stattype));
+
+ if (r_arg) {
+ if (F_arg) {
+ stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
+ } else {
+ stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ }
+ } else if (t_arg) {
+ stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ } else { /* Display both Rx and Tx lanes */
+ stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ }
+
+ dump_all_link_stats(linkid, stattype);
+ return;
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg) {
+ if (F_arg) {
+ fields_str = rx_fanout_stat_fields;
+ oftemplate = rx_fanout_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE_FOUT] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE_FOUT] =
+ print_fanout_stats;
+ } else {
+ fields_str = rx_lane_stat_fields;
+ oftemplate = rx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE] =
+ print_rx_lane_stats;
+ }
+ } else if (t_arg) {
+ fields_str = tx_lane_stat_fields;
+ oftemplate = tx_lane_s_fields;
+ state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_TX_LANE] = print_tx_lane_stats;
+ } else { /* Display both Rx and Tx lanes */
+ fields_str = lane_stat_fields;
+ oftemplate = lane_s_fields;
+ state.ls_stattype[DLADM_STAT_RX_LANE] = B_TRUE;
+ state.ls_stattype[DLADM_STAT_TX_LANE] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_RX_LANE] =
+ print_rx_generic_lane_stats;
+ state.ls_stats2str[DLADM_STAT_TX_LANE] =
+ print_tx_generic_lane_stats;
+ }
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+static void
+do_show_aggr(int argc, char *argv[], const char *use)
+{
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ uint32_t flags = DLADM_OPT_ACTIVE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_state_t state;
+ dladm_status_t status;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *aggr_stat_fields =
+ "link,port,ipkts,rbytes,opkts,obytes";
+ char *rx_aggr_stat_fields = "link,port,ipkts,rbytes";
+ char *tx_aggr_stat_fields = "link,port,opkts,obytes";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+ ofmt_field_t *oftemplate;
+
+ bzero(&state, sizeof (state));
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtpi:o:u:",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!dlstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ default:
+ die_opterr(optopt, option, use);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the options -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the options -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+
+ /* get link name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlen(argv[optind]) >= MAXLINKNAMELEN)
+ die("link name too long");
+
+ if ((status = dladm_name2info(handle, argv[optind], &linkid,
+ NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+ die_dlerr(status, "link %s is not valid", argv[optind]);
+ }
+ } else if (optind != argc) {
+ usage();
+ }
+
+ state.ls_unit = unit;
+ state.ls_parsable = p_arg;
+
+ if (state.ls_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ oftemplate = aggr_port_s_fields;
+ state.ls_stattype[DLADM_STAT_AGGR_PORT] = B_TRUE;
+ state.ls_stats2str[DLADM_STAT_AGGR_PORT] = print_aggr_port_stats;
+
+ if (r_arg)
+ fields_str = rx_aggr_stat_fields;
+ else if (t_arg)
+ fields_str = tx_aggr_stat_fields;
+ else
+ fields_str = aggr_stat_fields;
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, oftemplate, ofmtflags, 0, &ofmt);
+ dlstat_ofmt_check(oferr, state.ls_parsable, ofmt);
+ state.ls_ofmt = ofmt;
+
+ show_link_stats(linkid, state, interval);
+
+ ofmt_close(ofmt);
+}
+
+/* PRINTFLIKE1 */
+static void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: warning: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+}
+
+/*
+ * Also closes the dladm handle if it is not NULL.
+ */
+/* PRINTFLIKE2 */
+static void
+die_dlerr(dladm_status_t err, const char *format, ...)
+{
+ va_list alist;
+ char errmsg[DLADM_STRSIZE];
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+/* PRINTFLIKE1 */
+static void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+die_optdup(int opt)
+{
+ die("the option -%c cannot be specified more than once", opt);
+}
+
+static void
+die_opterr(int opt, int opterr, const char *usage)
+{
+ switch (opterr) {
+ case ':':
+ die("option '-%c' requires a value\nusage: %s", opt,
+ gettext(usage));
+ break;
+ case '?':
+ default:
+ die("unrecognized option '-%c'\nusage: %s", opt,
+ gettext(usage));
+ break;
+ }
+}
+
+/*
+ * default output callback function that, when invoked,
+ * prints string which is offset by ofmt_arg->ofmt_id within buf.
+ */
+static boolean_t
+print_default_cb(ofmt_arg_t *ofarg, char *buf, uint_t bufsize)
+{
+ char *value;
+
+ value = (char *)ofarg->ofmt_cbarg + ofarg->ofmt_id;
+ (void) strlcpy(buf, value, bufsize);
+ return (B_TRUE);
+}
+
+static void
+dlstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
+ ofmt_handle_t ofmt)
+{
+ char buf[OFMT_BUFSIZE];
+
+ if (oferr == OFMT_SUCCESS)
+ return;
+ (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
+ /*
+ * All errors are considered fatal in parsable mode.
+ * NOMEM errors are always fatal, regardless of mode.
+ * For other errors, we print diagnostics in human-readable
+ * mode and processs what we can.
+ */
+ if (parsable || oferr == OFMT_ENOFIELDS) {
+ ofmt_close(ofmt);
+ die(buf);
+ } else {
+ warn(buf);
+ }
+}
diff --git a/usr/src/cmd/dlstat/dlstat.xcl b/usr/src/cmd/dlstat/dlstat.xcl
new file mode 100644
index 0000000000..bc201f606f
--- /dev/null
+++ b/usr/src/cmd/dlstat/dlstat.xcl
@@ -0,0 +1,110 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+msgid " %s \n"
+msgid " %.2f"
+msgid " %6.2lf%c"
+msgid " %7.0lf%c"
+msgid " %s"
+msgid "---------------------------------------------------\n"
+msgid "--"
+msgid ": %s\n"
+msgid ":hrtFapi:o:u:"
+msgid ":rtaApi:o:u:"
+msgid ":rtapi:o:u:"
+msgid ":rtpi:o:u:"
+msgid ""
+msgid "\n"
+msgid "\t\t%15llu\n"
+msgid "\t%15s"
+msgid "# Time"
+msgid "%.0lf"
+msgid "%llu"
+msgid "%m/%d/%Y"
+msgid "%s \n"
+msgid "%s Mbps"
+msgid "%s: "
+msgid "%s: warning: "
+msgid "%s"
+msgid "%s\n"
+msgid "%T"
+msgid "all"
+msgid "B"
+msgid "BANDWIDTH"
+msgid "bcast"
+msgid "BLKCNT"
+msgid "BYTES"
+msgid "CH<10"
+msgid "CH>50"
+msgid "CH10-50"
+msgid "das:e:o:f:F:"
+msgid "DURATION"
+msgid "END"
+msgid "FOUT"
+msgid "G"
+msgid "gnuplot"
+msgid "hw"
+msgid "ID"
+msgid "INDEX"
+msgid "INTRS"
+msgid "IPKTS"
+msgid "K"
+msgid "lcl/sw"
+msgid "link,id,index,fout,ipkts,rbytes"
+msgid "link,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50"
+msgid "link,ipkts,rbytes,opkts,obytes"
+msgid "link,opkts,obytes,blkcnt,ublkcnt"
+msgid "link,port,ipkts,rbytes,opkts,obytes"
+msgid "link,port,ipkts,rbytes"
+msgid "link,port,opkts,obytes"
+msgid "link,start,end,rbytes,obytes,bandwidth"
+msgid "link,type,id,index,ipkts,rbytes,intrs,polls,ch<10,ch10-50,ch>50"
+msgid "link,type,id,index,opkts,obytes,blkcnt,ublkcnt"
+msgid "link,type,id,index,pkts,bytes"
+msgid "link,type,index,ipkts,rbytes"
+msgid "link,type,index,opkts,obytes"
+msgid "link,type,index,pkts,bytes"
+msgid "LINK"
+msgid "local"
+msgid "M"
+msgid "OBYTES"
+msgid "OPKTS"
+msgid "P"
+msgid "PKTS"
+msgid "POLLS"
+msgid "PORT"
+msgid "RBYTES"
+msgid "rx"
+msgid "SDROPS"
+msgid "show-aggr"
+msgid "show-link -h"
+msgid "show-link"
+msgid "show-phys"
+msgid "START"
+msgid "sw"
+msgid "T"
+msgid "tx"
+msgid "TYPE"
+msgid "UBLKCNT"
diff --git a/usr/src/cmd/flowadm/Makefile b/usr/src/cmd/flowadm/Makefile
index aa057c1f2b..19a15a1b47 100644
--- a/usr/src/cmd/flowadm/Makefile
+++ b/usr/src/cmd/flowadm/Makefile
@@ -19,7 +19,7 @@
# CDDL HEADER END
#
#
-# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
@@ -34,7 +34,7 @@ include ../Makefile.cmd
XGETFLAGS += -a -x $(PROG).xcl
LDLIBS += -L$(ROOT)/lib
-LDLIBS += -ldladm -lkstat -linetutil
+LDLIBS += -ldladm -linetutil
ROOTCFGDIR= $(ROOTETC)/dladm
ROOTCFGFILES= $(CONFIGFILES:%=$(ROOTCFGDIR)/%)
diff --git a/usr/src/cmd/flowadm/flowadm.c b/usr/src/cmd/flowadm/flowadm.c
index 2950adcf48..374fa1675c 100644
--- a/usr/src/cmd/flowadm/flowadm.c
+++ b/usr/src/cmd/flowadm/flowadm.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -31,7 +31,6 @@
#include <string.h>
#include <stropts.h>
#include <errno.h>
-#include <kstat.h>
#include <strings.h>
#include <getopt.h>
#include <unistd.h>
@@ -51,45 +50,22 @@
#include <stddef.h>
#include <ofmt.h>
-typedef struct show_usage_state_s {
- boolean_t us_plot;
- boolean_t us_parsable;
- boolean_t us_printheader;
- boolean_t us_first;
- boolean_t us_showall;
- ofmt_handle_t us_ofmt;
-} show_usage_state_t;
-
typedef struct show_flow_state {
- boolean_t fs_firstonly;
- boolean_t fs_donefirst;
- pktsum_t fs_prevstats;
- uint32_t fs_flags;
dladm_status_t fs_status;
ofmt_handle_t fs_ofmt;
const char *fs_flow;
- const char *fs_link;
boolean_t fs_parsable;
boolean_t fs_persist;
- boolean_t fs_stats;
- uint64_t fs_mask;
} show_flow_state_t;
typedef void cmdfunc_t(int, char **);
static cmdfunc_t do_add_flow, do_remove_flow, do_init_flow, do_show_flow;
static cmdfunc_t do_show_flowprop, do_set_flowprop, do_reset_flowprop;
-static cmdfunc_t do_show_usage;
static int show_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
static int show_flows_onelink(dladm_handle_t, datalink_id_t, void *);
-static void flow_stats(const char *, datalink_id_t, uint_t, char *,
- show_flow_state_t *);
-static void get_flow_stats(const char *, pktsum_t *);
-static int show_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
-static int show_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
-
static int remove_flow(dladm_handle_t, dladm_flow_attr_t *, void *);
static int show_flowprop(dladm_handle_t, dladm_flow_attr_t *, void *);
@@ -104,7 +80,7 @@ static void warn(const char *, ...);
static void warn_dlerr(dladm_status_t, const char *, ...);
/* callback functions for printing output */
-static ofmt_cb_t print_flowprop_cb, print_default_cb, print_flow_stats_cb;
+static ofmt_cb_t print_flowprop_cb, print_default_cb;
static void flowadm_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
typedef struct cmd {
@@ -120,15 +96,12 @@ static cmd_t cmds[] = {
{ "reset-flowprop", do_reset_flowprop },
{ "show-flow", do_show_flow },
{ "init-flow", do_init_flow },
- { "show-usage", do_show_usage }
};
static const struct option longopts[] = {
{"link", required_argument, 0, 'l'},
{"parsable", no_argument, 0, 'p'},
{"parseable", no_argument, 0, 'p'},
- {"statistics", no_argument, 0, 's'},
- {"interval", required_argument, 0, 'i'},
{"temporary", no_argument, 0, 't'},
{"root-dir", required_argument, 0, 'R'},
{ 0, 0, 0, 0 }
@@ -236,104 +209,6 @@ typedef struct flowprop_args_s {
char *fs_propname;
char *fs_flowname;
} flowprop_args_t;
-/*
- * structures for 'flowadm show-flow -s' (print statistics)
- */
-typedef enum {
- FLOW_S_FLOW,
- FLOW_S_IPKTS,
- FLOW_S_RBYTES,
- FLOW_S_IERRORS,
- FLOW_S_OPKTS,
- FLOW_S_OBYTES,
- FLOW_S_OERRORS
-} flow_s_field_index_t;
-
-static ofmt_field_t flow_s_fields[] = {
-/* name, field width, index, callback */
-{ "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb},
-{ "IPACKETS", 10, FLOW_S_IPKTS, print_flow_stats_cb},
-{ "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb},
-{ "IERRORS", 10, FLOW_S_IERRORS, print_flow_stats_cb},
-{ "OPACKETS", 12, FLOW_S_OPKTS, print_flow_stats_cb},
-{ "OBYTES", 12, FLOW_S_OBYTES, print_flow_stats_cb},
-{ "OERRORS", 8, FLOW_S_OERRORS, print_flow_stats_cb},
-NULL_OFMT}
-;
-
-typedef struct flow_args_s {
- char *flow_s_flow;
- pktsum_t *flow_s_psum;
-} flow_args_t;
-
-/*
- * structures for 'flowadm show-usage'
- */
-typedef struct usage_fields_buf_s {
- char usage_flow[12];
- char usage_duration[10];
- char usage_ipackets[9];
- char usage_rbytes[10];
- char usage_opackets[9];
- char usage_obytes[10];
- char usage_bandwidth[14];
-} usage_fields_buf_t;
-
-static ofmt_field_t usage_fields[] = {
-/* name, field width, offset */
-{ "FLOW", 13,
- offsetof(usage_fields_buf_t, usage_flow), print_default_cb},
-{ "DURATION", 11,
- offsetof(usage_fields_buf_t, usage_duration), print_default_cb},
-{ "IPACKETS", 10,
- offsetof(usage_fields_buf_t, usage_ipackets), print_default_cb},
-{ "RBYTES", 11,
- offsetof(usage_fields_buf_t, usage_rbytes), print_default_cb},
-{ "OPACKETS", 10,
- offsetof(usage_fields_buf_t, usage_opackets), print_default_cb},
-{ "OBYTES", 11,
- offsetof(usage_fields_buf_t, usage_obytes), print_default_cb},
-{ "BANDWIDTH", 15,
- offsetof(usage_fields_buf_t, usage_bandwidth), print_default_cb},
-NULL_OFMT}
-;
-
-/*
- * structures for 'dladm show-usage link'
- */
-
-typedef struct usage_l_fields_buf_s {
- char usage_l_flow[12];
- char usage_l_stime[13];
- char usage_l_etime[13];
- char usage_l_rbytes[8];
- char usage_l_obytes[8];
- char usage_l_bandwidth[14];
-} usage_l_fields_buf_t;
-
-static ofmt_field_t usage_l_fields[] = {
-/* name, field width, offset */
-{ "FLOW", 13,
- offsetof(usage_l_fields_buf_t, usage_l_flow), print_default_cb},
-{ "START", 14,
- offsetof(usage_l_fields_buf_t, usage_l_stime), print_default_cb},
-{ "END", 14,
- offsetof(usage_l_fields_buf_t, usage_l_etime), print_default_cb},
-{ "RBYTES", 9,
- offsetof(usage_l_fields_buf_t, usage_l_rbytes), print_default_cb},
-{ "OBYTES", 9,
- offsetof(usage_l_fields_buf_t, usage_l_obytes), print_default_cb},
-{ "BANDWIDTH", 15,
- offsetof(usage_l_fields_buf_t, usage_l_bandwidth), print_default_cb},
-NULL_OFMT}
-;
-
-#define PRI_HI 100
-#define PRI_LO 10
-#define PRI_NORM 50
-
-#define FLOWADM_CONF "/etc/dladm/flowadm.conf"
-#define BLANK_LINE(s) ((s[0] == '\0') || (s[0] == '#') || (s[0] == '\n'))
static char *progname;
@@ -360,15 +235,12 @@ usage(void)
" add-flow [-t] -l <link> -a <attr>=<value>[,...]\n"
"\t\t [-p <prop>=<value>,...] <flow>\n"
" remove-flow [-t] {-l <link> | <flow>}\n"
- " show-flow [-p] [-s [-i <interval>]] [-l <link>] "
+ " show-flow [-p] [-l <link>] "
"[<flow>]\n\n"
" set-flowprop [-t] -p <prop>=<value>[,...] <flow>\n"
" reset-flowprop [-t] [-p <prop>,...] <flow>\n"
" show-flowprop [-cP] [-l <link>] [-p <prop>,...] "
- "[<flow>]\n\n"
- " show-usage [-a] [-d | -F <format>] "
- "[-s <DD/MM/YYYY,HH:MM:SS>]\n"
- "\t\t [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> [<flow>]\n"));
+ "[<flow>]\n"));
/* close dladm handle if it was opened */
if (handle != NULL)
@@ -446,275 +318,6 @@ do_init_flow(int argc, char *argv[])
die_dlerr(status, "flows initialization failed");
}
-/* ARGSUSED */
-static int
-show_usage_date(dladm_usage_t *usage, void *arg)
-{
- show_usage_state_t *state = (show_usage_state_t *)arg;
- time_t stime;
- char timebuf[20];
- dladm_flow_attr_t attr;
- dladm_status_t status;
-
- /*
- * Only show usage information for existing flows unless '-a'
- * is specified.
- */
- if (!state->us_showall && ((status = dladm_flow_info(handle,
- usage->du_name, &attr)) != DLADM_STATUS_OK)) {
- return (status);
- }
-
- stime = usage->du_stime;
- (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
- localtime(&stime));
- (void) printf("%s\n", timebuf);
-
- return (DLADM_STATUS_OK);
-}
-
-static int
-show_usage_time(dladm_usage_t *usage, void *arg)
-{
- show_usage_state_t *state = (show_usage_state_t *)arg;
- char buf[DLADM_STRSIZE];
- usage_l_fields_buf_t ubuf;
- time_t time;
- double bw;
- dladm_flow_attr_t attr;
- dladm_status_t status;
-
- /*
- * Only show usage information for existing flows unless '-a'
- * is specified.
- */
- if (!state->us_showall && ((status = dladm_flow_info(handle,
- usage->du_name, &attr)) != DLADM_STATUS_OK)) {
- return (status);
- }
-
- if (state->us_plot) {
- if (!state->us_printheader) {
- if (state->us_first) {
- (void) printf("# Time");
- state->us_first = B_FALSE;
- }
- (void) printf(" %s", usage->du_name);
- if (usage->du_last) {
- (void) printf("\n");
- state->us_first = B_TRUE;
- state->us_printheader = B_TRUE;
- }
- } else {
- if (state->us_first) {
- time = usage->du_etime;
- (void) strftime(buf, sizeof (buf), "%T",
- localtime(&time));
- state->us_first = B_FALSE;
- (void) printf("%s", buf);
- }
- bw = (double)usage->du_bandwidth/1000;
- (void) printf(" %.2f", bw);
- if (usage->du_last) {
- (void) printf("\n");
- state->us_first = B_TRUE;
- }
- }
- return (DLADM_STATUS_OK);
- }
-
- bzero(&ubuf, sizeof (ubuf));
-
- (void) snprintf(ubuf.usage_l_flow, sizeof (ubuf.usage_l_flow), "%s",
- usage->du_name);
- time = usage->du_stime;
- (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
- (void) snprintf(ubuf.usage_l_stime, sizeof (ubuf.usage_l_stime), "%s",
- buf);
- time = usage->du_etime;
- (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
- (void) snprintf(ubuf.usage_l_etime, sizeof (ubuf.usage_l_etime), "%s",
- buf);
- (void) snprintf(ubuf.usage_l_rbytes, sizeof (ubuf.usage_l_rbytes),
- "%llu", usage->du_rbytes);
- (void) snprintf(ubuf.usage_l_obytes, sizeof (ubuf.usage_l_obytes),
- "%llu", usage->du_obytes);
- (void) snprintf(ubuf.usage_l_bandwidth, sizeof (ubuf.usage_l_bandwidth),
- "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
-
- ofmt_print(state->us_ofmt, (void *)&ubuf);
- return (DLADM_STATUS_OK);
-}
-
-static int
-show_usage_res(dladm_usage_t *usage, void *arg)
-{
- show_usage_state_t *state = (show_usage_state_t *)arg;
- char buf[DLADM_STRSIZE];
- usage_fields_buf_t ubuf;
- dladm_flow_attr_t attr;
- dladm_status_t status;
-
- /*
- * Only show usage information for existing flows unless '-a'
- * is specified.
- */
- if (!state->us_showall && ((status = dladm_flow_info(handle,
- usage->du_name, &attr)) != DLADM_STATUS_OK)) {
- return (status);
- }
-
- bzero(&ubuf, sizeof (ubuf));
-
- (void) snprintf(ubuf.usage_flow, sizeof (ubuf.usage_flow), "%s",
- usage->du_name);
- (void) snprintf(ubuf.usage_duration, sizeof (ubuf.usage_duration),
- "%llu", usage->du_duration);
- (void) snprintf(ubuf.usage_ipackets, sizeof (ubuf.usage_ipackets),
- "%llu", usage->du_ipackets);
- (void) snprintf(ubuf.usage_rbytes, sizeof (ubuf.usage_rbytes),
- "%llu", usage->du_rbytes);
- (void) snprintf(ubuf.usage_opackets, sizeof (ubuf.usage_opackets),
- "%llu", usage->du_opackets);
- (void) snprintf(ubuf.usage_obytes, sizeof (ubuf.usage_obytes),
- "%llu", usage->du_obytes);
- (void) snprintf(ubuf.usage_bandwidth, sizeof (ubuf.usage_bandwidth),
- "%s Mbps", dladm_bw2str(usage->du_bandwidth, buf));
-
- ofmt_print(state->us_ofmt, (void *)&ubuf);
-
- return (DLADM_STATUS_OK);
-}
-
-static boolean_t
-valid_formatspec(char *formatspec_str)
-{
- if (strcmp(formatspec_str, "gnuplot") == 0)
- return (B_TRUE);
- return (B_FALSE);
-}
-
-/* ARGSUSED */
-static void
-do_show_usage(int argc, char *argv[])
-{
- char *file = NULL;
- int opt;
- dladm_status_t status;
- boolean_t d_arg = B_FALSE;
- char *stime = NULL;
- char *etime = NULL;
- char *resource = NULL;
- show_usage_state_t state;
- boolean_t o_arg = B_FALSE;
- boolean_t F_arg = B_FALSE;
- char *fields_str = NULL;
- char *formatspec_str = NULL;
- char *all_fields =
- "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
- char *all_l_fields =
- "flow,start,end,rbytes,obytes,bandwidth";
- ofmt_handle_t ofmt;
- ofmt_status_t oferr;
- uint_t ofmtflags = 0;
-
- bzero(&state, sizeof (show_usage_state_t));
- state.us_parsable = B_FALSE;
- state.us_printheader = B_FALSE;
- state.us_plot = B_FALSE;
- state.us_first = B_TRUE;
-
- while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
- switch (opt) {
- case 'd':
- d_arg = B_TRUE;
- break;
- case 'a':
- state.us_showall = B_TRUE;
- break;
- case 'f':
- file = optarg;
- break;
- case 's':
- stime = optarg;
- break;
- case 'e':
- etime = optarg;
- break;
- case 'o':
- o_arg = B_TRUE;
- fields_str = optarg;
- break;
- case 'F':
- state.us_plot = F_arg = B_TRUE;
- formatspec_str = optarg;
- break;
- default:
- die_opterr(optopt, opt);
- }
- }
-
- if (file == NULL)
- die("show-usage requires a file");
-
- if (optind == (argc-1)) {
- dladm_flow_attr_t attr;
-
- if (!state.us_showall &&
- dladm_flow_info(handle, resource, &attr) !=
- DLADM_STATUS_OK) {
- die("invalid flow: '%s'", resource);
- }
- resource = argv[optind];
- }
-
- if (state.us_parsable)
- ofmtflags |= OFMT_PARSABLE;
- if (resource == NULL && stime == NULL && etime == NULL) {
- if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
- fields_str = all_fields;
- oferr = ofmt_open(fields_str, usage_fields, ofmtflags,
- 0, &ofmt);
- } else {
- if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
- fields_str = all_l_fields;
- oferr = ofmt_open(fields_str, usage_l_fields, ofmtflags,
- 0, &ofmt);
- }
-
- flowadm_ofmt_check(oferr, state.us_parsable, ofmt);
- state.us_ofmt = ofmt;
-
- if (F_arg && d_arg)
- die("incompatible -d and -F options");
-
- if (F_arg && valid_formatspec(formatspec_str) == B_FALSE)
- die("Format specifier %s not supported", formatspec_str);
-
- if (d_arg) {
- /* Print log dates */
- status = dladm_usage_dates(show_usage_date,
- DLADM_LOGTYPE_FLOW, file, resource, &state);
- } else if (resource == NULL && stime == NULL && etime == NULL &&
- !F_arg) {
- /* Print summary */
- status = dladm_usage_summary(show_usage_res,
- DLADM_LOGTYPE_FLOW, file, &state);
- } else if (resource != NULL) {
- /* Print log entries for named resource */
- status = dladm_walk_usage_res(show_usage_time,
- DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
- } else {
- /* Print time and information for each link */
- status = dladm_walk_usage_time(show_usage_time,
- DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
- }
-
- ofmt_close(ofmt);
- if (status != DLADM_STATUS_OK)
- die_dlerr(status, "show-usage");
-}
-
static void
do_add_flow(int argc, char *argv[])
{
@@ -981,176 +584,14 @@ show_flows_onelink(dladm_handle_t dh, datalink_id_t linkid, void *arg)
}
static void
-get_flow_stats(const char *flowname, pktsum_t *stats)
-{
- kstat_ctl_t *kcp;
- kstat_t *ksp;
-
- bzero(stats, sizeof (*stats));
-
- if ((kcp = kstat_open()) == NULL) {
- warn("kstat open operation failed");
- return;
- }
-
- ksp = dladm_kstat_lookup(kcp, NULL, -1, flowname, "flow");
-
- if (ksp != NULL)
- dladm_get_stats(kcp, ksp, stats);
-
- (void) kstat_close(kcp);
-}
-
-static boolean_t
-print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
-{
- flow_args_t *fargs = of_arg->ofmt_cbarg;
- pktsum_t *diff_stats = fargs->flow_s_psum;
-
- switch (of_arg->ofmt_id) {
- case FLOW_S_FLOW:
- (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
- break;
- case FLOW_S_IPKTS:
- (void) snprintf(buf, bufsize, "%llu",
- diff_stats->ipackets);
- break;
- case FLOW_S_RBYTES:
- (void) snprintf(buf, bufsize, "%llu",
- diff_stats->rbytes);
- break;
- case FLOW_S_IERRORS:
- (void) snprintf(buf, bufsize, "%u",
- diff_stats->ierrors);
- break;
- case FLOW_S_OPKTS:
- (void) snprintf(buf, bufsize, "%llu",
- diff_stats->opackets);
- break;
- case FLOW_S_OBYTES:
- (void) snprintf(buf, bufsize, "%llu",
- diff_stats->obytes);
- break;
- case FLOW_S_OERRORS:
- (void) snprintf(buf, bufsize, "%u",
- diff_stats->oerrors);
- break;
- default:
- die("invalid input");
- break;
- }
- return (B_TRUE);
-}
-
-/* ARGSUSED */
-static int
-show_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
-{
- show_flow_state_t *state = (show_flow_state_t *)arg;
- char *name = attr->fa_flowname;
- pktsum_t stats, diff_stats;
- flow_args_t fargs;
-
- if (state->fs_firstonly) {
- if (state->fs_donefirst)
- return (DLADM_WALK_TERMINATE);
- state->fs_donefirst = B_TRUE;
- } else {
- bzero(&state->fs_prevstats, sizeof (state->fs_prevstats));
- }
-
- get_flow_stats(name, &stats);
- dladm_stats_diff(&diff_stats, &stats, &state->fs_prevstats);
-
- fargs.flow_s_flow = name;
- fargs.flow_s_psum = &diff_stats;
- ofmt_print(state->fs_ofmt, (void *)&fargs);
- state->fs_prevstats = stats;
-
- return (DLADM_WALK_CONTINUE);
-}
-
-/*
- * Wrapper of dladm_walk_flow(show_flow,...) to make it usable for
- * dladm_walk_datalink_id(). Used for showing flow stats for
- * all flows on all links.
- */
-static int
-show_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
-{
- if (dladm_walk_flow(show_flow_stats, dh, linkid, arg, B_FALSE)
- == DLADM_STATUS_OK)
- return (DLADM_WALK_CONTINUE);
- else
- return (DLADM_WALK_TERMINATE);
-}
-
-/* ARGSUSED */
-static void
-flow_stats(const char *flow, datalink_id_t linkid, uint_t interval,
- char *fields_str, show_flow_state_t *state)
-{
- dladm_flow_attr_t attr;
- ofmt_handle_t ofmt;
- ofmt_status_t oferr;
- uint_t ofmtflags = 0;
-
- oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
- flowadm_ofmt_check(oferr, state->fs_parsable, ofmt);
- state->fs_ofmt = ofmt;
-
- if (flow != NULL &&
- dladm_flow_info(handle, flow, &attr) != DLADM_STATUS_OK)
- die("invalid flow %s", flow);
-
- /*
- * If an interval is specified, continuously show the stats
- * for only the first flow.
- */
- state->fs_firstonly = (interval != 0);
-
- for (;;) {
- state->fs_donefirst = B_FALSE;
-
- /* Show stats for named flow */
- if (flow != NULL) {
- state->fs_flow = flow;
- (void) show_flow_stats(handle, &attr, state);
-
- /* Show all stats on a link */
- } else if (linkid != DATALINK_INVALID_LINKID) {
- (void) dladm_walk_flow(show_flow_stats, handle, linkid,
- state, B_FALSE);
-
- /* Show all stats by datalink */
- } else {
- (void) dladm_walk_datalink_id(show_link_flow_stats,
- handle, state, DATALINK_CLASS_ALL,
- DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
- }
-
- if (interval == 0)
- break;
-
- (void) fflush(stdout);
- (void) sleep(interval);
- }
- ofmt_close(ofmt);
-}
-
-static void
do_show_flow(int argc, char *argv[])
{
char flowname[MAXFLOWNAMELEN];
char linkname[MAXLINKNAMELEN];
datalink_id_t linkid = DATALINK_ALL_LINKID;
int option;
- boolean_t s_arg = B_FALSE;
- boolean_t S_arg = B_FALSE;
- boolean_t i_arg = B_FALSE;
boolean_t l_arg = B_FALSE;
boolean_t o_arg = B_FALSE;
- uint32_t interval = 0;
show_flow_state_t state;
char *fields_str = NULL;
ofmt_handle_t ofmt;
@@ -1160,7 +601,7 @@ do_show_flow(int argc, char *argv[])
bzero(&state, sizeof (state));
opterr = 0;
- while ((option = getopt_long(argc, argv, ":pPsSi:l:o:",
+ while ((option = getopt_long(argc, argv, ":pPl:o:",
longopts, NULL)) != -1) {
switch (option) {
case 'p':
@@ -1170,18 +611,6 @@ do_show_flow(int argc, char *argv[])
case 'P':
state.fs_persist = B_TRUE;
break;
- case 's':
- if (s_arg)
- die_optdup(option);
-
- s_arg = B_TRUE;
- break;
- case 'S':
- if (S_arg)
- die_optdup(option);
-
- S_arg = B_TRUE;
- break;
case 'o':
if (o_arg)
die_optdup(option);
@@ -1189,15 +618,6 @@ do_show_flow(int argc, char *argv[])
o_arg = B_TRUE;
fields_str = optarg;
break;
- case 'i':
- if (i_arg)
- die_optdup(option);
-
- i_arg = B_TRUE;
-
- if (!dladm_str2interval(optarg, &interval))
- die("invalid interval value '%s'", optarg);
- break;
case 'l':
if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
>= MAXLINKNAMELEN)
@@ -1212,11 +632,6 @@ do_show_flow(int argc, char *argv[])
break;
}
}
- if (i_arg && !(s_arg || S_arg))
- die("the -i option can be used only with -s or -S");
-
- if (s_arg && S_arg)
- die("the -s option cannot be used with -S");
/* get flow name (optional last argument */
if (optind == (argc-1)) {
@@ -1226,17 +641,6 @@ do_show_flow(int argc, char *argv[])
state.fs_flow = flowname;
}
- if (S_arg) {
- dladm_continuous(handle, linkid, state.fs_flow, interval,
- FLOW_REPORT);
- return;
- }
-
- if (s_arg) {
- flow_stats(state.fs_flow, linkid, interval, fields_str, &state);
- return;
- }
-
oferr = ofmt_open(fields_str, flow_fields, ofmtflags, 0, &ofmt);
flowadm_ofmt_check(oferr, state.fs_parsable, ofmt);
state.fs_ofmt = ofmt;
@@ -1471,7 +875,7 @@ warn(const char *format, ...)
(void) vfprintf(stderr, format, alist);
va_end(alist);
- (void) putchar('\n');
+ (void) putc('\n', stderr);
}
/* PRINTFLIKE2 */
@@ -1503,7 +907,7 @@ die(const char *format, ...)
(void) vfprintf(stderr, format, alist);
va_end(alist);
- (void) putchar('\n');
+ (void) putc('\n', stderr);
/* close dladm handle if it was opened */
if (handle != NULL)
diff --git a/usr/src/cmd/flowstat/Makefile b/usr/src/cmd/flowstat/Makefile
new file mode 100644
index 0000000000..5cb3eb4025
--- /dev/null
+++ b/usr/src/cmd/flowstat/Makefile
@@ -0,0 +1,70 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+PROG=flowstat
+
+ROOTFS_PROG= $(PROG)
+
+POFILE= $(PROG).po
+
+include ../Makefile.cmd
+
+XGETFLAGS += -a -x $(PROG).xcl
+LDLIBS += -L$(ROOT)/lib
+LDLIBS += -ldladm -linetutil
+
+ROOTCFGDIR= $(ROOTETC)/dladm
+
+.KEEP_STATE:
+
+all: $(ROOTFS_PROG)
+
+#
+# Message catalog
+#
+_msg: $(POFILE)
+
+$(POFILE): $(PROG).c
+ $(RM) $@
+ $(COMPILE.cpp) $(PROG).c > $(POFILE).i
+ $(XGETTEXT) $(XGETFLAGS) $(POFILE).i
+ sed "/^domain/d" messages.po > $@
+ $(RM) messages.po $(POFILE).i
+
+install: all $(ROOTSBINPROG) $(ROOTCFGDIR)
+ $(RM) $(ROOTUSRSBINPROG)
+ -$(SYMLINK) ../../sbin/$(PROG) $(ROOTUSRSBINPROG)
+
+clean:
+
+lint: lint_PROG
+
+$(ROOTCFGDIR):
+ $(INS.dir)
+
+$(ROOTCFGDIR)/%: $(ROOTCFGDIR) %
+ $(INS.file)
+
+include ../Makefile.targ
diff --git a/usr/src/cmd/flowstat/flowstat.c b/usr/src/cmd/flowstat/flowstat.c
new file mode 100644
index 0000000000..3ddff9e34f
--- /dev/null
+++ b/usr/src/cmd/flowstat/flowstat.c
@@ -0,0 +1,1149 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <stdio.h>
+#include <locale.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stropts.h>
+#include <errno.h>
+#include <strings.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <priv.h>
+#include <netdb.h>
+#include <libintl.h>
+#include <libdlflow.h>
+#include <libdllink.h>
+#include <libdlstat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/ethernet.h>
+#include <inet/ip.h>
+#include <inet/ip6.h>
+#include <stddef.h>
+#include <ofmt.h>
+
+typedef struct flow_chain_s {
+ char fc_flowname[MAXFLOWNAMELEN];
+ boolean_t fc_visited;
+ flow_stat_t *fc_stat;
+ struct flow_chain_s *fc_next;
+} flow_chain_t;
+
+typedef struct show_flow_state {
+ flow_chain_t *fs_flowchain;
+ ofmt_handle_t fs_ofmt;
+ char fs_unit;
+ boolean_t fs_parsable;
+} show_flow_state_t;
+
+typedef struct show_history_state_s {
+ boolean_t us_plot;
+ boolean_t us_parsable;
+ boolean_t us_printheader;
+ boolean_t us_first;
+ boolean_t us_showall;
+ ofmt_handle_t us_ofmt;
+} show_history_state_t;
+
+static void do_show_history(int, char **);
+
+static int query_flow_stats(dladm_handle_t, dladm_flow_attr_t *, void *);
+static int query_link_flow_stats(dladm_handle_t, datalink_id_t, void *);
+
+static void die(const char *, ...);
+static void die_optdup(int);
+static void die_opterr(int, int, const char *);
+static void die_dlerr(dladm_status_t, const char *, ...);
+static void warn(const char *, ...);
+
+/* callback functions for printing output */
+static ofmt_cb_t print_default_cb, print_flow_stats_cb;
+static void flowstat_ofmt_check(ofmt_status_t, boolean_t, ofmt_handle_t);
+
+#define NULL_OFMT {NULL, 0, 0, NULL}
+
+/*
+ * structures for flowstat (printing live statistics)
+ */
+typedef enum {
+ FLOW_S_FLOW,
+ FLOW_S_IPKTS,
+ FLOW_S_RBYTES,
+ FLOW_S_IERRORS,
+ FLOW_S_OPKTS,
+ FLOW_S_OBYTES,
+ FLOW_S_OERRORS
+} flow_s_field_index_t;
+
+static ofmt_field_t flow_s_fields[] = {
+/* name, field width, index, callback */
+{ "FLOW", 15, FLOW_S_FLOW, print_flow_stats_cb},
+{ "IPKTS", 8, FLOW_S_IPKTS, print_flow_stats_cb},
+{ "RBYTES", 8, FLOW_S_RBYTES, print_flow_stats_cb},
+{ "IERRS", 8, FLOW_S_IERRORS, print_flow_stats_cb},
+{ "OPKTS", 8, FLOW_S_OPKTS, print_flow_stats_cb},
+{ "OBYTES", 8, FLOW_S_OBYTES, print_flow_stats_cb},
+{ "OERRS", 8, FLOW_S_OERRORS, print_flow_stats_cb},
+NULL_OFMT}
+;
+
+typedef struct flow_args_s {
+ char *flow_s_flow;
+ flow_stat_t *flow_s_stat;
+ char flow_s_unit;
+ boolean_t flow_s_parsable;
+} flow_args_t;
+
+/*
+ * structures for 'flowstat -h'
+ */
+typedef struct history_fields_buf_s {
+ char history_flow[12];
+ char history_duration[10];
+ char history_ipackets[9];
+ char history_rbytes[10];
+ char history_opackets[9];
+ char history_obytes[10];
+ char history_bandwidth[14];
+} history_fields_buf_t;
+
+static ofmt_field_t history_fields[] = {
+/* name, field width, offset */
+{ "FLOW", 13,
+ offsetof(history_fields_buf_t, history_flow), print_default_cb},
+{ "DURATION", 11,
+ offsetof(history_fields_buf_t, history_duration), print_default_cb},
+{ "IPACKETS", 10,
+ offsetof(history_fields_buf_t, history_ipackets), print_default_cb},
+{ "RBYTES", 11,
+ offsetof(history_fields_buf_t, history_rbytes), print_default_cb},
+{ "OPACKETS", 10,
+ offsetof(history_fields_buf_t, history_opackets), print_default_cb},
+{ "OBYTES", 11,
+ offsetof(history_fields_buf_t, history_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_fields_buf_t, history_bandwidth), print_default_cb},
+NULL_OFMT}
+;
+
+typedef struct history_l_fields_buf_s {
+ char history_l_flow[12];
+ char history_l_stime[13];
+ char history_l_etime[13];
+ char history_l_rbytes[8];
+ char history_l_obytes[8];
+ char history_l_bandwidth[14];
+} history_l_fields_buf_t;
+
+static ofmt_field_t history_l_fields[] = {
+/* name, field width, offset */
+{ "FLOW", 13,
+ offsetof(history_l_fields_buf_t, history_l_flow), print_default_cb},
+{ "START", 14,
+ offsetof(history_l_fields_buf_t, history_l_stime), print_default_cb},
+{ "END", 14,
+ offsetof(history_l_fields_buf_t, history_l_etime), print_default_cb},
+{ "RBYTES", 9,
+ offsetof(history_l_fields_buf_t, history_l_rbytes), print_default_cb},
+{ "OBYTES", 9,
+ offsetof(history_l_fields_buf_t, history_l_obytes), print_default_cb},
+{ "BANDWIDTH", 15,
+ offsetof(history_l_fields_buf_t, history_l_bandwidth),
+ print_default_cb},
+NULL_OFMT}
+;
+
+static char *progname;
+
+/*
+ * Handle to libdladm. Opened in main() before the sub-command
+ * specific function is called.
+ */
+static dladm_handle_t handle = NULL;
+
+const char *usage_ermsg = "flowstat [-r | -t] [-i interval] "
+ "[-l link] [flow]\n"
+ " flowstat [-S] [-A] [-i interval] [-p] [ -o field[,...]]\n"
+ " [-u R|K|M|G|T|P] [-l link] [flow]\n"
+ " flowstat -h [-a] [-d] [-F format]"
+ " [-s <DD/MM/YYYY,HH:MM:SS>]\n"
+ " [-e <DD/MM/YYYY,HH:MM:SS>] -f <logfile> "
+ "[<flow>]";
+
+static void
+usage(void)
+{
+ (void) fprintf(stderr, "%s\n", gettext(usage_ermsg));
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(1);
+}
+
+boolean_t
+flowstat_unit(char *oarg, char *unit)
+{
+ if ((strcmp(oarg, "R") == 0) || (strcmp(oarg, "K") == 0) ||
+ (strcmp(oarg, "M") == 0) || (strcmp(oarg, "G") == 0) ||
+ (strcmp(oarg, "T") == 0) || (strcmp(oarg, "P") == 0)) {
+ *unit = oarg[0];
+ return (B_TRUE);
+ }
+
+ return (B_FALSE);
+}
+
+void
+map_to_units(char *buf, uint_t bufsize, double num, char unit,
+ boolean_t parsable)
+{
+ if (parsable) {
+ (void) snprintf(buf, bufsize, "%.0lf", num);
+ return;
+ }
+
+ if (unit == '\0') {
+ int index;
+
+ for (index = 0; (int)(num/1000) != 0; index++, num /= 1000)
+ ;
+
+ switch (index) {
+ case 0:
+ unit = '\0';
+ break;
+ case 1:
+ unit = 'K';
+ break;
+ case 2:
+ unit = 'M';
+ break;
+ case 3:
+ unit = 'G';
+ break;
+ case 4:
+ unit = 'T';
+ break;
+ case 5:
+ /* Largest unit supported */
+ default:
+ unit = 'P';
+ break;
+ }
+ } else {
+ switch (unit) {
+ case 'R':
+ /* Already raw numbers */
+ unit = '\0';
+ break;
+ case 'K':
+ num /= 1000;
+ break;
+ case 'M':
+ num /= (1000*1000);
+ break;
+ case 'G':
+ num /= (1000*1000*1000);
+ break;
+ case 'T':
+ num /= (1000.0*1000.0*1000.0*1000.0);
+ break;
+ case 'P':
+ /* Largest unit supported */
+ default:
+ num /= (1000.0*1000.0*1000.0*1000.0*1000.0);
+ break;
+ }
+ }
+
+ if (unit == '\0')
+ (void) snprintf(buf, bufsize, " %7.0lf%c", num, unit);
+ else
+ (void) snprintf(buf, bufsize, " %6.2lf%c", num, unit);
+}
+
+flow_chain_t *
+get_flow_prev_stat(const char *flowname, void *arg)
+{
+ show_flow_state_t *state = arg;
+ flow_chain_t *flow_curr = NULL;
+
+ /* Scan prev flowname list and look for entry matching this entry */
+ for (flow_curr = state->fs_flowchain; flow_curr;
+ flow_curr = flow_curr->fc_next) {
+ if (strcmp(flow_curr->fc_flowname, flowname) == 0)
+ break;
+ }
+
+ /* New flow, add it */
+ if (flow_curr == NULL) {
+ flow_curr = (flow_chain_t *)malloc(sizeof (flow_chain_t));
+ if (flow_curr == NULL)
+ goto done;
+ (void) strncpy(flow_curr->fc_flowname, flowname,
+ MAXFLOWNAMELEN);
+ flow_curr->fc_stat = NULL;
+ flow_curr->fc_next = state->fs_flowchain;
+ state->fs_flowchain = flow_curr;
+ }
+done:
+ return (flow_curr);
+}
+
+/*
+ * Number of flows may change while flowstat -i is executing.
+ * Free memory allocated for flows that are no longer there.
+ * Prepare for next iteration by marking visited = false for
+ * existing stat entries.
+ */
+static void
+cleanup_removed_flows(show_flow_state_t *state)
+{
+ flow_chain_t *fcurr;
+ flow_chain_t *fprev;
+ flow_chain_t *tofree;
+
+ /* Delete all nodes from the list that have fc_visited marked false */
+ fcurr = state->fs_flowchain;
+ while (fcurr != NULL) {
+ if (fcurr->fc_visited) {
+ fcurr->fc_visited = B_FALSE;
+ fprev = fcurr;
+ fcurr = fcurr->fc_next;
+ continue;
+ }
+
+ /* Is it head of the list? */
+ if (fcurr == state->fs_flowchain)
+ state->fs_flowchain = fcurr->fc_next;
+ else
+ fprev->fc_next = fcurr->fc_next;
+
+ /* fprev remains the same */
+ tofree = fcurr;
+ fcurr = fcurr->fc_next;
+
+ /* Free stats memory for the removed flow */
+ dladm_flow_stat_free(tofree->fc_stat);
+ free(tofree);
+ }
+}
+
+static boolean_t
+print_flow_stats_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
+{
+ flow_args_t *fargs = of_arg->ofmt_cbarg;
+ flow_stat_t *diff_stats = fargs->flow_s_stat;
+ char unit = fargs->flow_s_unit;
+ boolean_t parsable = fargs->flow_s_parsable;
+
+ switch (of_arg->ofmt_id) {
+ case FLOW_S_FLOW:
+ (void) snprintf(buf, bufsize, "%s", fargs->flow_s_flow);
+ break;
+ case FLOW_S_IPKTS:
+ map_to_units(buf, bufsize, diff_stats->fl_ipackets, unit,
+ parsable);
+ break;
+ case FLOW_S_RBYTES:
+ map_to_units(buf, bufsize, diff_stats->fl_rbytes, unit,
+ parsable);
+ break;
+ case FLOW_S_IERRORS:
+ map_to_units(buf, bufsize, diff_stats->fl_ierrors, unit,
+ parsable);
+ break;
+ case FLOW_S_OPKTS:
+ map_to_units(buf, bufsize, diff_stats->fl_opackets, unit,
+ parsable);
+ break;
+ case FLOW_S_OBYTES:
+ map_to_units(buf, bufsize, diff_stats->fl_obytes, unit,
+ parsable);
+ break;
+ case FLOW_S_OERRORS:
+ map_to_units(buf, bufsize, diff_stats->fl_oerrors, unit,
+ parsable);
+ break;
+ default:
+ die("invalid input");
+ break;
+ }
+ return (B_TRUE);
+}
+
+/* ARGSUSED */
+static int
+query_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
+{
+ show_flow_state_t *state = arg;
+ flow_chain_t *flow_node;
+ flow_stat_t *curr_stat;
+ flow_stat_t *prev_stat;
+ flow_stat_t *diff_stat;
+ char *flowname = attr->fa_flowname;
+ flow_args_t fargs;
+
+ /* Get previous stats for the flow */
+ flow_node = get_flow_prev_stat(flowname, arg);
+ if (flow_node == NULL)
+ goto done;
+
+ flow_node->fc_visited = B_TRUE;
+ prev_stat = flow_node->fc_stat;
+
+ /* Query library for current stats */
+ curr_stat = dladm_flow_stat_query(flowname);
+ if (curr_stat == NULL)
+ goto done;
+
+ /* current stats - prev iteration stats */
+ diff_stat = dladm_flow_stat_diff(curr_stat, prev_stat);
+
+ /* Free prev stats */
+ dladm_flow_stat_free(prev_stat);
+
+ /* Prev <- curr stats */
+ flow_node->fc_stat = curr_stat;
+
+ if (diff_stat == NULL)
+ goto done;
+
+ /* Print stats */
+ fargs.flow_s_flow = flowname;
+ fargs.flow_s_stat = diff_stat;
+ fargs.flow_s_unit = state->fs_unit;
+ fargs.flow_s_parsable = state->fs_parsable;
+ ofmt_print(state->fs_ofmt, &fargs);
+
+ /* Free diff stats */
+ dladm_flow_stat_free(diff_stat);
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+/*
+ * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
+ * dladm_walk_datalink_id(). Used for showing flow stats for
+ * all flows on all links.
+ */
+static int
+query_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
+{
+ if (dladm_walk_flow(query_flow_stats, dh, linkid, arg, B_FALSE)
+ == DLADM_STATUS_OK)
+ return (DLADM_WALK_CONTINUE);
+ else
+ return (DLADM_WALK_TERMINATE);
+}
+
+void
+print_all_stats(name_value_stat_entry_t *stat_entry)
+{
+ name_value_stat_t *curr_stat;
+
+ printf("%s\n", stat_entry->nve_header);
+
+ for (curr_stat = stat_entry->nve_stats; curr_stat != NULL;
+ curr_stat = curr_stat->nv_nextstat) {
+ printf("\t%15s", curr_stat->nv_statname);
+ printf("\t%15llu\n", curr_stat->nv_statval);
+ }
+}
+
+/* ARGSUSED */
+static int
+dump_one_flow_stats(dladm_handle_t handle, dladm_flow_attr_t *attr, void *arg)
+{
+ char *flowname = attr->fa_flowname;
+ void *stat;
+
+ stat = dladm_flow_stat_query_all(flowname);
+ if (stat == NULL)
+ goto done;
+ print_all_stats(stat);
+ dladm_flow_stat_query_all_free(stat);
+
+done:
+ return (DLADM_WALK_CONTINUE);
+}
+
+/*
+ * Wrapper of dladm_walk_flow(query_flow_stats,...) to make it usable for
+ * dladm_walk_datalink_id(). Used for showing flow stats for
+ * all flows on all links.
+ */
+static int
+dump_link_flow_stats(dladm_handle_t dh, datalink_id_t linkid, void * arg)
+{
+ if (dladm_walk_flow(dump_one_flow_stats, dh, linkid, arg, B_FALSE)
+ == DLADM_STATUS_OK)
+ return (DLADM_WALK_CONTINUE);
+ else
+ return (DLADM_WALK_TERMINATE);
+}
+
+static void
+dump_all_flow_stats(dladm_flow_attr_t *attrp, void *arg, datalink_id_t linkid,
+ boolean_t flow_arg)
+{
+ /* Show stats for named flow */
+ if (flow_arg) {
+ (void) dump_one_flow_stats(handle, attrp, arg);
+
+ /* Show stats for flows on one link */
+ } else if (linkid != DATALINK_INVALID_LINKID) {
+ (void) dladm_walk_flow(dump_one_flow_stats, handle, linkid,
+ arg, B_FALSE);
+
+ /* Show stats for all flows on all links */
+ } else {
+ (void) dladm_walk_datalink_id(dump_link_flow_stats,
+ handle, arg, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ dladm_status_t status;
+ int option;
+ boolean_t r_arg = B_FALSE;
+ boolean_t t_arg = B_FALSE;
+ boolean_t p_arg = B_FALSE;
+ boolean_t i_arg = B_FALSE;
+ boolean_t o_arg = B_FALSE;
+ boolean_t u_arg = B_FALSE;
+ boolean_t A_arg = B_FALSE;
+ boolean_t S_arg = B_FALSE;
+ boolean_t flow_arg = B_FALSE;
+ datalink_id_t linkid = DATALINK_ALL_LINKID;
+ char linkname[MAXLINKNAMELEN];
+ char flowname[MAXFLOWNAMELEN];
+ uint32_t interval = 0;
+ char unit = '\0';
+ show_flow_state_t state;
+ char *fields_str = NULL;
+ char *o_fields_str = NULL;
+
+ char *total_stat_fields =
+ "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs";
+ char *rx_stat_fields =
+ "flow,ipkts,rbytes,ierrs";
+ char *tx_stat_fields =
+ "flow,opkts,obytes,oerrs";
+
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = OFMT_RIGHTJUST;
+
+ dladm_flow_attr_t attr;
+
+ (void) setlocale(LC_ALL, "");
+#if !defined(TEXT_DOMAIN)
+#define TEXT_DOMAIN "SYS_TEST"
+#endif
+ (void) textdomain(TEXT_DOMAIN);
+
+ progname = argv[0];
+
+ /* Open the libdladm handle */
+ if ((status = dladm_open(&handle)) != DLADM_STATUS_OK)
+ die_dlerr(status, "could not open /dev/dld");
+
+ bzero(&state, sizeof (state));
+
+ opterr = 0;
+ while ((option = getopt_long(argc, argv, ":rtApSi:o:u:l:h",
+ NULL, NULL)) != -1) {
+ switch (option) {
+ case 'r':
+ if (r_arg)
+ die_optdup(option);
+
+ r_arg = B_TRUE;
+ break;
+ case 't':
+ if (t_arg)
+ die_optdup(option);
+
+ t_arg = B_TRUE;
+ break;
+ case 'A':
+ if (A_arg)
+ die_optdup(option);
+
+ A_arg = B_TRUE;
+ break;
+ case 'p':
+ if (p_arg)
+ die_optdup(option);
+
+ p_arg = B_TRUE;
+ break;
+ case 'S':
+ if (S_arg)
+ die_optdup(option);
+ S_arg = B_TRUE;
+ break;
+ case 'i':
+ if (i_arg)
+ die_optdup(option);
+
+ i_arg = B_TRUE;
+ if (!dladm_str2interval(optarg, &interval))
+ die("invalid interval value '%s'", optarg);
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ o_fields_str = optarg;
+ break;
+ case 'u':
+ if (u_arg)
+ die_optdup(option);
+
+ u_arg = B_TRUE;
+ if (!flowstat_unit(optarg, &unit))
+ die("invalid unit value '%s',"
+ "unit must be R|K|M|G|T|P", optarg);
+ break;
+ case 'l':
+ if (strlcpy(linkname, optarg, MAXLINKNAMELEN)
+ >= MAXLINKNAMELEN)
+ die("link name too long\n");
+ if (dladm_name2info(handle, linkname, &linkid, NULL,
+ NULL, NULL) != DLADM_STATUS_OK)
+ die("invalid link '%s'", linkname);
+ break;
+ case 'h':
+ if (r_arg || t_arg || p_arg || o_arg || u_arg ||
+ i_arg || S_arg || A_arg) {
+ die("the option -h is not compatible with "
+ "-r, -t, -p, -o, -u, -i, -S, -A");
+ }
+ do_show_history(argc, argv);
+ return (0);
+ break;
+ default:
+ die_opterr(optopt, option, usage_ermsg);
+ break;
+ }
+ }
+
+ if (r_arg && t_arg)
+ die("the option -t and -r are not compatible");
+
+ if (u_arg && p_arg)
+ die("the option -u and -p are not compatible");
+
+ if (p_arg && !o_arg)
+ die("-p requires -o");
+
+ if (p_arg && strcasecmp(o_fields_str, "all") == 0)
+ die("\"-o all\" is invalid with -p");
+
+ if (S_arg &&
+ (r_arg || t_arg || p_arg || o_arg || u_arg))
+ die("the option -S is not compatible with "
+ "-r, -t, -p, -o, -u");
+
+ if (A_arg &&
+ (r_arg || t_arg || p_arg || o_arg || u_arg || i_arg))
+ die("the option -A is not compatible with "
+ "-r, -t, -p, -o, -u, -i");
+
+ /* get flow name (optional last argument) */
+ if (optind == (argc-1)) {
+ if (strlcpy(flowname, argv[optind], MAXFLOWNAMELEN)
+ >= MAXFLOWNAMELEN)
+ die("flow name too long");
+ flow_arg = B_TRUE;
+ } else if (optind != argc) {
+ usage();
+ }
+
+ if (S_arg) {
+ dladm_continuous(handle, linkid, (flow_arg ? flowname : NULL),
+ interval, FLOW_REPORT);
+ return (0);
+ }
+
+ if (flow_arg &&
+ dladm_flow_info(handle, flowname, &attr) != DLADM_STATUS_OK)
+ die("invalid flow %s", flowname);
+
+ if (A_arg) {
+ dump_all_flow_stats(&attr, &state, linkid, flow_arg);
+ return (0);
+ }
+
+ state.fs_unit = unit;
+ state.fs_parsable = p_arg;
+
+ if (state.fs_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+
+ if (r_arg)
+ fields_str = rx_stat_fields;
+ else if (t_arg)
+ fields_str = tx_stat_fields;
+ else
+ fields_str = total_stat_fields;
+
+ if (o_arg) {
+ fields_str = (strcasecmp(o_fields_str, "all") == 0) ?
+ fields_str : o_fields_str;
+ }
+
+ oferr = ofmt_open(fields_str, flow_s_fields, ofmtflags, 0, &ofmt);
+ flowstat_ofmt_check(oferr, state.fs_parsable, ofmt);
+ state.fs_ofmt = ofmt;
+
+ for (;;) {
+ /* Show stats for named flow */
+ if (flow_arg) {
+ (void) query_flow_stats(handle, &attr, &state);
+
+ /* Show stats for flows on one link */
+ } else if (linkid != DATALINK_INVALID_LINKID) {
+ (void) dladm_walk_flow(query_flow_stats, handle, linkid,
+ &state, B_FALSE);
+
+ /* Show stats for all flows on all links */
+ } else {
+ (void) dladm_walk_datalink_id(query_link_flow_stats,
+ handle, &state, DATALINK_CLASS_ALL,
+ DATALINK_ANY_MEDIATYPE, DLADM_OPT_ACTIVE);
+ }
+
+ if (interval == 0)
+ break;
+
+ (void) fflush(stdout);
+ cleanup_removed_flows(&state);
+ (void) sleep(interval);
+ }
+ ofmt_close(ofmt);
+
+ dladm_close(handle);
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+show_history_date(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = (show_history_state_t *)arg;
+ time_t stime;
+ char timebuf[20];
+ dladm_flow_attr_t attr;
+ dladm_status_t status;
+
+ /*
+ * Only show historical information for existing flows unless '-a'
+ * is specified.
+ */
+ if (!state->us_showall && ((status = dladm_flow_info(handle,
+ history->du_name, &attr)) != DLADM_STATUS_OK)) {
+ return (status);
+ }
+
+ stime = history->du_stime;
+ (void) strftime(timebuf, sizeof (timebuf), "%m/%d/%Y",
+ localtime(&stime));
+ (void) printf("%s\n", timebuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_time(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = (show_history_state_t *)arg;
+ char buf[DLADM_STRSIZE];
+ history_l_fields_buf_t ubuf;
+ time_t time;
+ double bw;
+ dladm_flow_attr_t attr;
+ dladm_status_t status;
+
+ /*
+ * Only show historical information for existing flows unless '-a'
+ * is specified.
+ */
+ if (!state->us_showall && ((status = dladm_flow_info(handle,
+ history->du_name, &attr)) != DLADM_STATUS_OK)) {
+ return (status);
+ }
+
+ if (state->us_plot) {
+ if (!state->us_printheader) {
+ if (state->us_first) {
+ (void) printf("# Time");
+ state->us_first = B_FALSE;
+ }
+ (void) printf(" %s", history->du_name);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->us_first = B_TRUE;
+ state->us_printheader = B_TRUE;
+ }
+ } else {
+ if (state->us_first) {
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T",
+ localtime(&time));
+ state->us_first = B_FALSE;
+ (void) printf("%s", buf);
+ }
+ bw = (double)history->du_bandwidth/1000;
+ (void) printf(" %.2f", bw);
+ if (history->du_last) {
+ (void) printf("\n");
+ state->us_first = B_TRUE;
+ }
+ }
+ return (DLADM_STATUS_OK);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.history_l_flow, sizeof (ubuf.history_l_flow), "%s",
+ history->du_name);
+ time = history->du_stime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.history_l_stime, sizeof (ubuf.history_l_stime),
+ "%s", buf);
+ time = history->du_etime;
+ (void) strftime(buf, sizeof (buf), "%T", localtime(&time));
+ (void) snprintf(ubuf.history_l_etime, sizeof (ubuf.history_l_etime),
+ "%s", buf);
+ (void) snprintf(ubuf.history_l_rbytes, sizeof (ubuf.history_l_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.history_l_obytes, sizeof (ubuf.history_l_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.history_l_bandwidth,
+ sizeof (ubuf.history_l_bandwidth), "%s Mbps",
+ dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->us_ofmt, (void *)&ubuf);
+ return (DLADM_STATUS_OK);
+}
+
+static int
+show_history_res(dladm_usage_t *history, void *arg)
+{
+ show_history_state_t *state = (show_history_state_t *)arg;
+ char buf[DLADM_STRSIZE];
+ history_fields_buf_t ubuf;
+ dladm_flow_attr_t attr;
+ dladm_status_t status;
+
+ /*
+ * Only show historical information for existing flows unless '-a'
+ * is specified.
+ */
+ if (!state->us_showall && ((status = dladm_flow_info(handle,
+ history->du_name, &attr)) != DLADM_STATUS_OK)) {
+ return (status);
+ }
+
+ bzero(&ubuf, sizeof (ubuf));
+
+ (void) snprintf(ubuf.history_flow, sizeof (ubuf.history_flow), "%s",
+ history->du_name);
+ (void) snprintf(ubuf.history_duration, sizeof (ubuf.history_duration),
+ "%llu", history->du_duration);
+ (void) snprintf(ubuf.history_ipackets, sizeof (ubuf.history_ipackets),
+ "%llu", history->du_ipackets);
+ (void) snprintf(ubuf.history_rbytes, sizeof (ubuf.history_rbytes),
+ "%llu", history->du_rbytes);
+ (void) snprintf(ubuf.history_opackets, sizeof (ubuf.history_opackets),
+ "%llu", history->du_opackets);
+ (void) snprintf(ubuf.history_obytes, sizeof (ubuf.history_obytes),
+ "%llu", history->du_obytes);
+ (void) snprintf(ubuf.history_bandwidth, sizeof (ubuf.history_bandwidth),
+ "%s Mbps", dladm_bw2str(history->du_bandwidth, buf));
+
+ ofmt_print(state->us_ofmt, (void *)&ubuf);
+
+ return (DLADM_STATUS_OK);
+}
+
+static boolean_t
+valid_formatspec(char *formatspec_str)
+{
+ return (strcmp(formatspec_str, "gnuplot") == 0);
+}
+
+/* ARGSUSED */
+static void
+do_show_history(int argc, char *argv[])
+{
+ char *file = NULL;
+ int opt;
+ dladm_status_t status;
+ boolean_t d_arg = B_FALSE;
+ char *stime = NULL;
+ char *etime = NULL;
+ char *resource = NULL;
+ show_history_state_t state;
+ boolean_t o_arg = B_FALSE;
+ boolean_t F_arg = B_FALSE;
+ char *fields_str = NULL;
+ char *formatspec_str = NULL;
+ char *all_fields =
+ "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth";
+ char *all_l_fields =
+ "flow,start,end,rbytes,obytes,bandwidth";
+ ofmt_handle_t ofmt;
+ ofmt_status_t oferr;
+ uint_t ofmtflags = 0;
+
+ bzero(&state, sizeof (show_history_state_t));
+ state.us_parsable = B_FALSE;
+ state.us_printheader = B_FALSE;
+ state.us_plot = B_FALSE;
+ state.us_first = B_TRUE;
+
+ while ((opt = getopt(argc, argv, "das:e:o:f:F:")) != -1) {
+ switch (opt) {
+ case 'd':
+ d_arg = B_TRUE;
+ break;
+ case 'a':
+ state.us_showall = B_TRUE;
+ break;
+ case 'f':
+ file = optarg;
+ break;
+ case 's':
+ stime = optarg;
+ break;
+ case 'e':
+ etime = optarg;
+ break;
+ case 'o':
+ o_arg = B_TRUE;
+ fields_str = optarg;
+ break;
+ case 'F':
+ state.us_plot = F_arg = B_TRUE;
+ formatspec_str = optarg;
+ break;
+ default:
+ die_opterr(optopt, opt, usage_ermsg);
+ }
+ }
+
+ if (file == NULL)
+ die("-h requires a file");
+
+ if (optind == (argc-1)) {
+ dladm_flow_attr_t attr;
+
+ resource = argv[optind];
+ if (!state.us_showall &&
+ dladm_flow_info(handle, resource, &attr) !=
+ DLADM_STATUS_OK) {
+ die("invalid flow: '%s'", resource);
+ }
+ }
+
+ if (state.us_parsable)
+ ofmtflags |= OFMT_PARSABLE;
+ if (resource == NULL && stime == NULL && etime == NULL) {
+ if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
+ fields_str = all_fields;
+ oferr = ofmt_open(fields_str, history_fields, ofmtflags,
+ 0, &ofmt);
+ } else {
+ if (!o_arg || (o_arg && strcasecmp(fields_str, "all") == 0))
+ fields_str = all_l_fields;
+ oferr = ofmt_open(fields_str, history_l_fields, ofmtflags,
+ 0, &ofmt);
+ }
+
+ flowstat_ofmt_check(oferr, state.us_parsable, ofmt);
+ state.us_ofmt = ofmt;
+
+ if (F_arg && d_arg)
+ die("incompatible -d and -F options");
+
+ if (F_arg && !valid_formatspec(formatspec_str))
+ die("Format specifier %s not supported", formatspec_str);
+
+ if (d_arg) {
+ /* Print log dates */
+ status = dladm_usage_dates(show_history_date,
+ DLADM_LOGTYPE_FLOW, file, resource, &state);
+ } else if (resource == NULL && stime == NULL && etime == NULL &&
+ !F_arg) {
+ /* Print summary */
+ status = dladm_usage_summary(show_history_res,
+ DLADM_LOGTYPE_FLOW, file, &state);
+ } else if (resource != NULL) {
+ /* Print log entries for named resource */
+ status = dladm_walk_usage_res(show_history_time,
+ DLADM_LOGTYPE_FLOW, file, resource, stime, etime, &state);
+ } else {
+ /* Print time and information for each flow */
+ status = dladm_walk_usage_time(show_history_time,
+ DLADM_LOGTYPE_FLOW, file, stime, etime, &state);
+ }
+
+ ofmt_close(ofmt);
+ if (status != DLADM_STATUS_OK)
+ die_dlerr(status, "-h");
+ dladm_close(handle);
+}
+
+static void
+warn(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: warning: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+}
+
+/* PRINTFLIKE1 */
+static void
+die(const char *format, ...)
+{
+ va_list alist;
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+
+ (void) putc('\n', stderr);
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+static void
+die_optdup(int opt)
+{
+ die("the option -%c cannot be specified more than once", opt);
+}
+
+static void
+die_opterr(int opt, int opterr, const char *usage)
+{
+ switch (opterr) {
+ case ':':
+ die("option '-%c' requires a value\nusage: %s", opt,
+ gettext(usage));
+ break;
+ case '?':
+ default:
+ die("unrecognized option '-%c'\nusage: %s", opt,
+ gettext(usage));
+ break;
+ }
+}
+
+/* PRINTFLIKE2 */
+static void
+die_dlerr(dladm_status_t err, const char *format, ...)
+{
+ va_list alist;
+ char errmsg[DLADM_STRSIZE];
+
+ format = gettext(format);
+ (void) fprintf(stderr, "%s: ", progname);
+
+ va_start(alist, format);
+ (void) vfprintf(stderr, format, alist);
+ va_end(alist);
+ (void) fprintf(stderr, ": %s\n", dladm_status2str(err, errmsg));
+
+ /* close dladm handle if it was opened */
+ if (handle != NULL)
+ dladm_close(handle);
+
+ exit(EXIT_FAILURE);
+}
+
+
+/*
+ * default output callback function that, when invoked from dladm_print_output,
+ * prints string which is offset by of_arg->ofmt_id within buf.
+ */
+static boolean_t
+print_default_cb(ofmt_arg_t *of_arg, char *buf, uint_t bufsize)
+{
+ char *value;
+
+ value = (char *)of_arg->ofmt_cbarg + of_arg->ofmt_id;
+ (void) strlcpy(buf, value, bufsize);
+ return (B_TRUE);
+}
+
+static void
+flowstat_ofmt_check(ofmt_status_t oferr, boolean_t parsable,
+ ofmt_handle_t ofmt)
+{
+ char buf[OFMT_BUFSIZE];
+
+ if (oferr == OFMT_SUCCESS)
+ return;
+ (void) ofmt_strerror(ofmt, oferr, buf, sizeof (buf));
+ /*
+ * All errors are considered fatal in parsable mode.
+ * NOMEM errors are always fatal, regardless of mode.
+ * For other errors, we print diagnostics in human-readable
+ * mode and processs what we can.
+ */
+ if (parsable || oferr == OFMT_ENOFIELDS) {
+ ofmt_close(ofmt);
+ die(buf);
+ } else {
+ warn(buf);
+ }
+}
diff --git a/usr/src/cmd/flowstat/flowstat.xcl b/usr/src/cmd/flowstat/flowstat.xcl
new file mode 100644
index 0000000000..369608c062
--- /dev/null
+++ b/usr/src/cmd/flowstat/flowstat.xcl
@@ -0,0 +1,73 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#
+
+msgid " %.2f"
+msgid " %6.2lf%c"
+msgid " %7.0lf%c"
+msgid " %s"
+msgid "-h"
+msgid ": %s\n"
+msgid ":rtApSi:o:u:l:h"
+msgid ""
+msgid "\n"
+msgid "\t%15llu\n"
+msgid "\t%15s"
+msgid "# Time"
+msgid "%.0lf"
+msgid "%llu"
+msgid "%m/%d/%Y"
+msgid "%s Mbps"
+msgid "%s: "
+msgid "%s: warning: "
+msgid "%s"
+msgid "%s\n"
+msgid "%T"
+msgid "all"
+msgid "B"
+msgid "BANDWIDTH"
+msgid "das:e:o:f:F:"
+msgid "DURATION"
+msgid "END"
+msgid "flow,duration,ipackets,rbytes,opackets,obytes,bandwidth"
+msgid "flow,ipkts,rbytes,ierrs,opkts,obytes,oerrs"
+msgid "flow,ipkts,rbytes,ierrs"
+msgid "flow,opkts,obytes,oerrs"
+msgid "flow,start,end,rbytes,obytes,bandwidth"
+msgid "FLOW"
+msgid "G"
+msgid "gnuplot"
+msgid "IERRS"
+msgid "IPACKETS"
+msgid "IPKTS"
+msgid "K"
+msgid "M"
+msgid "OBYTES"
+msgid "OERRS"
+msgid "OPACKETS"
+msgid "OPKTS"
+msgid "P"
+msgid "RBYTES"
+msgid "START"
+msgid "T"
diff --git a/usr/src/cmd/mdb/common/modules/mac/mac.c b/usr/src/cmd/mdb/common/modules/mac/mac.c
index 4a56960ca7..268d92ac2d 100644
--- a/usr/src/cmd/mdb/common/modules/mac/mac.c
+++ b/usr/src/cmd/mdb/common/modules/mac/mac.c
@@ -19,7 +19,7 @@
* CDDL HEADER END
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -34,6 +34,7 @@
#include <sys/mac_client_impl.h>
#include <sys/mac_flow_impl.h>
#include <sys/mac_soft_ring.h>
+#include <sys/mac_stat.h>
#define STRSIZE 64
#define MAC_RX_SRS_SIZE (MAX_RINGS_PER_GROUP * sizeof (uintptr_t))
@@ -59,12 +60,15 @@
#define MAC_SRS_STAT 0x04
#define MAC_SRS_CPU 0x08
#define MAC_SRS_VERBOSE 0x10
+#define MAC_SRS_INTR 0x20
#define MAC_SRS_RXSTAT (MAC_SRS_RX|MAC_SRS_STAT)
#define MAC_SRS_TXSTAT (MAC_SRS_TX|MAC_SRS_STAT)
#define MAC_SRS_RXCPU (MAC_SRS_RX|MAC_SRS_CPU)
#define MAC_SRS_TXCPU (MAC_SRS_TX|MAC_SRS_CPU)
#define MAC_SRS_RXCPUVERBOSE (MAC_SRS_RXCPU|MAC_SRS_VERBOSE)
#define MAC_SRS_TXCPUVERBOSE (MAC_SRS_TXCPU|MAC_SRS_VERBOSE)
+#define MAC_SRS_RXINTR (MAC_SRS_RX|MAC_SRS_INTR)
+#define MAC_SRS_TXINTR (MAC_SRS_TX|MAC_SRS_INTR)
static char *
mac_flow_proto2str(uint8_t protocol)
@@ -314,9 +318,28 @@ mac_flow_dcmd_output(uintptr_t addr, uint_t flags, uint_t args)
break;
}
case MAC_FLOW_STATS: {
+ uint64_t totibytes = 0;
+ uint64_t totobytes = 0;
+ mac_soft_ring_set_t *mac_srs;
+ mac_rx_stats_t *mac_rx_stat;
+ mac_tx_stats_t *mac_tx_stat;
+ int i;
+
+ for (i = 0; i < fe.fe_rx_srs_cnt; i++) {
+ mac_srs = (mac_soft_ring_set_t *)(fe.fe_rx_srs[i]);
+ mac_rx_stat = &mac_srs->srs_rx.sr_stat;
+ totibytes += mac_rx_stat->mrs_intrbytes +
+ mac_rx_stat->mrs_pollbytes +
+ mac_rx_stat->mrs_lclbytes;
+ }
+ mac_srs = (mac_soft_ring_set_t *)(fe.fe_tx_srs);
+ if (mac_srs != NULL) {
+ mac_tx_stat = &mac_srs->srs_tx.st_stat;
+ totobytes = mac_tx_stat->mts_obytes;
+ }
mdb_printf("%?p %-32s %16llu %16llu\n",
- addr, fe.fe_flow_name, fe.fe_flowstats.fs_rbytes,
- fe.fe_flowstats.fs_obytes);
+ addr, fe.fe_flow_name, totibytes, totobytes);
+
break;
}
}
@@ -444,6 +467,10 @@ mac_srs_txmode2str(mac_tx_srs_mode_t mode)
return ("BW");
case SRS_TX_BW_FANOUT:
return ("BWFO");
+ case SRS_TX_AGGR:
+ return ("AG");
+ case SRS_TX_BW_AGGR:
+ return ("BWAG");
}
return ("--");
}
@@ -460,6 +487,7 @@ mac_srs_help(void)
"\t-s\tdisplay statistics for RX or TX side\n"
"\t-c\tdisplay CPU binding for RX or TX side\n"
"\t-v\tverbose flag for CPU binding to list cpus\n"
+ "\t-i\tdisplay mac_ring_t and interrupt information\n"
"Note: use -r or -t (to specify RX or TX side respectively) along "
"with -c or -s\n");
mdb_printf("\n%<u>Interpreting TX Modes%</u>\n");
@@ -468,6 +496,8 @@ mac_srs_help(void)
mdb_printf("\t FO --> Fanout\n");
mdb_printf("\t BW --> Bandwidth\n");
mdb_printf("\tBWFO --> Bandwidth Fanout\n");
+ mdb_printf("\t AG --> Aggr\n");
+ mdb_printf("\tBWAG --> Bandwidth Aggr\n");
}
/*
@@ -520,6 +550,7 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
't', MDB_OPT_SETBITS, MAC_SRS_TX, &args,
'c', MDB_OPT_SETBITS, MAC_SRS_CPU, &args,
'v', MDB_OPT_SETBITS, MAC_SRS_VERBOSE, &args,
+ 'i', MDB_OPT_SETBITS, MAC_SRS_INTR, &args,
's', MDB_OPT_SETBITS, MAC_SRS_STAT, &args) != argc) {
return (DCMD_USAGE);
}
@@ -576,7 +607,7 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
"%08x %08x %8d %8d %3d\n",
addr, mci.mci_name, mac_srs_txmode2str(srs.srs_tx.st_mode),
srs.srs_state, srs.srs_type, srs.srs_count, srs.srs_size,
- srs.srs_oth_ring_count);
+ srs.srs_tx_ring_count);
break;
}
case MAC_SRS_RXCPU: {
@@ -596,30 +627,148 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
return (DCMD_OK);
mdb_printf("%?p %-20s %-4d %-4d "
"%-6d %-4d %-7d\n",
- addr, mci.mci_name, mc.mc_ncpus, mc.mc_pollid,
- mc.mc_workerid, mc.mc_intr_cpu, mc.mc_fanout_cnt);
+ addr, mci.mci_name, mc.mc_ncpus, mc.mc_rx_pollid,
+ mc.mc_rx_workerid, mc.mc_rx_intr_cpu, mc.mc_rx_fanout_cnt);
break;
}
case MAC_SRS_TXCPU: {
mac_cpus_t mc = srs.srs_cpu;
+ mac_soft_ring_t *s_ringp, s_ring;
+ boolean_t first = B_TRUE;
+ int i;
if (DCMD_HDRSPEC(flags)) {
- mdb_printf("%?s %-20s %-4s %-6s "
- "%-4s %-7s\n",
- "", "", "NUM", "WORKER",
- "INTR", "FANOUT");
- mdb_printf("%<u>%?s %-20s %-4s %-6s "
- "%-4s %-7s%</u>\n",
- "ADDR", "LINK_NAME", "CPUS", "CPU",
- "CPU", "CPU_CNT");
+ mdb_printf("%?s %-12s %?s %8s %8s %8s\n",
+ "", "", "SOFT", "WORKER", "INTR", "RETARGETED");
+ mdb_printf("%<u>%?s %-12s %?s %8s %8s %8s%</u>\n",
+ "ADDR", "LINK_NAME", "RING", "CPU", "CPU", "CPU");
}
- if ((args & MAC_SRS_TX) && !(srs.srs_type & SRST_TX))
+ if (!(srs.srs_type & SRST_TX))
return (DCMD_OK);
- mdb_printf("%?p %-20s %-4d "
- "%-6d %-4d %-7d\n",
- addr, mci.mci_name, mc.mc_ncpus,
- mc.mc_workerid, mc.mc_intr_cpu, mc.mc_fanout_cnt);
+
+ mdb_printf("%?p %-12s ", addr, mci.mci_name);
+
+ /*
+ * Case of no soft rings, print the info from
+ * mac_srs_tx_t.
+ */
+ if (srs.srs_tx_ring_count == 0) {
+ mdb_printf("%?p %8d %8d %8d\n",
+ 0, mc.mc_tx_fanout_cpus[0],
+ mc.mc_tx_intr_cpu[0],
+ mc.mc_tx_retargeted_cpu[0]);
+ break;
+ }
+
+ for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
+ s_ringp = s_ring.s_ring_next, i++) {
+ (void) mdb_vread(&s_ring, sizeof (s_ring),
+ (uintptr_t)s_ringp);
+ if (first) {
+ mdb_printf("%?p %8d %8d %8d\n",
+ s_ringp, mc.mc_tx_fanout_cpus[i],
+ mc.mc_tx_intr_cpu[i],
+ mc.mc_tx_retargeted_cpu[i]);
+ first = B_FALSE;
+ continue;
+ }
+ mdb_printf("%?s %-12s %?p %8d %8d %8d\n",
+ "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
+ mc.mc_tx_intr_cpu[i], mc.mc_tx_retargeted_cpu[i]);
+ }
+ break;
+ }
+ case MAC_SRS_TXINTR: {
+ mac_cpus_t mc = srs.srs_cpu;
+ mac_soft_ring_t *s_ringp, s_ring;
+ mac_ring_t *m_ringp, m_ring;
+ boolean_t first = B_TRUE;
+ int i;
+
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf("%?s %-12s %?s %8s %?s %6s %6s\n",
+ "", "", "SOFT", "WORKER", "MAC", "", "INTR");
+ mdb_printf("%<u>%?s %-12s %?s %8s %?s %6s %6s%</u>\n",
+ "ADDR", "LINK_NAME", "RING", "CPU", "RING",
+ "SHARED", "CPU");
+ }
+ if (!(srs.srs_type & SRST_TX))
+ return (DCMD_OK);
+
+ mdb_printf("%?p %-12s ", addr, mci.mci_name);
+
+ /*
+ * Case of no soft rings, print the info from
+ * mac_srs_tx_t.
+ */
+ if (srs.srs_tx_ring_count == 0) {
+ m_ringp = srs.srs_tx.st_arg2;
+ if (m_ringp != NULL) {
+ (void) mdb_vread(&m_ring, sizeof (m_ring),
+ (uintptr_t)m_ringp);
+ mdb_printf("%?p %8d %?p %6d %6d\n",
+ 0, mc.mc_tx_fanout_cpus[0], m_ringp,
+ m_ring.mr_info.mri_intr.mi_ddi_shared,
+ mc.mc_tx_retargeted_cpu[0]);
+ } else {
+ mdb_printf("%?p %8d %?p %6d %6d\n",
+ 0, mc.mc_tx_fanout_cpus[0], 0,
+ 0, mc.mc_tx_retargeted_cpu[0]);
+ }
+ break;
+ }
+
+ for (s_ringp = srs.srs_soft_ring_head, i = 0; s_ringp != NULL;
+ s_ringp = s_ring.s_ring_next, i++) {
+ (void) mdb_vread(&s_ring, sizeof (s_ring),
+ (uintptr_t)s_ringp);
+ m_ringp = s_ring.s_ring_tx_arg2;
+ (void) mdb_vread(&m_ring, sizeof (m_ring),
+ (uintptr_t)m_ringp);
+ if (first) {
+ mdb_printf("%?p %8d %?p %6d %6d\n",
+ s_ringp, mc.mc_tx_fanout_cpus[i],
+ m_ringp,
+ m_ring.mr_info.mri_intr.mi_ddi_shared,
+ mc.mc_tx_retargeted_cpu[i]);
+ first = B_FALSE;
+ continue;
+ }
+ mdb_printf("%?s %-12s %?p %8d %?p %6d %6d\n",
+ "", "", s_ringp, mc.mc_tx_fanout_cpus[i],
+ m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
+ mc.mc_tx_retargeted_cpu[i]);
+ }
+ break;
+ }
+ case MAC_SRS_RXINTR: {
+ mac_cpus_t mc = srs.srs_cpu;
+ mac_ring_t *m_ringp, m_ring;
+
+ if (DCMD_HDRSPEC(flags)) {
+ mdb_printf("%?s %-12s %?s %8s %6s %6s\n",
+ "", "", "MAC", "", "POLL", "INTR");
+ mdb_printf("%<u>%?s %-12s %?s %8s %6s %6s%</u>\n",
+ "ADDR", "LINK_NAME", "RING", "SHARED", "CPU",
+ "CPU");
+ }
+ if ((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX))
+ return (DCMD_OK);
+
+ mdb_printf("%?p %-12s ", addr, mci.mci_name);
+
+ m_ringp = srs.srs_ring;
+ if (m_ringp != NULL) {
+ (void) mdb_vread(&m_ring, sizeof (m_ring),
+ (uintptr_t)m_ringp);
+ mdb_printf("%?p %8d %6d %6d\n",
+ m_ringp, m_ring.mr_info.mri_intr.mi_ddi_shared,
+ mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
+ } else {
+ mdb_printf("%?p %8d %6d %6d\n",
+ 0, 0, mc.mc_rx_pollid, mc.mc_rx_intr_cpu);
+ }
break;
}
case MAC_SRS_RXCPUVERBOSE:
@@ -640,8 +789,8 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
((args & MAC_SRS_RX) && (srs.srs_type & SRST_TX)))
return (DCMD_OK);
mdb_printf("%?p %-20s %-20d %-20d\n", addr, mci.mci_name,
- mc.mc_ncpus, mc.mc_fanout_cnt);
- if (mc.mc_ncpus == 0 && mc.mc_fanout_cnt == 0)
+ mc.mc_ncpus, mc.mc_rx_fanout_cnt);
+ if (mc.mc_ncpus == 0 && mc.mc_rx_fanout_cnt == 0)
break;
/* print all cpus and cpus for soft rings */
while (!cpu_done || !fanout_done) {
@@ -658,14 +807,15 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
else
mdb_printf("%*s", len, "");
fanout_done = mac_srs_print_cpu(&fanout_index,
- mc.mc_fanout_cnt, mc.mc_fanout_cpus, NULL);
+ mc.mc_rx_fanout_cnt,
+ mc.mc_rx_fanout_cpus, NULL);
}
mdb_printf("\n");
}
break;
}
case MAC_SRS_RXSTAT: {
- mac_srs_rx_t srs_rx = srs.srs_rx;
+ mac_rx_stats_t *mac_rx_stat = &srs.srs_rx.sr_stat;
if (DCMD_HDRSPEC(flags)) {
mdb_printf("%?s %-16s %8s %8s "
@@ -682,13 +832,14 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
mdb_printf("%?p %-16s %8d "
"%8d %8d "
"%8d %8d\n",
- addr, mci.mci_name, srs_rx.sr_intr_count,
- srs_rx.sr_poll_count, srs_rx.sr_chain_cnt_undr10,
- srs_rx.sr_chain_cnt_10to50, srs_rx.sr_chain_cnt_over50);
+ addr, mci.mci_name, mac_rx_stat->mrs_intrcnt,
+ mac_rx_stat->mrs_pollcnt, mac_rx_stat->mrs_chaincntundr10,
+ mac_rx_stat->mrs_chaincnt10to50,
+ mac_rx_stat->mrs_chaincntover50);
break;
}
case MAC_SRS_TXSTAT: {
- mac_srs_tx_t srs_tx = srs.srs_tx;
+ mac_tx_stats_t *mac_tx_stat = &srs.srs_tx.st_stat;
mac_soft_ring_t *s_ringp, s_ring;
boolean_t first = B_TRUE;
@@ -708,10 +859,11 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
* Case of no soft rings, print the info from
* mac_srs_tx_t.
*/
- if (srs.srs_oth_ring_count == 0) {
+ if (srs.srs_tx_ring_count == 0) {
mdb_printf("%?p %8d %8d %8d\n",
- 0, srs_tx.st_drop_count, srs_tx.st_blocked_cnt,
- srs_tx.st_unblocked_cnt);
+ 0, mac_tx_stat->mts_sdrops,
+ mac_tx_stat->mts_blockcnt,
+ mac_tx_stat->mts_unblockcnt);
break;
}
@@ -719,18 +871,19 @@ mac_srs_dcmd(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
s_ringp = s_ring.s_ring_next) {
(void) mdb_vread(&s_ring, sizeof (s_ring),
(uintptr_t)s_ringp);
+ mac_tx_stat = &s_ring.s_st_stat;
if (first) {
mdb_printf("%?p %8d %8d %8d\n",
- s_ringp, s_ring.s_ring_drops,
- s_ring.s_ring_blocked_cnt,
- s_ring.s_ring_unblocked_cnt);
+ s_ringp, mac_tx_stat->mts_sdrops,
+ mac_tx_stat->mts_blockcnt,
+ mac_tx_stat->mts_unblockcnt);
first = B_FALSE;
continue;
}
mdb_printf("%?s %-20s %?p %8d %8d %8d\n",
- "", "", s_ringp, s_ring.s_ring_drops,
- s_ring.s_ring_blocked_cnt,
- s_ring.s_ring_unblocked_cnt);
+ "", "", s_ringp, mac_tx_stat->mts_sdrops,
+ mac_tx_stat->mts_blockcnt,
+ mac_tx_stat->mts_unblockcnt);
}
break;
}
@@ -853,8 +1006,9 @@ mac_ring_help(void)
static const mdb_dcmd_t dcmds[] = {
{"mac_flow", "?[-u] [-aprtsm]", "display Flow Entry structures",
mac_flow_dcmd, mac_flow_help},
- {"mac_srs", "?[ -r[s|c[v]] | -t[s|c[v]] ]", "display MAC Soft Ring Set"
- " structures", mac_srs_dcmd, mac_srs_help},
+ {"mac_srs", "?[ -r[i|s|c[v]] | -t[i|s|c[v]] ]",
+ "display MAC Soft Ring Set" " structures", mac_srs_dcmd,
+ mac_srs_help},
{"mac_ring", "?", "display MAC ring (hardware) structures",
mac_ring_dcmd, mac_ring_help},
{ NULL }
diff --git a/usr/src/cmd/zoneadmd/vplat.c b/usr/src/cmd/zoneadmd/vplat.c
index cb2eff9995..8dce6b20aa 100644
--- a/usr/src/cmd/zoneadmd/vplat.c
+++ b/usr/src/cmd/zoneadmd/vplat.c
@@ -2530,6 +2530,7 @@ static int
add_datalink(zlog_t *zlogp, char *zone_name, datalink_id_t linkid, char *dlname)
{
dladm_status_t err;
+ boolean_t cpuset, poolset;
/* First check if it's in use by global zone. */
if (zonecfg_ifname_exists(AF_INET, dlname) ||
@@ -2547,6 +2548,36 @@ add_datalink(zlog_t *zlogp, char *zone_name, datalink_id_t linkid, char *dlname)
"WARNING: unable to add network interface");
return (-1);
}
+
+ /*
+ * Set the pool of this link if the zone has a pool and
+ * neither the cpus nor the pool datalink property is
+ * already set.
+ */
+ err = dladm_linkprop_is_set(dld_handle, linkid, DLADM_PROP_VAL_CURRENT,
+ "cpus", &cpuset);
+ if (err != DLADM_STATUS_OK) {
+ zdlerror(zlogp, err, dlname,
+ "WARNING: unable to check if cpus link property is set");
+ }
+ err = dladm_linkprop_is_set(dld_handle, linkid, DLADM_PROP_VAL_CURRENT,
+ "pool", &poolset);
+ if (err != DLADM_STATUS_OK) {
+ zdlerror(zlogp, err, dlname,
+ "WARNING: unable to check if pool link property is set");
+ }
+
+ if ((strlen(pool_name) != 0) && !cpuset && !poolset) {
+ err = dladm_set_linkprop(dld_handle, linkid, "pool",
+ &pool_name, 1, DLADM_OPT_ACTIVE);
+ if (err != DLADM_STATUS_OK) {
+ zerror(zlogp, B_FALSE, "WARNING: unable to set "
+ "pool %s to datalink %s", pool_name, dlname);
+ bzero(pool_name, MAXPATHLEN);
+ }
+ } else {
+ bzero(pool_name, MAXPATHLEN);
+ }
return (0);
}
@@ -2644,6 +2675,72 @@ configure_exclusive_network_interfaces(zlog_t *zlogp)
}
static int
+remove_datalink_pool(zlog_t *zlogp, zoneid_t zoneid)
+{
+ ushort_t flags;
+ zone_iptype_t iptype;
+ int i, dlnum = 0;
+ datalink_id_t *dllink, *dllinks = NULL;
+ dladm_status_t err;
+
+ if (strlen(pool_name) == 0)
+ return (0);
+
+ if (zone_getattr(zoneid, ZONE_ATTR_FLAGS, &flags,
+ sizeof (flags)) < 0) {
+ if (vplat_get_iptype(zlogp, &iptype) < 0) {
+ zerror(zlogp, B_TRUE, "unable to determine "
+ "ip-type");
+ return (-1);
+ }
+ } else {
+ if (flags & ZF_NET_EXCL)
+ iptype = ZS_EXCLUSIVE;
+ else
+ iptype = ZS_SHARED;
+ }
+
+ if (iptype == ZS_EXCLUSIVE) {
+ /*
+ * Get the datalink count and for each datalink,
+ * attempt to clear the pool property and clear
+ * the pool_name.
+ */
+ if (zone_list_datalink(zoneid, &dlnum, NULL) != 0) {
+ zerror(zlogp, B_TRUE, "unable to count network "
+ "interfaces");
+ return (-1);
+ }
+
+ if (dlnum == 0)
+ return (0);
+
+ if ((dllinks = malloc(dlnum * sizeof (datalink_id_t)))
+ == NULL) {
+ zerror(zlogp, B_TRUE, "memory allocation failed");
+ return (-1);
+ }
+ if (zone_list_datalink(zoneid, &dlnum, dllinks) != 0) {
+ zerror(zlogp, B_TRUE, "unable to list network "
+ "interfaces");
+ return (-1);
+ }
+
+ bzero(pool_name, MAXPATHLEN);
+ for (i = 0, dllink = dllinks; i < dlnum; i++, dllink++) {
+ err = dladm_set_linkprop(dld_handle, *dllink, "pool",
+ NULL, 0, DLADM_OPT_ACTIVE);
+ if (err != DLADM_STATUS_OK) {
+ zerror(zlogp, B_TRUE,
+ "WARNING: unable to clear pool");
+ }
+ }
+ free(dllinks);
+ }
+ return (0);
+}
+
+static int
unconfigure_exclusive_network_interfaces(zlog_t *zlogp, zoneid_t zoneid)
{
int dlnum = 0;
@@ -4006,6 +4103,7 @@ setup_zone_rm(zlog_t *zlogp, char *zone_name, zoneid_t zoneid)
zerror(zlogp, B_FALSE, "WARNING: %s",
zonecfg_strerror(res));
}
+ (void) zonecfg_get_poolname(handle, zone_name, pool_name, MAXPATHLEN);
zonecfg_fini_handle(handle);
return (Z_OK);
@@ -4253,6 +4351,12 @@ vplat_create(zlog_t *zlogp, zone_mnt_t mount_cmd)
goto error;
}
+ if ((pool_name = malloc(MAXPATHLEN)) == NULL) {
+ zerror(zlogp, B_TRUE, "memory allocation failed");
+ return (Z_NOMEM);
+ }
+ bzero(pool_name, MAXPATHLEN);
+
/*
* The following actions are not performed when merely mounting a zone
* for administrative use.
@@ -4575,6 +4679,11 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
goto error;
}
+ if (remove_datalink_pool(zlogp, zoneid) != 0) {
+ zerror(zlogp, B_FALSE, "unable clear datalink pool property");
+ goto error;
+ }
+
if (zone_shutdown(zoneid) != 0) {
zerror(zlogp, B_TRUE, "unable to shutdown zone");
goto error;
@@ -4699,6 +4808,8 @@ vplat_teardown(zlog_t *zlogp, boolean_t unmount_cmd, boolean_t rebooting)
}
}
+ free(pool_name);
+
remove_mlps(zlogp, zoneid);
if (zone_destroy(zoneid) != 0) {
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.c b/usr/src/cmd/zoneadmd/zoneadmd.c
index 90803d8770..743370c1ad 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.c
+++ b/usr/src/cmd/zoneadmd/zoneadmd.c
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -106,6 +106,7 @@
static char *progname;
char *zone_name; /* zone which we are managing */
+char *pool_name;
char default_brand[MAXNAMELEN];
char brand_name[MAXNAMELEN];
boolean_t zone_isnative;
diff --git a/usr/src/cmd/zoneadmd/zoneadmd.h b/usr/src/cmd/zoneadmd/zoneadmd.h
index 25ac7bf801..da6aa369ed 100644
--- a/usr/src/cmd/zoneadmd/zoneadmd.h
+++ b/usr/src/cmd/zoneadmd/zoneadmd.h
@@ -20,7 +20,7 @@
*/
/*
- * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
@@ -86,6 +86,7 @@ extern mutex_t msglock;
extern boolean_t in_death_throes;
extern boolean_t bringup_failure_recovery;
extern char *zone_name;
+extern char *pool_name;
extern char brand_name[MAXNAMELEN];
extern char default_brand[MAXNAMELEN];
extern char boot_args[BOOTARGS_MAX];