diff options
Diffstat (limited to 'usr/src/cmd')
| -rw-r--r-- | usr/src/cmd/Makefile | 4 | ||||
| -rw-r--r-- | usr/src/cmd/acctadm/main.c | 53 | ||||
| -rw-r--r-- | usr/src/cmd/dladm/dladm.c | 104 | ||||
| -rw-r--r-- | usr/src/cmd/dlstat/Makefile | 49 | ||||
| -rw-r--r-- | usr/src/cmd/dlstat/dlstat.c | 2457 | ||||
| -rw-r--r-- | usr/src/cmd/dlstat/dlstat.xcl | 110 | ||||
| -rw-r--r-- | usr/src/cmd/flowadm/Makefile | 4 | ||||
| -rw-r--r-- | usr/src/cmd/flowadm/flowadm.c | 610 | ||||
| -rw-r--r-- | usr/src/cmd/flowstat/Makefile | 70 | ||||
| -rw-r--r-- | usr/src/cmd/flowstat/flowstat.c | 1149 | ||||
| -rw-r--r-- | usr/src/cmd/flowstat/flowstat.xcl | 73 | ||||
| -rw-r--r-- | usr/src/cmd/mdb/common/modules/mac/mac.c | 230 | ||||
| -rw-r--r-- | usr/src/cmd/zoneadmd/vplat.c | 111 | ||||
| -rw-r--r-- | usr/src/cmd/zoneadmd/zoneadmd.c | 3 | ||||
| -rw-r--r-- | usr/src/cmd/zoneadmd/zoneadmd.h | 3 |
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]; |
