/*
* 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
* 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 <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <kstat.h>
#include <limits.h>
#include <unistd.h>
#include <signal.h>
#include <libdllink.h>
#include <libdlflow.h>
#include <libdlstat.h>
#include <libdlaggr.h>
struct flowlist {
int fd;
};
/* Exported functions */
/*
* dladm_kstat_lookup() is a modified version of kstat_lookup which
* adds the class as a selector.
*/
kstat_t *
{
return (ksp);
}
return (NULL);
}
/*
* dladm_get_stats() populates the supplied pktsum_t structure with
* the input and output packet and byte kstats from the kstat_t
* found with dladm_kstat_lookup.
*/
void
{
return;
return;
}
return;
}
return;
}
return;
}
return;
}
return;
}
}
int
{
return (-1);
return (-1);
switch (type) {
case KSTAT_DATA_UINT64:
break;
case KSTAT_DATA_UINT32:
break;
default:
return (-1);
}
return (0);
}
{
return (status);
return (DLADM_STATUS_LINKINVAL);
if (status != DLADM_STATUS_OK)
return (status);
if (status != DLADM_STATUS_OK)
return (status);
warn("kstat_open operation failed");
return (-1);
}
/*
* The kstat query could fail if the underlying MAC
* driver was already detached.
*/
goto bail;
goto bail;
goto bail;
(void) kstat_close(kcp);
return (DLADM_STATUS_OK);
bail:
(void) kstat_close(kcp);
return (dladm_errno2status(errno));
}
/* Compute sum of 2 pktsums (s1 = s2 + s3) */
void
{
}
/* Compute differences between 2 pktsums (s1 = s2 - s3) */
void
{
}
typedef struct {
const char *si_name;
} stat_info_t;
/* Definitions for rx lane stats */
};
};
};
/* Definitions for tx lane stats */
};
};
/* Definitions for rx ring stats */
};
/* Definitions for tx ring stats */
};
/* Definitions for fanout stats */
};
/* Definitions for total stats */
};
/* Definitions for aggr stats */
};
/* Definitions for flow stats */
};
/* Rx lane specific functions */
static boolean_t i_dlstat_rx_lane_match(void *, void *);
static void * i_dlstat_rx_lane_stat_entry_diff(void *, void *);
/* Tx lane specific functions */
static boolean_t i_dlstat_tx_lane_match(void *, void *);
static void * i_dlstat_tx_lane_stat_entry_diff(void *, void *);
/* Rx lane total specific functions */
/* Tx lane total specific functions */
/* Fanout specific functions */
static boolean_t i_dlstat_fanout_match(void *, void *);
static void * i_dlstat_fanout_stat_entry_diff(void *, void *);
/* Rx ring specific functions */
static boolean_t i_dlstat_rx_ring_match(void *, void *);
static void * i_dlstat_rx_ring_stat_entry_diff(void *, void *);
/* Tx ring specific functions */
static boolean_t i_dlstat_tx_ring_match(void *, void *);
static void * i_dlstat_tx_ring_stat_entry_diff(void *, void *);
/* Rx ring total specific functions */
/* Tx ring total specific functions */
/* Summary specific functions */
static boolean_t i_dlstat_total_match(void *, void *);
static void * i_dlstat_total_stat_entry_diff(void *, void *);
/* Aggr port specific functions */
static boolean_t i_dlstat_aggr_port_match(void *, void *);
static void * i_dlstat_aggr_port_stat_entry_diff(void *, void *);
/* Misc stat specific functions */
typedef void * dladm_stat_diff_t(void *, void *);
typedef struct dladm_stat_desc_s {
/*
* dladm_stat_table has one entry for each supported stat. ds_querystat returns
* a chain of 'stat entries' for the queried stat.
* Each stat entry has set of identifiers (ids) and an object containing actual
* stat values. These stat entry objects are chained together in a linked list
* of datatype dladm_stat_chain_t. Head of this list is returned to the caller
* of dladm_link_stat_query.
*
* One node in the chain is shown below:
*
* -------------------------
* | dc_statentry |
* | -------------- |
* | | ids | |
* | -------------- |
* | | stat fields | |
* | -------------- |
* -------------------------
* | dc_next ---------|------> to next stat entry
* -------------------------
*
* In particular, for query DLADM_STAT_RX_LANE, dc_statentry carries pointer to
* object of type rx_lane_stat_entry_t.
*
* dladm_link_stat_query_all returns similar chain. However, instead of storing
* stat fields as raw numbers, it stores those as chain of <name, value> pairs.
* The resulting structure is depicted below:
*
* -------------------------
* | dc_statentry |
* | -------------- | ---------------
* | | nv_header | | | name, val |
* | -------------- | ---------------
* | | nve_stats---|----|-->| nv_nextstat--|---> to next name, val pair
* | -------------- | ---------------
* -------------------------
* | dc_next ---------|------> to next stat entry
* -------------------------
*/
/*
* We don't support -i <interval> query with misc stats. Several table fields
* are left uninitialized thus.
*/
0,
};
/* Internal functions */
static void *
{
}
static boolean_t
{
}
/* Diff between two stats */
static void
{
int i;
for (i = 0; i < size; i++) {
}
}
/*
* Perform diff = s1 - s2, where diff, s1, s2 are structure objects of same
* datatype. slist is list of offsets of the fields within the structure.
*/
} else { \
} \
}
/* Sum two stats */
static void
{
int i;
for (i = 0; i < size; i++) {
}
}
/* Look up kstat value */
static void
{
int i;
return;
for (i = 0; i < size; i++) {
KSTAT_DATA_UINT64, val) < 0)
return;
}
}
/* Append linked list list1 to linked list list2 and return resulting list */
static dladm_stat_chain_t *
{
return (list2);
/* list1 has at least one element, find last element in list1 */
return (list1);
}
typedef enum {
void
{
int i, j;
for (j = 1; j < size; j++) {
}
}
/* Support for legacy drivers */
void
{
return;
(void) kstat_close(kcp);
}
void *
{
/* Query for dls stats */
/* Convert to desired data type */
if (rx_lane_stat_entry == NULL)
goto done;
/* Allocate memory for wrapper */
goto done;
}
done:
return (head);
}
void *
{
/* Query for dls stats */
/* Convert to desired data type */
if (tx_lane_stat_entry == NULL)
goto done;
/* Allocate memory for wrapper */
goto done;
}
done:
return (head);
}
/*
* Ideally, we would want an ioctl to return list of ring-ids (or lane-ids)
* for a given data-link (or mac client). We could then query for specific
* kstats based on these ring-ids (lane-ids).
* Ring-ids (or lane-ids) could be returned like any other link properties
* queried by dladm show-linkprop. However, non-global zones do not have
* access to this information today.
* We thus opt for an implementation that relies heavily on kstat internals:
* i_dlstat_*search routines and i_dlstat_get_idlist.
*/
/* rx hwlane specific */
static boolean_t
{
return (ksp->ks_instance == 0 &&
}
/* tx hwlane specific */
static boolean_t
{
return (ksp->ks_instance == 0 &&
}
/* rx fanout specific */
static boolean_t
{
return (ksp->ks_instance == 0 &&
}
/* rx ring specific */
static boolean_t
{
return (ksp->ks_instance == 0 &&
}
/* tx ring specific */
static boolean_t
{
return (ksp->ks_instance == 0) &&
}
typedef struct dladm_extract_idlist_s {
char *di_prefix;
};
static void
{
char *prefix;
int prefixlen;
*size = 0;
warn("kstat_open operation failed");
goto done;
}
fptr_searchkstat(ksp)) {
}
}
done:
(void) kstat_close(kcp);
}
static dladm_stat_chain_t *
{
int i = 0;
warn("kstat_open operation failed");
return (NULL);
}
for (i = 0; i < idlist_size; i++) {
index);
continue;
break;
break;
}
sizeof (curr->dc_statheader));
else
}
done:
(void) kstat_close(kcp);
return (head);
}
static misc_stat_entry_t *
{
return (NULL);
goto done;
if (misc_stat_entry == NULL)
goto done;
done:
(void) kstat_close(kcp);
return (misc_stat_entry);
}
/* Rx lane statistic specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
static void *
{
if (rx_lane_stat_entry == NULL)
goto done;
rx_lane_stat_entry->rle_index = i;
done:
return (rx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
{
if (rx_lane_stat_entry == NULL)
goto done;
done:
return (rx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
{
if (rx_lane_stat_entry == NULL)
goto done;
if (local_stat_entry == NULL)
goto done;
done:
return (local_stat_entry);
}
static dladm_stat_chain_t *
{
if (local_stats != NULL) {
sizeof (local_stats->dc_statheader));
}
return (local_stats);
}
static dladm_stat_chain_t *
{
if (misc_stat_entry == NULL)
goto done;
if (rx_lane_stat_entry == NULL)
goto done;
goto done;
}
done:
return (head);
}
static dladm_stat_chain_t *
{
if (misc_stat_entry == NULL)
goto done;
if (rx_lane_stat_entry == NULL)
goto done;
goto done;
}
done:
return (head);
}
static dladm_stat_chain_t *
{
}
/*ARGSUSED*/
static dladm_stat_chain_t *
const char *linkname)
{
}
void *
{
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
/* Check if it is legacy driver */
goto done;
}
if (is_legacy_driver) {
goto done;
}
if (lane_stats == NULL)
done:
return (head);
}
/* Tx lane statistic specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
static void *
{
if (tx_lane_stat_entry == NULL)
goto done;
tx_lane_stat_entry->tle_index = i;
done:
return (tx_lane_stat_entry);
}
/*ARGSUSED*/
static void *
{
if (tx_lane_stat_entry == NULL)
goto done;
done:
return (tx_lane_stat_entry);
}
static dladm_stat_chain_t *
{
if (misc_stat_entry == NULL)
goto done;
if (tx_lane_stat_entry == NULL)
goto done;
goto done;
}
done:
return (head);
}
static dladm_stat_chain_t *
{
if (misc_stat_entry == NULL)
goto done;
if (tx_lane_stat_entry == NULL)
goto done;
goto done;
}
done:
return (head);
}
static dladm_stat_chain_t *
{
}
/*ARGSUSED*/
static dladm_stat_chain_t *
const char *linkname)
{
}
void *
{
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
/* Check if it is legacy driver */
goto done;
}
if (is_legacy_driver) {
goto done;
}
if (lane_stats == NULL)
done:
return (head);
}
/* Rx lane total statistic specific functions */
void *
{
/* Get per rx lane stats */
if (rx_lane_head == NULL)
goto done;
if (total_stats == NULL)
goto done;
}
if (total_head == NULL) {
goto done;
}
sizeof (total_head->dc_statheader));
done:
return (total_head);
}
/* Tx lane total statistic specific functions */
void *
{
/* Get per tx lane stats */
if (tx_lane_head == NULL)
goto done;
if (total_stats == NULL)
goto done;
}
if (total_head == NULL) {
goto done;
}
sizeof (total_head->dc_statheader));
done:
return (total_head);
}
/* Fanout specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
static void *
{
if (fanout_stat_entry == NULL)
goto done;
/* Set by the caller later */
done:
return (fanout_stat_entry);
}
static void *
{
int i;
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
for (i = 0; i < idlist_size; i++) {
break;
else /* Link new lane list to end of previous lane list */
/* Walk new lane list and set ids */
/*
* Save last pointer of previous linked list.
* This pointer is used to chain linked lists
* generated in each iteration.
*/
}
}
return (head);
}
void *
const char *linkname)
{
}
void *
const char *linkname)
{
}
void *
{
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
if (fout_swlane_and_local_stats == NULL) {
goto done;
}
} else { /* no hwlane, mix of local+sw classified */
}
done:
return (head);
}
/* Rx ring statistic specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
static void *
{
if (rx_ring_stat_entry == NULL)
goto done;
rx_ring_stat_entry->re_index = i;
done:
return (rx_ring_stat_entry);
}
void *
{
char *modname;
/*
* kstats corresponding to physical device rings continue to use
* device names even if the link is renamed using dladm rename-link.
* Thus, given a linkid, we lookup the physical device name.
* However, if an aggr is renamed, kstats corresponding to its
* pseudo rings are renamed as well.
*/
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
if (class != DATALINK_CLASS_AGGR) {
return (NULL);
}
} else
}
/* Tx ring statistic specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
static void *
{
if (tx_ring_stat_entry == NULL)
goto done;
tx_ring_stat_entry->re_index = i;
done:
return (tx_ring_stat_entry);
}
void *
{
char *modname;
/*
* kstats corresponding to physical device rings continue to use
* device names even if the link is renamed using dladm rename-link.
* Thus, given a linkid, we lookup the physical device name.
* However, if an aggr is renamed, kstats corresponding to its
* pseudo rings are renamed as well.
*/
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
return (NULL);
}
if (class != DATALINK_CLASS_AGGR) {
return (NULL);
}
} else
}
/* Rx ring total statistic specific functions */
void *
{
/* Get per rx ring stats */
if (rx_ring_head == NULL)
goto done;
if (total_stats == NULL)
goto done;
}
if (total_head == NULL) {
goto done;
}
sizeof (total_head->dc_statheader));
done:
return (total_head);
}
/* Tx ring total statistic specific functions */
void *
{
/* Get per tx ring stats */
if (tx_ring_head == NULL)
goto done;
if (total_stats == NULL)
goto done;
}
if (total_head == NULL) {
goto done;
}
sizeof (total_head->dc_statheader));
done:
return (total_head);
}
/* Summary statistic specific functions */
/*ARGSUSED*/
static boolean_t
{
/* Always single entry for total */
return (B_TRUE);
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
void *
{
/* Get total rx lane stats */
goto done;
/* Get total tx lane stats */
goto done;
/* Build total stat */
if (total_stat_entry == NULL)
goto done;
/* Extract total rx ipackets, rbytes */
/* Extract total tx opackets, obytes */
goto done;
}
sizeof (head->dc_statheader));
done:
return (head);
}
/* Aggr total statistic(summed across all component ports) specific functions */
void *
{
if (total_stats == NULL)
goto done;
}
if (total_head == NULL) {
goto done;
}
done:
return (total_head);
}
/* Aggr port statistic specific functions */
static boolean_t
{
}
static void *
{
if (diff_entry == NULL)
goto done;
done:
return (diff_entry);
}
/*
* Query dls stats for the aggr port. This results in query for stats into
* the corresponding device driver.
*/
static aggr_port_stat_entry_t *
{
goto done;
warn("kstat open operation failed");
return (NULL);
}
goto done;
if (aggr_port_stat_entry == NULL)
goto done;
/* Save port's linkid */
done:
(void) kstat_close(kcp);
return (aggr_port_stat_entry);
}
void *
{
int i;
/* Get aggr info */
!= DLADM_STATUS_OK)
goto done;
/* For every port that is member of this aggr do */
DLADM_OPT_ACTIVE) != DLADM_STATUS_OK) {
goto done;
}
/* Create dladm_stat_chain_t object for this stat */
goto done;
}
sizeof (curr->dc_statheader));
/* Chain this aggr port stat entry */
/* head of the stat list */
else
}
/*
* Prepend the stat list with cumulative aggr stats i.e. summed over all
* component ports
*/
if (total_stats != NULL) {
head = total_stats;
}
done:
return (head);
}
/* Misc stat specific functions */
void *
{
DLPI_LINKNAME_MAX) != DLADM_STATUS_OK) {
goto done;
}
if (misc_stat_entry == NULL)
goto done;
goto done;
}
sizeof (head->dc_statheader));
done:
return (head);
}
/* Exported functions */
{
}
{
/* Perform op1 - op2, store result in diff */
break;
}
}
goto done;
/* prev iteration did not have this stat entry */
} else {
}
goto done;
}
else
}
done:
return (diff_head);
}
void
{
}
}
/* Query all link stats */
static name_value_stat_t *
{
int i;
for (i = 0; i < size; i++) {
break;
sizeof (curr_stat->nv_statname));
else
}
return (head_stat);
}
void *
{
void *statfields;
/* Allocate memory for query all stat entry */
if (name_value_stat_entry == NULL)
goto done;
/* Header for these stat fields */
sizeof (name_value_stat_entry->nve_header));
/* Extract stat fields from the statentry */
/* Convert curr_stat to <statname, statval> pair */
done:
return (name_value_stat_entry);
}
void *
{
/*
* For every stat in the chain, build header and convert all
* its stat fields
*/
if (nvstat_curr == NULL)
break;
break;
}
else
}
done:
return (nvstat_head);
}
{
/* Query the requested stat */
goto done;
/*
* Convert every statfield in every stat-entry of stat chain to
* <statname, statval> pair
*/
/* Free stat_head */
done:
return (nvstat_head);
}
void
{
}
}
}
/* flow stats specific routines */
{
return (NULL);
goto done;
}
done:
(void) kstat_close(kcp);
return (flow_stat);
}
{
goto done;
} else {
}
done:
return (diff_stat);
}
void
{
}
/* Query all flow stats */
{
/* Query flow stats */
goto done;
/* Allocate memory for query all stat entry */
if (name_value_stat_entry == NULL) {
goto done;
}
/* Header for these stat fields */
/* Convert every statfield in flow_stat to <statname, statval> pair */
/* Free flow_stat */
done:
return (name_value_stat_entry);
}
void
{
}
}