summaryrefslogtreecommitdiff
path: root/usr/src/lib/libdladm/common/usage.c
diff options
context:
space:
mode:
authorEric Cheng <none@none>2008-12-04 18:16:10 -0800
committerEric Cheng <none@none>2008-12-04 18:16:10 -0800
commitda14cebe459d3275048785f25bd869cb09b5307f (patch)
treea394d2c61ec4d7591782a4a5db4e3a157c3ca89a /usr/src/lib/libdladm/common/usage.c
parent03361682bf38acf5bcc36ee83a0d6277731eee68 (diff)
downloadillumos-joyent-da14cebe459d3275048785f25bd869cb09b5307f.tar.gz
PSARC/2006/357 Crossbow - Network Virtualization and Resource Management
6498311 Crossbow - Network Virtualization and Resource Management 6402493 DLPI provider loopback behavior should be improved 6453165 move mac capabs definitions outside mac.h 6338667 Need ability to use NAT for non-global zones 6692884 several threads hung due to deadlock scenario between aggr and mac 6768302 dls: soft_ring_bind/unbind race can panic in thread_affinity_set with cpu_id == -1 6635849 race between lacp_xmit_sm() and aggr_m_stop() ends in panic 6742712 potential message double free in the aggr driver 6754299 a potential race between aggr_m_tx() and aggr_port_delete() 6485324 mi_data_lock recursively held when enabling promiscuous mode on an aggregation 6442559 Forwarding perf bottleneck due to mac_rx() calls 6505462 assertion failure after removing a port from a snooped aggregation 6716664 need to add src/dst IP address to soft ring fanout --HG-- rename : usr/src/uts/common/io/dls/dls_soft_ring.c => usr/src/uts/common/io/mac/mac_soft_ring.c rename : usr/src/uts/common/inet/ip/ip_cksum.c => usr/src/uts/common/os/ip_cksum.c rename : usr/src/uts/common/inet/sctp_crc32.c => usr/src/uts/common/os/sctp_crc32.c rename : usr/src/uts/common/sys/dls_soft_ring.h => usr/src/uts/common/sys/mac_soft_ring.h
Diffstat (limited to 'usr/src/lib/libdladm/common/usage.c')
-rw-r--r--usr/src/lib/libdladm/common/usage.c1437
1 files changed, 1437 insertions, 0 deletions
diff --git a/usr/src/lib/libdladm/common/usage.c b/usr/src/lib/libdladm/common/usage.c
new file mode 100644
index 0000000000..07ef7bbb22
--- /dev/null
+++ b/usr/src/lib/libdladm/common/usage.c
@@ -0,0 +1,1437 @@
+/*
+ * 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 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <exacct.h>
+#include <libdladm.h>
+
+#define TIMEBUFLEN 20
+#define GBIT 1000000000
+#define MBIT 1000000
+#define KBIT 1000
+
+#define NET_RESET_TOT(tbytes, ttime, tibytes, tobytes, step) { \
+ (step) = 1; \
+ (tbytes) = 0; \
+ (ttime) = 0; \
+ (tibytes) = 0; \
+ (tobytes) = 0; \
+ }
+
+/* Flow/Link Descriptor */
+typedef struct net_desc_s {
+ char net_desc_name[LIFNAMSIZ];
+ char net_desc_devname[LIFNAMSIZ];
+ uchar_t net_desc_ehost[ETHERADDRL];
+ uchar_t net_desc_edest[ETHERADDRL];
+ ushort_t net_desc_vlan_tpid;
+ ushort_t net_desc_vlan_tci;
+ ushort_t net_desc_sap;
+ ushort_t net_desc_cpuid;
+ ushort_t net_desc_priority;
+ uint64_t net_desc_bw_limit;
+ in6_addr_t net_desc_saddr;
+ in6_addr_t net_desc_daddr;
+ boolean_t net_desc_isv4;
+ in_port_t net_desc_sport;
+ in_port_t net_desc_dport;
+ uint8_t net_desc_protocol;
+ uint8_t net_desc_dsfield;
+ boolean_t net_desc_newrec;
+} net_desc_t;
+
+/* Time structure: Year, Month, Day, Hour, Min, Sec */
+typedef struct net_time_s {
+ int net_time_yr;
+ int net_time_mon;
+ int net_time_day;
+ int net_time_hr;
+ int net_time_min;
+ int net_time_sec;
+} net_time_t;
+
+/* Flow/Link Stats */
+typedef struct net_stat_s {
+ char net_stat_name[LIFNAMSIZ];
+ uint64_t net_stat_ibytes;
+ uint64_t net_stat_obytes;
+ uint64_t net_stat_ipackets;
+ uint64_t net_stat_opackets;
+ uint64_t net_stat_ierrors;
+ uint64_t net_stat_oerrors;
+ uint64_t net_stat_tibytes;
+ uint64_t net_stat_tobytes;
+ uint64_t net_stat_tipackets;
+ uint64_t net_stat_topackets;
+ uint64_t net_stat_tierrors;
+ uint64_t net_stat_toerrors;
+ uint64_t net_stat_ctime;
+ uint64_t net_stat_tdiff;
+ net_time_t net_stat_time;
+ struct net_stat_s *net_stat_next;
+ net_desc_t *net_stat_desc;
+ boolean_t net_stat_isref;
+} net_stat_t;
+
+/* Used to create the [gnu]plot file */
+typedef struct net_plot_entry_s {
+ char *net_pe_name;
+ uint64_t net_pe_tottime;
+ uint64_t net_pe_totbytes;
+ uint64_t net_pe_totibytes;
+ uint64_t net_pe_totobytes;
+ uint64_t net_pe_lasttime;
+} net_plot_entry_t;
+
+/* Stats entry */
+typedef struct net_entry_s {
+ net_desc_t *net_entry_desc;
+ net_stat_t *net_entry_shead;
+ net_stat_t *net_entry_stail;
+ int net_entry_scount;
+ net_stat_t *net_entry_sref;
+ net_stat_t *net_entry_tstats;
+ uint64_t net_entry_ttime;
+ struct net_entry_s *net_entry_next;
+} net_entry_t;
+
+/* Time sorted list */
+typedef struct net_time_entry_s {
+ net_stat_t *my_time_stat;
+ struct net_time_entry_s *net_time_entry_next;
+ struct net_time_entry_s *net_time_entry_prev;
+} net_time_entry_t;
+
+/* The parsed table */
+typedef struct net_table_s {
+ /* List of stats */
+ net_entry_t *net_table_head;
+ net_entry_t *net_table_tail;
+ int net_entries;
+
+ /*
+ * Optimization I : List sorted by time, i.e:
+ * Time Resource ..
+ * -------------------------------
+ * 11.15.10 bge0
+ * 11.15.10 ce0
+ * 11.15.10 vnic1
+ * 11.15.15 bge0
+ * 11.15.15 ce0
+ * 11.15.15 vnic1
+ */
+ net_time_entry_t *net_time_head;
+ net_time_entry_t *net_time_tail;
+
+ /*
+ * Optimization II : List sorted by resources
+ * Time Resource ..
+ * -------------------------------
+ * 11.15.10 bge0
+ * 11.15.15 bge0
+ * 11.15.10 ce0
+ * 11.15.15 ce0
+ * 11.15.10 vnic1
+ * 11.15.15 vnic1
+ */
+ net_time_entry_t *net_ctime_head;
+ net_time_entry_t *net_ctime_tail;
+
+ /* Common to both the above (sorted) lists. */
+ int net_time_entries;
+} net_table_t;
+
+#define NET_DATE_GREATER 0
+#define NET_DATE_LESSER 1
+#define NET_DATE_EQUAL 2
+
+#define NET_TIME_GREATER 0
+#define NET_TIME_LESSER 1
+#define NET_TIME_EQUAL 2
+
+#ifndef _LP64
+#define FMT_UINT64 "%-15llu"
+#else
+#define FMT_UINT64 "%-15lu"
+#endif
+
+/*
+ * Given a timebuf of the form M/D/Y,H:M:S break it into individual elements.
+ */
+static void
+dissect_time(char *tbuf, net_time_t *nt)
+{
+ char *d;
+ char *t;
+ char *dd;
+ char *h;
+ char *endp;
+
+ if (tbuf == NULL || nt == NULL)
+ return;
+
+ d = strtok(tbuf, ","); /* Date */
+ t = strtok(NULL, ","); /* Time */
+
+ /* Month */
+ dd = strtok(d, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_mon = strtol(dd, &endp, 10);
+
+ /* Day */
+ dd = strtok(NULL, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_day = strtol(dd, &endp, 10);
+
+ /* Year */
+ dd = strtok(NULL, "/");
+ if (dd == NULL)
+ return;
+ nt->net_time_yr = strtol(dd, &endp, 10);
+ if (strlen(dd) <= 2)
+ nt->net_time_yr += 2000;
+
+ if (t == NULL)
+ return;
+
+ /* Hour */
+ h = strtok(t, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_hr = strtol(h, &endp, 10);
+
+ /* Min */
+ h = strtok(NULL, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_min = strtol(h, &endp, 10);
+
+ /* Sec */
+ h = strtok(NULL, ":");
+ if (h == NULL)
+ return;
+ nt->net_time_sec = strtol(h, &endp, 10);
+}
+
+/* Get a stat item from an object in the exacct file */
+static void
+add_stat_item(ea_object_t *o, net_stat_t *ns)
+{
+ switch (o->eo_catalog & EXT_TYPE_MASK) {
+ case EXT_STRING:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_NAME) {
+ (void) strncpy(ns->net_stat_name, o->eo_item.ei_string,
+ strlen(o->eo_item.ei_string));
+ }
+ break;
+ case EXT_UINT64:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_STATS_CURTIME) {
+ time_t _time;
+ char timebuf[TIMEBUFLEN];
+
+ ns->net_stat_ctime = o->eo_item.ei_uint64;
+ _time = ns->net_stat_ctime;
+ (void) strftime(timebuf, sizeof (timebuf),
+ "%m/%d/%Y,%T\n", localtime(&_time));
+ dissect_time(timebuf, &ns->net_stat_time);
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IBYTES) {
+ ns->net_stat_ibytes = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OBYTES) {
+ ns->net_stat_obytes = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IPKTS) {
+ ns->net_stat_ipackets = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OPKTS) {
+ ns->net_stat_opackets = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_IERRPKTS) {
+ ns->net_stat_ierrors = o->eo_item.ei_uint64;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_STATS_OERRPKTS) {
+ ns->net_stat_oerrors = o->eo_item.ei_uint64;
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* Get a description item from an object in the exacct file */
+static void
+add_desc_item(ea_object_t *o, net_desc_t *nd)
+{
+ switch (o->eo_catalog & EXT_TYPE_MASK) {
+ case EXT_STRING:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_NAME) {
+ (void) strncpy(nd->net_desc_name, o->eo_item.ei_string,
+ strlen(o->eo_item.ei_string));
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DEVNAME) {
+ (void) strncpy(nd->net_desc_devname,
+ o->eo_item.ei_string, strlen(o->eo_item.ei_string));
+ }
+ break;
+ case EXT_UINT8:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_PROTOCOL) {
+ nd->net_desc_protocol = o->eo_item.ei_uint8;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DSFIELD) {
+ nd->net_desc_dsfield = o->eo_item.ei_uint8;
+ }
+ break;
+ case EXT_UINT16:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_SPORT) {
+ nd->net_desc_sport = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_DPORT) {
+ nd->net_desc_dport = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_SAP) {
+ nd->net_desc_sap = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_VLAN_TPID) {
+ nd->net_desc_vlan_tpid = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_VLAN_TCI) {
+ nd->net_desc_vlan_tci = o->eo_item.ei_uint16;
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_PRIORITY) {
+ nd->net_desc_priority = o->eo_item.ei_uint16;
+ }
+ break;
+ case EXT_UINT32:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4SADDR ||
+ (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V4DADDR) {
+ struct in_addr addr;
+
+ addr.s_addr = htonl(o->eo_item.ei_uint32);
+
+ if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_V4SADDR) {
+ IN6_INADDR_TO_V4MAPPED(&addr,
+ &nd->net_desc_saddr);
+ } else {
+ IN6_INADDR_TO_V4MAPPED(&addr,
+ &nd->net_desc_daddr);
+ }
+ }
+ break;
+ case EXT_UINT64:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_BWLIMIT)
+ nd->net_desc_bw_limit = o->eo_item.ei_uint64;
+ break;
+ case EXT_RAW:
+ if ((o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6SADDR ||
+ (o->eo_catalog & EXD_DATA_MASK) == EXD_NET_DESC_V6DADDR) {
+ in6_addr_t addr;
+
+ addr = *(in6_addr_t *)o->eo_item.ei_raw;
+ if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_V6SADDR) {
+ nd->net_desc_saddr = addr;
+ } else {
+ nd->net_desc_daddr = addr;
+ }
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_EHOST) {
+ bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_ehost,
+ ETHERADDRL);
+ } else if ((o->eo_catalog & EXD_DATA_MASK) ==
+ EXD_NET_DESC_EDEST) {
+ bcopy((uchar_t *)o->eo_item.ei_raw, nd->net_desc_edest,
+ ETHERADDRL);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+/* Add a description item to the table */
+static dladm_status_t
+add_desc_to_tbl(net_table_t *net_table, net_desc_t *nd)
+{
+ net_entry_t *ne;
+
+ if ((ne = calloc(1, sizeof (net_entry_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if ((ne->net_entry_tstats = calloc(1, sizeof (net_stat_t))) == NULL) {
+ free(ne);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ ne->net_entry_desc = nd;
+ ne->net_entry_shead = NULL;
+ ne->net_entry_stail = NULL;
+ ne->net_entry_scount = 0;
+
+ if (net_table->net_table_head == NULL) {
+ net_table->net_table_head = ne;
+ net_table->net_table_tail = ne;
+ } else {
+ net_table->net_table_tail->net_entry_next = ne;
+ net_table->net_table_tail = ne;
+ }
+ net_table->net_entries++;
+ return (DLADM_STATUS_OK);
+}
+
+/* Compare dates and return if t1 is equal, greater or lesser than t2 */
+static int
+compare_date(net_time_t *t1, net_time_t *t2)
+{
+ if (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon == t2->net_time_mon &&
+ t1->net_time_day == t2->net_time_day) {
+ return (NET_DATE_EQUAL);
+ }
+ if (t1->net_time_yr > t2->net_time_yr ||
+ (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon > t2->net_time_mon) ||
+ (t1->net_time_yr == t2->net_time_yr &&
+ t1->net_time_mon == t2->net_time_mon &&
+ t1->net_time_day > t2->net_time_day)) {
+ return (NET_DATE_GREATER);
+ }
+ return (NET_DATE_LESSER);
+}
+
+/* Compare times and return if t1 is equal, greater or lesser than t2 */
+static int
+compare_time(net_time_t *t1, net_time_t *t2)
+{
+ int cd;
+
+ cd = compare_date(t1, t2);
+
+ if (cd == NET_DATE_GREATER) {
+ return (NET_TIME_GREATER);
+ } else if (cd == NET_DATE_LESSER) {
+ return (NET_TIME_LESSER);
+ } else {
+ if (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min == t2->net_time_min &&
+ t1->net_time_sec == t2->net_time_sec) {
+ return (NET_TIME_EQUAL);
+ }
+ if (t1->net_time_hr > t2->net_time_hr ||
+ (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min > t2->net_time_min) ||
+ (t1->net_time_hr == t2->net_time_hr &&
+ t1->net_time_min == t2->net_time_min &&
+ t1->net_time_sec > t2->net_time_sec)) {
+ return (NET_TIME_GREATER);
+ }
+ }
+ return (NET_TIME_LESSER);
+}
+
+/*
+ * Given a start and end time and start and end entries check if the
+ * times are within the range, and adjust, if needed.
+ */
+static dladm_status_t
+chk_time_bound(net_time_t *s, net_time_t *e, net_time_t *sns,
+ net_time_t *ens)
+{
+ if (s != NULL && e != NULL) {
+ if (compare_time(s, e) == NET_TIME_GREATER)
+ return (DLADM_STATUS_BADTIMEVAL);
+ }
+ if (s != NULL) {
+ if (compare_time(s, sns) == NET_TIME_LESSER) {
+ s->net_time_yr = sns->net_time_yr;
+ s->net_time_mon = sns->net_time_mon;
+ s->net_time_day = sns->net_time_day;
+ s->net_time_hr = sns->net_time_hr;
+ s->net_time_min = sns->net_time_min;
+ s->net_time_sec = sns->net_time_sec;
+ }
+ }
+ if (e != NULL) {
+ if (compare_time(e, ens) == NET_TIME_GREATER) {
+ e->net_time_yr = ens->net_time_yr;
+ e->net_time_mon = ens->net_time_mon;
+ e->net_time_day = ens->net_time_day;
+ e->net_time_hr = ens->net_time_hr;
+ e->net_time_min = ens->net_time_min;
+ e->net_time_sec = ens->net_time_sec;
+ }
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Given a start and end time (strings), convert them into net_time_t
+ * and also check for the range given the head and tail of the list.
+ * If stime is lower then head or etime is greated than tail, adjust.
+ */
+static dladm_status_t
+get_time_range(net_time_entry_t *head, net_time_entry_t *tail,
+ net_time_t *st, net_time_t *et, char *stime, char *etime)
+{
+ bzero(st, sizeof (net_time_t));
+ bzero(et, sizeof (net_time_t));
+
+ if (stime == NULL && etime == NULL)
+ return (0);
+
+ if (stime != NULL)
+ dissect_time(stime, st);
+ if (etime != NULL)
+ dissect_time(etime, et);
+
+ if (stime != NULL || etime != NULL) {
+ return (chk_time_bound(stime == NULL ? NULL : st,
+ etime == NULL ? NULL : et,
+ &head->my_time_stat->net_stat_time,
+ &tail->my_time_stat->net_stat_time));
+ }
+ return (0);
+}
+
+/*
+ * Walk the list from a given starting point and return when we find
+ * an entry that is greater or equal to st. lasttime will point to the
+ * previous time entry.
+ */
+static void
+get_starting_point(net_time_entry_t *head, net_time_entry_t **start,
+ net_time_t *st, char *stime, uint64_t *lasttime)
+{
+ net_time_entry_t *next = head;
+
+ if (head == NULL) {
+ *start = NULL;
+ return;
+ }
+ if (stime == NULL) {
+ *start = head;
+ *lasttime = head->my_time_stat->net_stat_ctime;
+ return;
+ }
+ *start = NULL;
+ while (next != NULL) {
+ if (compare_time(st,
+ &next->my_time_stat->net_stat_time) != NET_TIME_LESSER) {
+ *lasttime = next->my_time_stat->net_stat_ctime;
+ next = next->net_time_entry_next;
+ continue;
+ }
+ *start = next;
+ break;
+ }
+}
+
+/*
+ * Point entry (pe) functions
+ */
+/* Clear all the counters. Done after the contents are written to the file */
+static void
+clear_pe(net_plot_entry_t *pe, int entries, int *pentries)
+{
+ int count;
+
+ for (count = 0; count < entries; count++) {
+ pe[count].net_pe_totbytes = 0;
+ pe[count].net_pe_totibytes = 0;
+ pe[count].net_pe_totobytes = 0;
+ pe[count].net_pe_tottime = 0;
+ }
+ *pentries = 0;
+}
+
+/* Update an entry in the point entry table */
+static void
+update_pe(net_plot_entry_t *pe, net_stat_t *nns, int nentries,
+ int *pentries, uint64_t lasttime)
+{
+ int count;
+
+ for (count = 0; count < nentries; count++) {
+ if ((strlen(nns->net_stat_name) ==
+ strlen(pe[count].net_pe_name)) &&
+ (strncmp(pe[count].net_pe_name, nns->net_stat_name,
+ strlen(nns->net_stat_name)) == 0)) {
+ break;
+ }
+ }
+ if (count == nentries)
+ return;
+
+ if (pe[count].net_pe_totbytes == 0)
+ pe[count].net_pe_lasttime = lasttime;
+
+ pe[count].net_pe_totbytes += nns->net_stat_ibytes +
+ nns->net_stat_obytes;
+ pe[count].net_pe_tottime += nns->net_stat_tdiff;
+ pe[count].net_pe_totibytes += nns->net_stat_ibytes;
+ pe[count].net_pe_totobytes += nns->net_stat_obytes;
+ (*pentries)++;
+}
+
+/* Flush the contents of the point entry table to the file. */
+static void
+add_pe_to_file(int (*fn)(dladm_usage_t *, void *), net_plot_entry_t *pe,
+ net_stat_t *ns, int entries, void *arg)
+{
+ int count;
+ dladm_usage_t usage;
+ uint64_t tottime;
+
+ bcopy(&ns->net_stat_ctime, &usage.du_etime, sizeof (usage.du_etime));
+ for (count = 0; count < entries; count++) {
+ bcopy(pe[count].net_pe_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&pe[count].net_pe_lasttime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ usage.du_rbytes = pe[count].net_pe_totibytes;
+ usage.du_obytes = pe[count].net_pe_totobytes;
+ tottime = pe[count].net_pe_tottime;
+ usage.du_bandwidth = (tottime > 0) ?
+ ((pe[count].net_pe_totbytes * 8) / tottime) : 0;
+ usage.du_last = (count == entries-1);
+ fn(&usage, arg);
+ }
+}
+
+/*
+ * Net entry functions
+ */
+static net_entry_t *
+get_ne_from_table(net_table_t *net_table, char *name)
+{
+ int count;
+ net_desc_t *nd;
+ net_entry_t *ne = net_table->net_table_head;
+
+ for (count = 0; count < net_table->net_entries; count++) {
+ nd = ne->net_entry_desc;
+ if ((strlen(name) == strlen(nd->net_desc_name)) &&
+ (strncmp(name, nd->net_desc_name, strlen(name)) == 0)) {
+ return (ne);
+ }
+ ne = ne->net_entry_next;
+ }
+ return (NULL);
+}
+
+/* Get the entry for the descriptor, if it exists */
+static net_desc_t *
+get_ndesc(net_table_t *net_table, net_desc_t *nd)
+{
+ int count;
+ net_desc_t *nd1;
+ net_entry_t *ne = net_table->net_table_head;
+
+ for (count = 0; count < net_table->net_entries; count++) {
+ nd1 = ne->net_entry_desc;
+ if (strlen(nd1->net_desc_name) == strlen(nd->net_desc_name) &&
+ strlen(nd1->net_desc_devname) ==
+ strlen(nd->net_desc_devname) &&
+ strncmp(nd1->net_desc_name, nd->net_desc_name,
+ strlen(nd1->net_desc_name)) == 0 &&
+ strncmp(nd1->net_desc_devname, nd->net_desc_devname,
+ strlen(nd1->net_desc_devname)) == 0 &&
+ bcmp(nd1->net_desc_ehost, nd->net_desc_ehost,
+ ETHERADDRL) == 0 &&
+ bcmp(nd1->net_desc_edest, nd->net_desc_edest,
+ ETHERADDRL) == 0 &&
+ nd1->net_desc_vlan_tpid == nd->net_desc_vlan_tpid &&
+ nd1->net_desc_vlan_tci == nd->net_desc_vlan_tci &&
+ nd1->net_desc_sap == nd->net_desc_sap &&
+ nd1->net_desc_cpuid == nd->net_desc_cpuid &&
+ nd1->net_desc_priority == nd->net_desc_priority &&
+ nd1->net_desc_bw_limit == nd->net_desc_bw_limit &&
+ nd1->net_desc_sport == nd->net_desc_sport &&
+ nd1->net_desc_dport == nd->net_desc_dport &&
+ nd1->net_desc_protocol == nd->net_desc_protocol &&
+ nd1->net_desc_dsfield == nd->net_desc_dsfield &&
+ IN6_ARE_ADDR_EQUAL(&nd1->net_desc_saddr,
+ &nd->net_desc_saddr) &&
+ IN6_ARE_ADDR_EQUAL(&nd1->net_desc_daddr,
+ &nd->net_desc_daddr)) {
+ return (nd1);
+ }
+ ne = ne->net_entry_next;
+ }
+ return (NULL);
+}
+
+/*
+ * Update the stat entries. The stats in the file are cumulative, so in order
+ * to have increments, we maintain a reference stat entry, which contains
+ * the stats when the record was first written and a total stat entry, which
+ * maintains the running count. When we want to add a stat entry, if it
+ * the reference stat entry, we don't come here. For subsequent entries,
+ * we get the increment by subtracting the current value from the reference
+ * stat and the total stat.
+ */
+static void
+update_stats(net_stat_t *ns1, net_entry_t *ne, net_stat_t *ref)
+{
+
+ /* get the increment */
+ ns1->net_stat_ibytes -= (ref->net_stat_ibytes + ref->net_stat_tibytes);
+ ns1->net_stat_obytes -= (ref->net_stat_obytes + ref->net_stat_tobytes);
+ ns1->net_stat_ipackets -= (ref->net_stat_ipackets +
+ ref->net_stat_tipackets);
+ ns1->net_stat_opackets -= (ref->net_stat_opackets +
+ ref->net_stat_topackets);
+ ns1->net_stat_ierrors -= (ref->net_stat_ierrors +
+ ref->net_stat_tierrors);
+ ns1->net_stat_oerrors -= (ref->net_stat_oerrors +
+ ref->net_stat_toerrors);
+
+ /* update total bytes */
+ ref->net_stat_tibytes += ns1->net_stat_ibytes;
+ ref->net_stat_tobytes += ns1->net_stat_obytes;
+ ref->net_stat_tipackets += ns1->net_stat_ipackets;
+ ref->net_stat_topackets += ns1->net_stat_opackets;
+ ref->net_stat_tierrors += ns1->net_stat_ierrors;
+ ref->net_stat_toerrors += ns1->net_stat_oerrors;
+
+ ne->net_entry_tstats->net_stat_ibytes += ns1->net_stat_ibytes;
+ ne->net_entry_tstats->net_stat_obytes += ns1->net_stat_obytes;
+ ne->net_entry_tstats->net_stat_ipackets += ns1->net_stat_ipackets;
+ ne->net_entry_tstats->net_stat_opackets += ns1->net_stat_opackets;
+ ne->net_entry_tstats->net_stat_ierrors += ns1->net_stat_ierrors;
+ ne->net_entry_tstats->net_stat_oerrors += ns1->net_stat_oerrors;
+}
+
+/* Add the stat entry into the table */
+static dladm_status_t
+add_stat_to_tbl(net_table_t *net_table, net_stat_t *ns)
+{
+ net_entry_t *ne;
+
+ ne = get_ne_from_table(net_table, ns->net_stat_name);
+ if (ne == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ /* Ptr to flow desc */
+ ns->net_stat_desc = ne->net_entry_desc;
+ if (ns->net_stat_desc->net_desc_newrec) {
+ ns->net_stat_desc->net_desc_newrec = B_FALSE;
+ ns->net_stat_isref = B_TRUE;
+ ne->net_entry_sref = ns;
+ } else if (ns->net_stat_ibytes < ne->net_entry_sref->net_stat_tibytes ||
+ (ns->net_stat_obytes < ne->net_entry_sref->net_stat_tobytes)) {
+ ns->net_stat_isref = B_TRUE;
+ ne->net_entry_sref = ns;
+ } else {
+ ns->net_stat_isref = B_FALSE;
+ update_stats(ns, ne, ne->net_entry_sref);
+ }
+ if (ne->net_entry_shead == NULL) {
+ ne->net_entry_shead = ns;
+ ne->net_entry_stail = ns;
+ } else {
+ if (!ns->net_stat_isref) {
+ ne->net_entry_ttime += (ns->net_stat_ctime -
+ ne->net_entry_stail->net_stat_ctime);
+ ns->net_stat_tdiff = ns->net_stat_ctime -
+ ne->net_entry_stail->net_stat_ctime;
+ }
+ ne->net_entry_stail->net_stat_next = ns;
+ ne->net_entry_stail = ns;
+ }
+
+ ne->net_entry_scount++;
+ return (DLADM_STATUS_OK);
+}
+
+/* Add a flow/link descriptor record to the table */
+static dladm_status_t
+add_desc(net_table_t *net_table, ea_file_t *ef, int nobjs)
+{
+ net_desc_t *nd;
+ net_desc_t *dnd;
+ int count;
+ ea_object_t scratch;
+
+ if ((nd = calloc(1, sizeof (net_desc_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+ nd->net_desc_newrec = B_TRUE;
+
+ for (count = 0; count < nobjs; count++) {
+ if (ea_get_object(ef, &scratch) == -1) {
+ free(nd);
+ return (DLADM_STATUS_NOMEM);
+ }
+ add_desc_item(&scratch, nd);
+ }
+ if ((dnd = get_ndesc(net_table, nd)) != NULL) {
+ dnd->net_desc_newrec = B_TRUE;
+ free(nd);
+ return (DLADM_STATUS_OK);
+ }
+ if (add_desc_to_tbl(net_table, nd) != 0) {
+ free(nd);
+ return (DLADM_STATUS_NOMEM);
+ }
+ return (DLADM_STATUS_OK);
+}
+
+/* Make an entry into the time sorted list */
+static void
+addto_time_list(net_table_t *net_table, net_time_entry_t *nt,
+ net_time_entry_t *ntc)
+{
+ net_stat_t *ns = nt->my_time_stat;
+ net_stat_t *ns1;
+ net_time_entry_t *end;
+ net_time_t *t1;
+ int count;
+
+ t1 = &ns->net_stat_time;
+
+ net_table->net_time_entries++;
+
+ if (net_table->net_time_head == NULL) {
+ net_table->net_time_head = nt;
+ net_table->net_time_tail = nt;
+ } else {
+ net_table->net_time_tail->net_time_entry_next = nt;
+ nt->net_time_entry_prev = net_table->net_time_tail;
+ net_table->net_time_tail = nt;
+ }
+
+ if (net_table->net_ctime_head == NULL) {
+ net_table->net_ctime_head = ntc;
+ net_table->net_ctime_tail = ntc;
+ } else {
+ end = net_table->net_ctime_tail;
+ count = 0;
+ while (count < net_table->net_time_entries - 1) {
+ ns1 = end->my_time_stat;
+ /* Just add it to the tail */
+ if (compare_date(t1, &ns1->net_stat_time) ==
+ NET_DATE_GREATER) {
+ break;
+ }
+ if ((strlen(ns1->net_stat_name) ==
+ strlen(ns->net_stat_name)) &&
+ (strncmp(ns1->net_stat_name, ns->net_stat_name,
+ strlen(ns1->net_stat_name)) == 0)) {
+ ntc->net_time_entry_next =
+ end->net_time_entry_next;
+ if (end->net_time_entry_next != NULL) {
+ end->net_time_entry_next->
+ net_time_entry_prev = ntc;
+ } else {
+ net_table->net_ctime_tail = ntc;
+ }
+ end->net_time_entry_next = ntc;
+ ntc->net_time_entry_prev = end;
+ return;
+ }
+ count++;
+ end = end->net_time_entry_prev;
+ }
+ net_table->net_ctime_tail->net_time_entry_next = ntc;
+ ntc->net_time_entry_prev = net_table->net_ctime_tail;
+ net_table->net_ctime_tail = ntc;
+ }
+}
+
+/* Add stat entry into the lists */
+static dladm_status_t
+add_stats(net_table_t *net_table, ea_file_t *ef, int nobjs)
+{
+ net_stat_t *ns;
+ int count;
+ ea_object_t scratch;
+ net_time_entry_t *nt;
+ net_time_entry_t *ntc;
+
+ if ((ns = calloc(1, sizeof (net_stat_t))) == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ if ((nt = calloc(1, sizeof (net_time_entry_t))) == NULL) {
+ free(ns);
+ return (DLADM_STATUS_NOMEM);
+ }
+ if ((ntc = calloc(1, sizeof (net_time_entry_t))) == NULL) {
+ free(ns);
+ free(nt);
+ return (DLADM_STATUS_NOMEM);
+ }
+
+ nt->my_time_stat = ns;
+ ntc->my_time_stat = ns;
+
+ for (count = 0; count < nobjs; count++) {
+ if (ea_get_object(ef, &scratch) == -1) {
+ free(ns);
+ free(nt);
+ free(ntc);
+ return (DLADM_STATUS_NOMEM);
+ }
+ add_stat_item(&scratch, ns);
+ }
+ if (add_stat_to_tbl(net_table, ns) != 0) {
+ free(ns);
+ free(nt);
+ free(ntc);
+ return (DLADM_STATUS_NOMEM);
+ }
+ addto_time_list(net_table, nt, ntc);
+ return (DLADM_STATUS_OK);
+}
+
+/* Free the entire table */
+static void
+free_logtable(net_table_t *net_table)
+{
+ net_entry_t *head;
+ net_entry_t *next;
+ net_stat_t *ns;
+ net_stat_t *ns1;
+ net_time_entry_t *thead;
+ net_time_entry_t *tnext;
+
+ thead = net_table->net_time_head;
+ while (thead != NULL) {
+ thead->my_time_stat = NULL;
+ tnext = thead->net_time_entry_next;
+ thead->net_time_entry_next = NULL;
+ thead->net_time_entry_prev = NULL;
+ free(thead);
+ thead = tnext;
+ }
+ net_table->net_time_head = NULL;
+ net_table->net_time_tail = NULL;
+
+ thead = net_table->net_ctime_head;
+ while (thead != NULL) {
+ thead->my_time_stat = NULL;
+ tnext = thead->net_time_entry_next;
+ thead->net_time_entry_next = NULL;
+ thead->net_time_entry_prev = NULL;
+ free(thead);
+ thead = tnext;
+ }
+ net_table->net_ctime_head = NULL;
+ net_table->net_ctime_tail = NULL;
+
+ net_table->net_time_entries = 0;
+
+ head = net_table->net_table_head;
+ while (head != NULL) {
+ next = head->net_entry_next;
+ head->net_entry_next = NULL;
+ ns = head->net_entry_shead;
+ while (ns != NULL) {
+ ns1 = ns->net_stat_next;
+ free(ns);
+ ns = ns1;
+ }
+ head->net_entry_scount = 0;
+ head->net_entry_sref = NULL;
+ free(head->net_entry_desc);
+ free(head->net_entry_tstats);
+ free(head);
+ head = next;
+ }
+ net_table->net_table_head = NULL;
+ net_table->net_table_tail = NULL;
+ net_table->net_time_entries = 0;
+ free(net_table);
+}
+
+/* Parse the exacct file, and return the parsed table. */
+static void *
+parse_logfile(char *file, int logtype, dladm_status_t *status)
+{
+ ea_file_t ef;
+ ea_object_t scratch;
+ net_table_t *net_table;
+
+ *status = DLADM_STATUS_OK;
+ if ((net_table = calloc(1, sizeof (net_table_t))) == NULL) {
+ *status = DLADM_STATUS_NOMEM;
+ return (NULL);
+ }
+ if (ea_open(&ef, file, NULL, 0, O_RDONLY, 0) == -1) {
+ *status = DLADM_STATUS_BADARG;
+ free(net_table);
+ return (NULL);
+ }
+ bzero(&scratch, sizeof (ea_object_t));
+ while (ea_get_object(&ef, &scratch) != -1) {
+ if (scratch.eo_type != EO_GROUP) {
+ (void) ea_free_item(&scratch, EUP_ALLOC);
+ (void) bzero(&scratch, sizeof (ea_object_t));
+ continue;
+ }
+ /* Read Link Desc/Stat records */
+ if (logtype == DLADM_LOGTYPE_FLOW) {
+ /* Flow Descriptor */
+ if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ /* Flow Stats */
+ } else if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ } else if (logtype == DLADM_LOGTYPE_LINK) {
+ /* Link Descriptor */
+ if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_LINK_DESC) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ /* Link Stats */
+ } else if ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_LINK_STATS) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ } else {
+ if (((scratch.eo_catalog & EXD_DATA_MASK) ==
+ EXD_GROUP_NET_LINK_DESC) || ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_DESC)) {
+ (void) add_desc(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ } else if (((scratch.eo_catalog & EXD_DATA_MASK) ==
+ EXD_GROUP_NET_LINK_STATS) || ((scratch.eo_catalog &
+ EXD_DATA_MASK) == EXD_GROUP_NET_FLOW_STATS)) {
+ (void) add_stats(net_table, &ef,
+ scratch.eo_group.eg_nobjs - 1);
+ }
+ }
+ (void) ea_free_item(&scratch, EUP_ALLOC);
+ (void) bzero(&scratch, sizeof (ea_object_t));
+ }
+
+ (void) ea_close(&ef);
+ return ((void *)net_table);
+}
+
+/*
+ * Walk the ctime list. This is used when looking for usage records
+ * based on a "resource" name.
+ */
+dladm_status_t
+dladm_walk_usage_res(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *resource, char *stime, char *etime, void *arg)
+{
+ net_table_t *net_table;
+ net_time_t st, et;
+ net_time_entry_t *start;
+ net_stat_t *ns = NULL;
+ net_stat_t *nns;
+ uint64_t tot_time = 0;
+ uint64_t last_time;
+ uint64_t tot_bytes = 0;
+ uint64_t tot_ibytes = 0;
+ uint64_t tot_obytes = 0;
+ boolean_t gotstart = B_FALSE;
+ dladm_status_t status;
+ dladm_usage_t usage;
+ int step = 1;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+ start = net_table->net_ctime_head;
+
+ /* Time range */
+ status = get_time_range(net_table->net_ctime_head,
+ net_table->net_ctime_tail, &st, &et, stime, etime);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+
+ while (start != NULL) {
+ nns = start->my_time_stat;
+
+ /* Get to the resource we are interested in */
+ if ((strlen(resource) != strlen(nns->net_stat_name)) ||
+ (strncmp(resource, nns->net_stat_name,
+ strlen(nns->net_stat_name)) != 0)) {
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ /* Find the first record */
+ if (!gotstart) {
+ get_starting_point(start, &start, &st, stime,
+ &last_time);
+ if (start == NULL)
+ break;
+ nns = start->my_time_stat;
+ gotstart = B_TRUE;
+ }
+
+ /* Write one entry and return if we are out of the range */
+ if (etime != NULL && compare_time(&nns->net_stat_time, &et)
+ == NET_TIME_GREATER) {
+ if (tot_bytes != 0) {
+ bcopy(ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ }
+ return (DLADM_STATUS_OK);
+ }
+
+ /*
+ * If this is a reference entry, just print what we have
+ * and proceed.
+ */
+ if (nns->net_stat_isref) {
+ if (tot_bytes != 0) {
+ bcopy(&nns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&nns->net_stat_ctime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
+ tot_obytes, step);
+ }
+ last_time = nns->net_stat_ctime;
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ ns = nns;
+ if (--step == 0) {
+ tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
+ tot_ibytes += ns->net_stat_ibytes;
+ tot_obytes += ns->net_stat_obytes;
+ tot_time += ns->net_stat_tdiff;
+ bcopy(&ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+
+ NET_RESET_TOT(tot_bytes, tot_time, tot_ibytes,
+ tot_obytes, step);
+ last_time = ns->net_stat_ctime;
+ } else {
+ tot_bytes += ns->net_stat_ibytes + ns->net_stat_obytes;
+ tot_ibytes += ns->net_stat_ibytes;
+ tot_obytes += ns->net_stat_obytes;
+ tot_time += ns->net_stat_tdiff;
+ }
+ start = start->net_time_entry_next;
+ }
+
+ if (tot_bytes != 0) {
+ bcopy(&ns->net_stat_name, &usage.du_name,
+ sizeof (usage.du_name));
+ bcopy(&last_time, &usage.du_stime,
+ sizeof (usage.du_stime));
+ bcopy(&ns->net_stat_ctime, &usage.du_etime,
+ sizeof (usage.du_etime));
+ usage.du_rbytes = tot_ibytes;
+ usage.du_obytes = tot_obytes;
+ usage.du_bandwidth = tot_bytes*8/tot_time;
+ usage.du_last = B_TRUE;
+ fn(&usage, arg);
+ }
+
+ free_logtable(net_table);
+ return (status);
+}
+
+/*
+ * Walk the time sorted list if a resource is not specified.
+ */
+dladm_status_t
+dladm_walk_usage_time(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *stime, char *etime, void *arg)
+{
+ net_table_t *net_table;
+ net_time_entry_t *start;
+ net_stat_t *ns = NULL, *nns;
+ net_time_t st, et, *t1;
+ net_desc_t *nd;
+ net_entry_t *ne;
+ net_plot_entry_t *pe;
+ int count;
+ int step = 1;
+ int nentries = 0, pentries = 0;
+ uint64_t last_time;
+ dladm_status_t status;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+ start = net_table->net_time_head;
+
+ /* Find the first and last records and starting point */
+ status = get_time_range(net_table->net_time_head,
+ net_table->net_time_tail, &st, &et, stime, etime);
+ if (status != DLADM_STATUS_OK)
+ return (status);
+ get_starting_point(start, &start, &st, stime, &last_time);
+ /*
+ * Could assert to be non-null, since get_time_range()
+ * would have adjusted.
+ */
+ if (start == NULL)
+ return (DLADM_STATUS_BADTIMEVAL);
+
+ /*
+ * Collect entries for all resources in a time slot before
+ * writing to the file.
+ */
+ nentries = net_table->net_entries;
+
+ pe = malloc(sizeof (net_plot_entry_t) * net_table->net_entries + 1);
+ if (pe == NULL)
+ return (DLADM_STATUS_NOMEM);
+
+ ne = net_table->net_table_head;
+ for (count = 0; count < nentries; count++) {
+ nd = ne->net_entry_desc;
+ pe[count].net_pe_name = nd->net_desc_name;
+ ne = ne->net_entry_next;
+ }
+
+ clear_pe(pe, nentries, &pentries);
+
+ /* Write header to file */
+ /* add_pe_to_file(fn, pe, ns, nentries, arg); */
+
+ t1 = &start->my_time_stat->net_stat_time;
+
+ while (start != NULL) {
+
+ nns = start->my_time_stat;
+ /*
+ * We have crossed the time boundary, check if we need to
+ * print out now.
+ */
+ if (compare_time(&nns->net_stat_time, t1) ==
+ NET_TIME_GREATER) {
+ /* return if we are out of the range */
+ if (etime != NULL &&
+ compare_time(&nns->net_stat_time, &et) ==
+ NET_TIME_GREATER) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries,
+ arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ free(pe);
+ return (DLADM_STATUS_OK);
+ }
+ /* update the stats from the ns. */
+ t1 = &nns->net_stat_time;
+ last_time = ns->net_stat_ctime;
+ if (--step == 0) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries,
+ arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ step = 1;
+ }
+ }
+
+ /*
+ * if this is a reference entry, just print what we have
+ * for this resource and proceed. We will end up writing
+ * the stats for all the entries when we hit a ref element,
+ * which means 'steps' for some might not be accurate, but
+ * that is fine, the alternative is to write only the
+ * resource for which we hit a reference entry.
+ */
+ if (nns->net_stat_isref) {
+ if (pentries > 0) {
+ add_pe_to_file(fn, pe, ns, nentries, arg);
+ clear_pe(pe, nentries, &pentries);
+ }
+ step = 1;
+ } else {
+ update_pe(pe, nns, nentries, &pentries, last_time);
+ }
+ ns = nns;
+ start = start->net_time_entry_next;
+ }
+
+ if (pentries > 0)
+ add_pe_to_file(fn, pe, ns, nentries, arg);
+
+ free(pe);
+ free_logtable(net_table);
+
+ return (DLADM_STATUS_OK);
+}
+
+dladm_status_t
+dladm_usage_summary(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, void *arg)
+{
+ net_table_t *net_table;
+ net_entry_t *ne;
+ net_desc_t *nd;
+ net_stat_t *ns;
+ int count;
+ dladm_usage_t usage;
+ dladm_status_t status;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+
+ ne = net_table->net_table_head;
+ for (count = 0; count < net_table->net_entries; count++) {
+ ns = ne->net_entry_tstats;
+ nd = ne->net_entry_desc;
+
+ if (ns->net_stat_ibytes + ns->net_stat_obytes == 0)
+ continue;
+ bcopy(&nd->net_desc_name, &usage.du_name,
+ sizeof (usage.du_name));
+ usage.du_duration = ne->net_entry_ttime;
+ usage.du_ipackets = ns->net_stat_ipackets;
+ usage.du_rbytes = ns->net_stat_ibytes;
+ usage.du_opackets = ns->net_stat_opackets;
+ usage.du_obytes = ns->net_stat_obytes;
+ usage.du_bandwidth =
+ (ns->net_stat_ibytes + ns->net_stat_obytes) * 8 /
+ usage.du_duration;
+ usage.du_last = (count == net_table->net_entries-1);
+ fn(&usage, arg);
+
+ ne = ne->net_entry_next;
+ }
+
+ free_logtable(net_table);
+ return (DLADM_STATUS_OK);
+}
+
+/*
+ * Walk the ctime list and display the dates of the records.
+ */
+dladm_status_t
+dladm_usage_dates(int (*fn)(dladm_usage_t *, void *), int logtype,
+ char *logfile, char *resource, void *arg)
+{
+ net_table_t *net_table;
+ net_time_entry_t *start;
+ net_stat_t *nns;
+ net_time_t st;
+ net_time_t *lasttime = NULL;
+ uint64_t last_time;
+ boolean_t gotstart = B_FALSE;
+ dladm_status_t status;
+ dladm_usage_t usage;
+
+ /* Parse the log file */
+ net_table = parse_logfile(logfile, logtype, &status);
+ if (net_table == NULL)
+ return (status);
+
+ if (net_table->net_entries == 0)
+ return (DLADM_STATUS_OK);
+
+ start = net_table->net_ctime_head;
+
+ while (start != NULL) {
+ nns = start->my_time_stat;
+
+ /* get to the resource we are interested in */
+ if (resource != NULL) {
+ if ((strlen(resource) != strlen(nns->net_stat_name)) ||
+ (strncmp(resource, nns->net_stat_name,
+ strlen(nns->net_stat_name)) != 0)) {
+ start = start->net_time_entry_next;
+ continue;
+ }
+ }
+
+ /* get the starting point in the logfile */
+ if (!gotstart) {
+ get_starting_point(start, &start, &st, NULL,
+ &last_time);
+ if (start == NULL)
+ break;
+ nns = start->my_time_stat;
+ gotstart = B_TRUE;
+ }
+
+ if (lasttime == NULL ||
+ compare_date(&nns->net_stat_time, lasttime) ==
+ NET_DATE_GREATER) {
+ bzero(&usage, sizeof (dladm_usage_t));
+ bcopy(&nns->net_stat_ctime, &usage.du_stime,
+ sizeof (usage.du_stime));
+ fn(&usage, arg);
+ lasttime = &nns->net_stat_time;
+ }
+
+ start = start->net_time_entry_next;
+ continue;
+ }
+
+ free_logtable(net_table);
+ return (status);
+}