mirror of
https://github.com/openvswitch/ovs
synced 2025-08-22 09:58:01 +00:00
netdev: Custom statistics.
- New get_custom_stats interface function is added to netdev. It allows particular netdev implementation to expose custom counters in dictionary format (counter name/counter value). - New statistics are retrieved using experimenter code and are printed as a result to ofctl dump-ports. - New counters are available for OpenFlow 1.4+. - New statistics are printed to output via ofctl only if those are present in reply message. - New statistics definition is added to include/openflow/intel-ext.h. - Custom statistics are implemented only for dpdk-physical port type. - DPDK-physical implementation uses xstats to collect statistics. Only dropped and error counters are exposed. Co-authored-by: Ben Pfaff <blp@ovn.org> Signed-off-by: Ben Pfaff <blp@ovn.org> Signed-off-by: Michal Weglicki <michalx.weglicki@intel.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
parent
cd32509e4a
commit
971f4b394c
@ -311,12 +311,16 @@ performance of non-tunnel traffic, specifically for smaller size packet.
|
||||
|
||||
.. _extended-statistics:
|
||||
|
||||
Extended Statistics
|
||||
-------------------
|
||||
Extended & Custom Statistics
|
||||
----------------------------
|
||||
|
||||
DPDK Extended Statistics API allows PMD to expose unique set of statistics.
|
||||
The Extended statistics are implemented and supported only for DPDK physical
|
||||
and vHost ports.
|
||||
and vHost ports. Custom statistics are dynamic set of counters which can
|
||||
vary depenend on a driver. Those statistics are implemented
|
||||
for DPDK physical ports and contain all "dropped", "error" and "management"
|
||||
counters from XSTATS. XSTATS counters list can be found here:
|
||||
<https://wiki.opnfv.org/display/fastpath/Collectd+Metrics+and+Events>`__.
|
||||
|
||||
To enable statistics, you have to enable OpenFlow 1.4 support for OVS.
|
||||
Configure bridge br0 to support OpenFlow version 1.4::
|
||||
@ -333,8 +337,9 @@ Query the port statistics by explicitly specifying -O OpenFlow14 option::
|
||||
|
||||
$ ovs-ofctl -O OpenFlow14 dump-ports br0
|
||||
|
||||
Note: vHost ports supports only partial statistics. RX packet size based
|
||||
counter are only supported and doesn't include TX packet size counters.
|
||||
Note about "Extended Statistics": vHost ports supports only partial
|
||||
statistics. RX packet size based counter are only supported and
|
||||
doesn't include TX packet size counters.
|
||||
|
||||
.. _port-hotplug:
|
||||
|
||||
|
5
NEWS
5
NEWS
@ -32,6 +32,11 @@ Post-v2.8.0
|
||||
* Add support for vHost IOMMU
|
||||
* New debug appctl command 'netdev-dpdk/get-mempool-info'.
|
||||
* All the netdev-dpdk appctl commands described in ovs-vswitchd man page.
|
||||
* Custom statistics:
|
||||
- DPDK physical ports now return custom set of "dropped", "error" and
|
||||
"management" statistics.
|
||||
- ovs-ofctl dump-ports command now prints new of set custom statistics
|
||||
if available (for OpenFlow 1.4+).
|
||||
- vswitchd:
|
||||
* Datapath IDs may now be specified as 0x1 (etc.) instead of 16 digits.
|
||||
* Configuring a controller, or unconfiguring all controllers, now deletes
|
||||
|
@ -27,9 +27,11 @@
|
||||
|
||||
enum intel_port_stats_subtype {
|
||||
INTEL_PORT_STATS_RFC2819 = 1,
|
||||
INTEL_PORT_STATS_CUSTOM
|
||||
};
|
||||
|
||||
#define INTEL_PORT_STATS_RFC2819_SIZE 184
|
||||
#define INTEL_PORT_STATS_CUSTOM_SIZE 16
|
||||
|
||||
/* Struct implements custom property type based on
|
||||
* 'ofp_prop_experimenter'. */
|
||||
@ -70,4 +72,30 @@ struct intel_port_stats_rfc2819 {
|
||||
OFP_ASSERT(sizeof (struct intel_port_stats_rfc2819) ==
|
||||
INTEL_PORT_STATS_RFC2819_SIZE);
|
||||
|
||||
/* Structure implements custom property type based on
|
||||
* 'ofp_prop_experimenter'. It contains custom
|
||||
* statistics in dictionary format */
|
||||
struct intel_port_custom_stats {
|
||||
ovs_be16 type; /* OFPPSPT14_EXPERIMENTER. */
|
||||
ovs_be16 length; /* Length in bytes of this property excluding
|
||||
* trailing padding. */
|
||||
ovs_be32 experimenter; /* INTEL_VENDOR_ID. */
|
||||
ovs_be32 exp_type; /* INTEL_PORT_STATS_*. */
|
||||
|
||||
uint8_t pad[2];
|
||||
ovs_be16 stats_array_size; /* number of counters. */
|
||||
|
||||
/* Followed by:
|
||||
* - Exactly 'stats_array_size' array elements of
|
||||
* dynamic structure which contains:
|
||||
* - "NAME SIZE" - counter name size (number of characters)
|
||||
* - "COUNTER NAME" - Exact number of characters
|
||||
* defined by "NAME SIZE".
|
||||
* - "COUNTER VALUE" - ovs_be64 counter value,
|
||||
* - Zero or more bytes to fill out the
|
||||
* overall length in header.length. */
|
||||
};
|
||||
OFP_ASSERT(sizeof(struct intel_port_custom_stats) ==
|
||||
INTEL_PORT_STATS_CUSTOM_SIZE);
|
||||
|
||||
#endif /* openflow/intel-ext.h */
|
||||
|
@ -27,6 +27,9 @@ extern "C" {
|
||||
|
||||
struct netdev;
|
||||
|
||||
/* Maximum name length for custom statistics counters */
|
||||
#define NETDEV_CUSTOM_STATS_NAME_SIZE 64
|
||||
|
||||
/* Network device statistics.
|
||||
*
|
||||
* Values of unsupported statistics are set to all-1-bits (UINT64_MAX). */
|
||||
@ -85,6 +88,18 @@ struct netdev_stats {
|
||||
uint64_t rx_jabber_errors;
|
||||
};
|
||||
|
||||
/* Structure representation of custom statistics counter */
|
||||
struct netdev_custom_counter {
|
||||
uint64_t value;
|
||||
char name[NETDEV_CUSTOM_STATS_NAME_SIZE];
|
||||
};
|
||||
|
||||
/* Structure representation of custom statistics */
|
||||
struct netdev_custom_stats {
|
||||
uint16_t size;
|
||||
struct netdev_custom_counter *counters;
|
||||
};
|
||||
|
||||
/* Features. */
|
||||
enum netdev_features {
|
||||
NETDEV_F_10MB_HD = 1 << 0, /* 10 Mb half-duplex rate support. */
|
||||
@ -115,6 +130,8 @@ uint64_t netdev_features_to_bps(enum netdev_features features,
|
||||
bool netdev_features_is_full_duplex(enum netdev_features features);
|
||||
int netdev_set_advertisements(struct netdev *, enum netdev_features advertise);
|
||||
|
||||
void netdev_free_custom_stats_counters(struct netdev_custom_stats *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -1174,6 +1174,7 @@ bool ofputil_parse_key_value(char **stringp, char **keyp, char **valuep);
|
||||
struct ofputil_port_stats {
|
||||
ofp_port_t port_no;
|
||||
struct netdev_stats stats;
|
||||
struct netdev_custom_stats custom_stats;
|
||||
uint32_t duration_sec; /* UINT32_MAX if unknown. */
|
||||
uint32_t duration_nsec;
|
||||
};
|
||||
|
@ -434,6 +434,14 @@ struct netdev_dpdk {
|
||||
* from the enum set 'dpdk_hw_ol_features' */
|
||||
uint32_t hw_ol_features;
|
||||
);
|
||||
|
||||
PADDED_MEMBERS(CACHE_LINE_SIZE,
|
||||
/* Names of all XSTATS counters */
|
||||
struct rte_eth_xstat_name *rte_xstats_names;
|
||||
int rte_xstats_names_size;
|
||||
int rte_xstats_ids_size;
|
||||
uint64_t *rte_xstats_ids;
|
||||
);
|
||||
};
|
||||
|
||||
struct netdev_rxq_dpdk {
|
||||
@ -916,6 +924,12 @@ common_construct(struct netdev *netdev, dpdk_port_t port_no,
|
||||
|
||||
netdev_request_reconfigure(netdev);
|
||||
|
||||
dev->rte_xstats_names = NULL;
|
||||
dev->rte_xstats_names_size = 0;
|
||||
|
||||
dev->rte_xstats_ids = NULL;
|
||||
dev->rte_xstats_ids_size = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1154,6 +1168,126 @@ netdev_dpdk_dealloc(struct netdev *netdev)
|
||||
rte_free(dev);
|
||||
}
|
||||
|
||||
static void
|
||||
netdev_dpdk_clear_xstats(struct netdev_dpdk *dev) OVS_REQUIRES(dev->mutex)
|
||||
{
|
||||
/* If statistics are already allocated, we have to
|
||||
* reconfigure, as port_id could have been changed. */
|
||||
if (dev->rte_xstats_names) {
|
||||
free(dev->rte_xstats_names);
|
||||
dev->rte_xstats_names = NULL;
|
||||
dev->rte_xstats_names_size = 0;
|
||||
}
|
||||
if (dev->rte_xstats_ids) {
|
||||
free(dev->rte_xstats_ids);
|
||||
dev->rte_xstats_ids = NULL;
|
||||
dev->rte_xstats_ids_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const char*
|
||||
netdev_dpdk_get_xstat_name(struct netdev_dpdk *dev, uint64_t id)
|
||||
{
|
||||
if (id >= dev->rte_xstats_names_size) {
|
||||
return "UNKNOWN";
|
||||
}
|
||||
return dev->rte_xstats_names[id].name;
|
||||
}
|
||||
|
||||
static bool
|
||||
netdev_dpdk_configure_xstats(struct netdev_dpdk *dev)
|
||||
OVS_REQUIRES(dev->mutex)
|
||||
{
|
||||
int rte_xstats_len;
|
||||
bool ret;
|
||||
struct rte_eth_xstat *rte_xstats;
|
||||
uint64_t id;
|
||||
int xstats_no;
|
||||
const char *name;
|
||||
|
||||
/* Retrieving all XSTATS names. If something will go wrong
|
||||
* or amount of counters will be equal 0, rte_xstats_names
|
||||
* buffer will be marked as NULL, and any further xstats
|
||||
* query won't be performed (e.g. during netdev_dpdk_get_stats
|
||||
* execution). */
|
||||
|
||||
ret = false;
|
||||
rte_xstats = NULL;
|
||||
|
||||
if (dev->rte_xstats_names == NULL || dev->rte_xstats_ids == NULL) {
|
||||
dev->rte_xstats_names_size =
|
||||
rte_eth_xstats_get_names(dev->port_id, NULL, 0);
|
||||
|
||||
if (dev->rte_xstats_names_size < 0) {
|
||||
VLOG_WARN("Cannot get XSTATS for port: %"PRIu8, dev->port_id);
|
||||
dev->rte_xstats_names_size = 0;
|
||||
} else {
|
||||
/* Reserve memory for xstats names and values */
|
||||
dev->rte_xstats_names = xcalloc(dev->rte_xstats_names_size,
|
||||
sizeof *dev->rte_xstats_names);
|
||||
|
||||
if (dev->rte_xstats_names) {
|
||||
/* Retreive xstats names */
|
||||
rte_xstats_len =
|
||||
rte_eth_xstats_get_names(dev->port_id,
|
||||
dev->rte_xstats_names,
|
||||
dev->rte_xstats_names_size);
|
||||
|
||||
if (rte_xstats_len < 0) {
|
||||
VLOG_WARN("Cannot get XSTATS names for port: %"PRIu8,
|
||||
dev->port_id);
|
||||
goto out;
|
||||
} else if (rte_xstats_len != dev->rte_xstats_names_size) {
|
||||
VLOG_WARN("XSTATS size doesn't match for port: %"PRIu8,
|
||||
dev->port_id);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->rte_xstats_ids = xcalloc(dev->rte_xstats_names_size,
|
||||
sizeof(uint64_t));
|
||||
|
||||
/* We have to calculate number of counters */
|
||||
rte_xstats = xmalloc(rte_xstats_len * sizeof *rte_xstats);
|
||||
memset(rte_xstats, 0xff, sizeof *rte_xstats * rte_xstats_len);
|
||||
|
||||
/* Retreive xstats values */
|
||||
if (rte_eth_xstats_get(dev->port_id, rte_xstats,
|
||||
rte_xstats_len) > 0) {
|
||||
dev->rte_xstats_ids_size = 0;
|
||||
xstats_no = 0;
|
||||
for (uint32_t i = 0; i < rte_xstats_len; i++) {
|
||||
id = rte_xstats[i].id;
|
||||
name = netdev_dpdk_get_xstat_name(dev, id);
|
||||
/* We need to filter out everything except
|
||||
* dropped, error and management counters */
|
||||
if (string_ends_with(name, "_errors") ||
|
||||
strstr(name, "_management_") ||
|
||||
string_ends_with(name, "_dropped")) {
|
||||
|
||||
dev->rte_xstats_ids[xstats_no] = id;
|
||||
xstats_no++;
|
||||
}
|
||||
}
|
||||
dev->rte_xstats_ids_size = xstats_no;
|
||||
ret = true;
|
||||
} else {
|
||||
VLOG_WARN("Can't get XSTATS IDs for port: %"PRIu8,
|
||||
dev->port_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Already configured */
|
||||
ret = true;
|
||||
}
|
||||
|
||||
out:
|
||||
if (!ret) {
|
||||
netdev_dpdk_clear_xstats(dev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
netdev_dpdk_get_config(const struct netdev *netdev, struct smap *args)
|
||||
{
|
||||
@ -1325,6 +1459,7 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args,
|
||||
dev->devargs = xstrdup(new_devargs);
|
||||
dev->port_id = new_port_id;
|
||||
netdev_request_reconfigure(&dev->up);
|
||||
netdev_dpdk_clear_xstats(dev);
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
@ -2181,6 +2316,56 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
netdev_dpdk_get_custom_stats(const struct netdev *netdev,
|
||||
struct netdev_custom_stats *custom_stats)
|
||||
{
|
||||
|
||||
uint32_t i;
|
||||
struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
|
||||
int rte_xstats_ret;
|
||||
|
||||
ovs_mutex_lock(&dev->mutex);
|
||||
|
||||
if (netdev_dpdk_configure_xstats(dev)) {
|
||||
uint64_t *values = xcalloc(dev->rte_xstats_ids_size,
|
||||
sizeof(uint64_t));
|
||||
|
||||
rte_xstats_ret =
|
||||
rte_eth_xstats_get_by_id(dev->port_id, dev->rte_xstats_ids,
|
||||
values, dev->rte_xstats_ids_size);
|
||||
|
||||
if (rte_xstats_ret > 0 &&
|
||||
rte_xstats_ret <= dev->rte_xstats_ids_size) {
|
||||
|
||||
custom_stats->size = rte_xstats_ret;
|
||||
custom_stats->counters =
|
||||
(struct netdev_custom_counter *) xcalloc(rte_xstats_ret,
|
||||
sizeof(struct netdev_custom_counter));
|
||||
|
||||
for (i = 0; i < rte_xstats_ret; i++) {
|
||||
ovs_strlcpy(custom_stats->counters[i].name,
|
||||
netdev_dpdk_get_xstat_name(dev,
|
||||
dev->rte_xstats_ids[i]),
|
||||
NETDEV_CUSTOM_STATS_NAME_SIZE);
|
||||
custom_stats->counters[i].value = values[i];
|
||||
}
|
||||
} else {
|
||||
VLOG_WARN("Cannot get XSTATS values for port: %"PRIu8,
|
||||
dev->port_id);
|
||||
custom_stats->counters = NULL;
|
||||
custom_stats->size = 0;
|
||||
/* Let's clear statistics cache, so it will be
|
||||
* reconfigured */
|
||||
netdev_dpdk_clear_xstats(dev);
|
||||
}
|
||||
}
|
||||
|
||||
ovs_mutex_unlock(&dev->mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
netdev_dpdk_get_features(const struct netdev *netdev,
|
||||
enum netdev_features *current,
|
||||
@ -3392,6 +3577,7 @@ unlock:
|
||||
#define NETDEV_DPDK_CLASS(NAME, INIT, CONSTRUCT, DESTRUCT, \
|
||||
SET_CONFIG, SET_TX_MULTIQ, SEND, \
|
||||
GET_CARRIER, GET_STATS, \
|
||||
GET_CUSTOM_STATS, \
|
||||
GET_FEATURES, GET_STATUS, \
|
||||
RECONFIGURE, RXQ_RECV) \
|
||||
{ \
|
||||
@ -3426,6 +3612,7 @@ unlock:
|
||||
netdev_dpdk_get_carrier_resets, \
|
||||
netdev_dpdk_set_miimon, \
|
||||
GET_STATS, \
|
||||
GET_CUSTOM_STATS, \
|
||||
GET_FEATURES, \
|
||||
NULL, /* set_advertisements */ \
|
||||
NULL, /* get_pt_mode */ \
|
||||
@ -3475,6 +3662,7 @@ static const struct netdev_class dpdk_class =
|
||||
netdev_dpdk_eth_send,
|
||||
netdev_dpdk_get_carrier,
|
||||
netdev_dpdk_get_stats,
|
||||
netdev_dpdk_get_custom_stats,
|
||||
netdev_dpdk_get_features,
|
||||
netdev_dpdk_get_status,
|
||||
netdev_dpdk_reconfigure,
|
||||
@ -3491,6 +3679,7 @@ static const struct netdev_class dpdk_ring_class =
|
||||
netdev_dpdk_ring_send,
|
||||
netdev_dpdk_get_carrier,
|
||||
netdev_dpdk_get_stats,
|
||||
netdev_dpdk_get_custom_stats,
|
||||
netdev_dpdk_get_features,
|
||||
netdev_dpdk_get_status,
|
||||
netdev_dpdk_reconfigure,
|
||||
@ -3509,6 +3698,7 @@ static const struct netdev_class dpdk_vhost_class =
|
||||
netdev_dpdk_vhost_get_stats,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
netdev_dpdk_vhost_reconfigure,
|
||||
netdev_dpdk_vhost_rxq_recv);
|
||||
static const struct netdev_class dpdk_vhost_client_class =
|
||||
@ -3524,6 +3714,7 @@ static const struct netdev_class dpdk_vhost_client_class =
|
||||
netdev_dpdk_vhost_get_stats,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
netdev_dpdk_vhost_client_reconfigure,
|
||||
netdev_dpdk_vhost_rxq_recv);
|
||||
|
||||
|
@ -46,6 +46,8 @@
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(netdev_dummy);
|
||||
|
||||
#define C_STATS_SIZE 2
|
||||
|
||||
struct reconnect;
|
||||
|
||||
struct dummy_packet_stream {
|
||||
@ -109,6 +111,7 @@ struct netdev_dummy {
|
||||
struct eth_addr hwaddr OVS_GUARDED;
|
||||
int mtu OVS_GUARDED;
|
||||
struct netdev_stats stats OVS_GUARDED;
|
||||
struct netdev_custom_counter custom_stats[C_STATS_SIZE] OVS_GUARDED;
|
||||
enum netdev_flags flags OVS_GUARDED;
|
||||
int ifindex OVS_GUARDED;
|
||||
int numa_id OVS_GUARDED;
|
||||
@ -686,6 +689,13 @@ netdev_dummy_construct(struct netdev *netdev_)
|
||||
netdev->requested_n_txq = netdev_->n_txq;
|
||||
netdev->numa_id = 0;
|
||||
|
||||
memset(&netdev->custom_stats, 0, sizeof(netdev->custom_stats));
|
||||
|
||||
ovs_strlcpy(netdev->custom_stats[0].name,
|
||||
"rx_custom_packets_1", NETDEV_CUSTOM_STATS_NAME_SIZE);
|
||||
ovs_strlcpy(netdev->custom_stats[1].name,
|
||||
"rx_custom_packets_2", NETDEV_CUSTOM_STATS_NAME_SIZE);
|
||||
|
||||
dummy_packet_conn_init(&netdev->conn);
|
||||
|
||||
ovs_list_init(&netdev->rxes);
|
||||
@ -1021,6 +1031,8 @@ netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
|
||||
ovs_mutex_lock(&netdev->mutex);
|
||||
netdev->stats.rx_packets++;
|
||||
netdev->stats.rx_bytes += dp_packet_size(packet);
|
||||
netdev->custom_stats[0].value++;
|
||||
netdev->custom_stats[1].value++;
|
||||
ovs_mutex_unlock(&netdev->mutex);
|
||||
|
||||
batch->packets[0] = packet;
|
||||
@ -1214,6 +1226,29 @@ netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
netdev_dummy_get_custom_stats(const struct netdev *netdev,
|
||||
struct netdev_custom_stats *custom_stats)
|
||||
{
|
||||
int i;
|
||||
|
||||
struct netdev_dummy *dev = netdev_dummy_cast(netdev);
|
||||
|
||||
custom_stats->size = 2;
|
||||
custom_stats->counters =
|
||||
(struct netdev_custom_counter *) xcalloc(C_STATS_SIZE,
|
||||
sizeof(struct netdev_custom_counter));
|
||||
|
||||
for (i = 0 ; i < C_STATS_SIZE ; i++) {
|
||||
custom_stats->counters[i].value = dev->custom_stats[i].value;
|
||||
ovs_strlcpy(custom_stats->counters[i].name,
|
||||
dev->custom_stats[i].name,
|
||||
NETDEV_CUSTOM_STATS_NAME_SIZE);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
netdev_dummy_get_queue(const struct netdev *netdev OVS_UNUSED,
|
||||
unsigned int queue_id, struct smap *details OVS_UNUSED)
|
||||
@ -1383,6 +1418,7 @@ netdev_dummy_update_flags(struct netdev *netdev_,
|
||||
NULL, /* get_carrier_resets */ \
|
||||
NULL, /* get_miimon */ \
|
||||
netdev_dummy_get_stats, \
|
||||
netdev_dummy_get_custom_stats, \
|
||||
\
|
||||
NULL, /* get_features */ \
|
||||
NULL, /* set_advertisements */ \
|
||||
|
@ -2853,6 +2853,7 @@ netdev_linux_update_flags(struct netdev *netdev_, enum netdev_flags off,
|
||||
netdev_linux_get_carrier_resets, \
|
||||
netdev_linux_set_miimon_interval, \
|
||||
GET_STATS, \
|
||||
NULL, \
|
||||
\
|
||||
GET_FEATURES, \
|
||||
netdev_linux_set_advertisements, \
|
||||
|
@ -458,6 +458,19 @@ struct netdev_class {
|
||||
* (UINT64_MAX). */
|
||||
int (*get_stats)(const struct netdev *netdev, struct netdev_stats *);
|
||||
|
||||
/* Retrieves current device custom stats for 'netdev' into 'custom_stats'.
|
||||
*
|
||||
* A network device should return only available statistics (if any).
|
||||
* If there are not statistics available, empty array should be
|
||||
* returned.
|
||||
*
|
||||
* The caller initializes 'custom_stats' before calling this function.
|
||||
* The caller takes ownership over allocated array of counters inside
|
||||
* structure netdev_custom_stats.
|
||||
* */
|
||||
int (*get_custom_stats)(const struct netdev *netdev,
|
||||
struct netdev_custom_stats *custom_stats);
|
||||
|
||||
/* Stores the features supported by 'netdev' into each of '*current',
|
||||
* '*advertised', '*supported', and '*peer'. Each value is a bitmap of
|
||||
* NETDEV_F_* bits.
|
||||
|
@ -907,6 +907,7 @@ netdev_vport_get_ifindex(const struct netdev *netdev_)
|
||||
NULL, /* get_carrier_resets */ \
|
||||
NULL, /* get_miimon */ \
|
||||
get_stats, \
|
||||
NULL, /* get_custom_stats */ \
|
||||
\
|
||||
NULL, /* get_features */ \
|
||||
NULL, /* set_advertisements */ \
|
||||
|
27
lib/netdev.c
27
lib/netdev.c
@ -1421,6 +1421,21 @@ netdev_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
|
||||
return error;
|
||||
}
|
||||
|
||||
/* Retrieves current device custom stats for 'netdev'. */
|
||||
int
|
||||
netdev_get_custom_stats(const struct netdev *netdev,
|
||||
struct netdev_custom_stats *custom_stats)
|
||||
{
|
||||
int error;
|
||||
memset(custom_stats, 0, sizeof *custom_stats);
|
||||
error = (netdev->netdev_class->get_custom_stats
|
||||
? netdev->netdev_class->get_custom_stats(netdev, custom_stats)
|
||||
: EOPNOTSUPP);
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
|
||||
/* Attempts to set input rate limiting (policing) policy, such that up to
|
||||
* 'kbits_rate' kbps of traffic is accepted, with a maximum accumulative burst
|
||||
* size of 'kbits' kb. */
|
||||
@ -2376,6 +2391,18 @@ netdev_ports_flow_get(const struct dpif_class *dpif_class, struct match *match,
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
void
|
||||
netdev_free_custom_stats_counters(struct netdev_custom_stats *custom_stats)
|
||||
{
|
||||
if (custom_stats) {
|
||||
if (custom_stats->counters) {
|
||||
free(custom_stats->counters);
|
||||
custom_stats->counters = NULL;
|
||||
custom_stats->size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static void
|
||||
netdev_ports_flow_init(void)
|
||||
|
@ -296,6 +296,8 @@ struct netdev *netdev_find_dev_by_in4(const struct in_addr *);
|
||||
|
||||
/* Statistics. */
|
||||
int netdev_get_stats(const struct netdev *, struct netdev_stats *);
|
||||
int netdev_get_custom_stats(const struct netdev *,
|
||||
struct netdev_custom_stats *);
|
||||
|
||||
/* Quality of service. */
|
||||
struct netdev_qos_capabilities {
|
||||
|
@ -1841,6 +1841,7 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
|
||||
const struct ofputil_port_map *port_map,
|
||||
int verbosity)
|
||||
{
|
||||
uint32_t i;
|
||||
ds_put_format(string, " %"PRIuSIZE" ports\n", ofputil_count_port_stats(oh));
|
||||
if (verbosity < 1) {
|
||||
return 0;
|
||||
@ -1950,6 +1951,25 @@ ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
|
||||
ds_put_cstr(string, "\n");
|
||||
ds_destroy(&string_ext_stats);
|
||||
}
|
||||
|
||||
if (ps.custom_stats.size) {
|
||||
ds_put_cstr(string, " CUSTOM Statistics");
|
||||
for (i = 0; i < ps.custom_stats.size; i++) {
|
||||
/* 3 counters in the row */
|
||||
if (ps.custom_stats.counters[i].name[0]) {
|
||||
if (i % 3 == 0) {
|
||||
ds_put_cstr(string, "\n");
|
||||
ds_put_cstr(string, " ");
|
||||
} else {
|
||||
ds_put_char(string, ' ');
|
||||
}
|
||||
ds_put_format(string, "%s=%"PRIu64",",
|
||||
ps.custom_stats.counters[i].name,
|
||||
ps.custom_stats.counters[i].value);
|
||||
}
|
||||
}
|
||||
ds_put_cstr(string, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
106
lib/ofp-util.c
106
lib/ofp-util.c
@ -7999,15 +7999,18 @@ ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
|
||||
{
|
||||
struct ofp14_port_stats_prop_ethernet *eth;
|
||||
struct intel_port_stats_rfc2819 *stats_rfc2819;
|
||||
struct intel_port_custom_stats *stats_custom;
|
||||
struct ofp14_port_stats *ps14;
|
||||
struct ofpbuf *reply;
|
||||
uint16_t i;
|
||||
ovs_be64 counter_value;
|
||||
size_t custom_stats_start, start_ofs;
|
||||
|
||||
reply = ofpmp_reserve(replies, sizeof *ps14 + sizeof *eth +
|
||||
sizeof *stats_rfc2819);
|
||||
reply = ofpbuf_from_list(ovs_list_back(replies));
|
||||
start_ofs = reply->size;
|
||||
|
||||
ps14 = ofpbuf_put_uninit(reply, sizeof *ps14);
|
||||
ps14->length = htons(sizeof *ps14 + sizeof *eth +
|
||||
sizeof *stats_rfc2819);
|
||||
|
||||
memset(ps14->pad, 0, sizeof ps14->pad);
|
||||
ps14->port_no = ofputil_port_to_ofp11(ops->port_no);
|
||||
ps14->duration_sec = htonl(ops->duration_sec);
|
||||
@ -8027,10 +8030,10 @@ ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
|
||||
eth->rx_crc_err = htonll(ops->stats.rx_crc_errors);
|
||||
eth->collisions = htonll(ops->stats.collisions);
|
||||
|
||||
uint64_t prop_type = OFPPROP_EXP(INTEL_VENDOR_ID,
|
||||
uint64_t prop_type_stats = OFPPROP_EXP(INTEL_VENDOR_ID,
|
||||
INTEL_PORT_STATS_RFC2819);
|
||||
|
||||
stats_rfc2819 = ofpprop_put_zeros(reply, prop_type,
|
||||
stats_rfc2819 = ofpprop_put_zeros(reply, prop_type_stats,
|
||||
sizeof *stats_rfc2819);
|
||||
|
||||
memset(stats_rfc2819->pad, 0, sizeof stats_rfc2819->pad);
|
||||
@ -8076,6 +8079,38 @@ ofputil_append_ofp14_port_stats(const struct ofputil_port_stats *ops,
|
||||
htonll(ops->stats.rx_fragmented_errors);
|
||||
stats_rfc2819->rx_jabber_errors =
|
||||
htonll(ops->stats.rx_jabber_errors);
|
||||
|
||||
if (ops->custom_stats.counters && ops->custom_stats.size) {
|
||||
custom_stats_start = reply->size;
|
||||
|
||||
uint64_t prop_type_custom = OFPPROP_EXP(INTEL_VENDOR_ID,
|
||||
INTEL_PORT_STATS_CUSTOM);
|
||||
|
||||
stats_custom = ofpprop_put_zeros(reply, prop_type_custom,
|
||||
sizeof *stats_custom);
|
||||
|
||||
stats_custom->stats_array_size = htons(ops->custom_stats.size);
|
||||
|
||||
for (i = 0; i < ops->custom_stats.size; i++) {
|
||||
uint8_t counter_size = strlen(ops->custom_stats.counters[i].name);
|
||||
/* Counter name size */
|
||||
ofpbuf_put(reply, &counter_size, sizeof(counter_size));
|
||||
/* Counter name */
|
||||
ofpbuf_put(reply, ops->custom_stats.counters[i].name,
|
||||
counter_size);
|
||||
/* Counter value */
|
||||
counter_value = htonll(ops->custom_stats.counters[i].value);
|
||||
ofpbuf_put(reply, &counter_value,
|
||||
sizeof(ops->custom_stats.counters[i].value));
|
||||
}
|
||||
|
||||
ofpprop_end(reply, custom_stats_start);
|
||||
}
|
||||
|
||||
ps14 = ofpbuf_at_assert(reply, start_ofs, sizeof *ps14);
|
||||
ps14->length = htons(reply->size - start_ofs);
|
||||
|
||||
ofpmp_postappend(replies, start_ofs);
|
||||
}
|
||||
|
||||
/* Encode a ports stat for 'ops' and append it to 'replies'. */
|
||||
@ -8238,6 +8273,56 @@ parse_intel_port_stats_rfc2819_property(const struct ofpbuf *payload,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_intel_port_custom_property(const struct ofpbuf *payload,
|
||||
struct ofputil_port_stats *ops)
|
||||
{
|
||||
const struct intel_port_custom_stats *custom_stats = payload->data;
|
||||
|
||||
ops->custom_stats.size = ntohs(custom_stats->stats_array_size);
|
||||
|
||||
ops->custom_stats.counters = xcalloc(ops->custom_stats.size,
|
||||
sizeof *ops->custom_stats.counters);
|
||||
|
||||
uint16_t msg_size = ntohs(custom_stats->length);
|
||||
uint16_t current_len = sizeof *custom_stats;
|
||||
uint8_t *current = (uint8_t *)payload->data + current_len;
|
||||
uint8_t string_size = 0;
|
||||
uint8_t value_size = 0;
|
||||
ovs_be64 counter_value = 0;
|
||||
|
||||
for (int i = 0; i < ops->custom_stats.size; i++) {
|
||||
current_len += string_size + value_size;
|
||||
current += string_size + value_size;
|
||||
|
||||
value_size = sizeof(uint64_t);
|
||||
/* Counter name size */
|
||||
string_size = *current;
|
||||
|
||||
/* Buffer overrun check */
|
||||
if (current_len + string_size + value_size > msg_size) {
|
||||
VLOG_WARN_RL(&bad_ofmsg_rl, "Custom statistics buffer overrun! "
|
||||
"Further message parsing is aborted.");
|
||||
break;
|
||||
}
|
||||
|
||||
current++;
|
||||
current_len++;
|
||||
|
||||
/* Counter name. */
|
||||
struct netdev_custom_counter *c = &ops->custom_stats.counters[i];
|
||||
size_t len = MIN(string_size, sizeof c->name - 1);
|
||||
memcpy(c->name, current, len);
|
||||
c->name[len] = '\0';
|
||||
memcpy(&counter_value, current + string_size, value_size);
|
||||
|
||||
/* Counter value. */
|
||||
c->value = ntohll(counter_value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum ofperr
|
||||
parse_intel_port_stats_property(const struct ofpbuf *payload,
|
||||
uint32_t exp_type,
|
||||
@ -8249,6 +8334,9 @@ parse_intel_port_stats_property(const struct ofpbuf *payload,
|
||||
case INTEL_PORT_STATS_RFC2819:
|
||||
error = parse_intel_port_stats_rfc2819_property(payload, ops);
|
||||
break;
|
||||
case INTEL_PORT_STATS_CUSTOM:
|
||||
error = parse_intel_port_custom_property(payload, ops);
|
||||
break;
|
||||
default:
|
||||
error = OFPERR_OFPBPC_BAD_EXP_TYPE;
|
||||
break;
|
||||
@ -8308,6 +8396,11 @@ ofputil_pull_ofp14_port_stats(struct ofputil_port_stats *ops,
|
||||
INTEL_PORT_STATS_RFC2819,
|
||||
ops);
|
||||
break;
|
||||
case OFPPROP_EXP(INTEL_VENDOR_ID, INTEL_PORT_STATS_CUSTOM):
|
||||
error = parse_intel_port_stats_property(&payload,
|
||||
INTEL_PORT_STATS_CUSTOM,
|
||||
ops);
|
||||
break;
|
||||
default:
|
||||
error = OFPPROP_UNKNOWN(true, "port stats", type);
|
||||
break;
|
||||
@ -8354,6 +8447,7 @@ ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
|
||||
enum ofpraw raw;
|
||||
|
||||
memset(&(ps->stats), 0xFF, sizeof (ps->stats));
|
||||
memset(&(ps->custom_stats), 0, sizeof (ps->custom_stats));
|
||||
|
||||
error = (msg->header ? ofpraw_decode(&raw, msg->header)
|
||||
: ofpraw_pull(&raw, msg));
|
||||
|
13
lib/util.c
13
lib/util.c
@ -321,6 +321,19 @@ ovs_strzcpy(char *dst, const char *src, size_t size)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if 'str' ends with given 'suffix'.
|
||||
*/
|
||||
int
|
||||
string_ends_with(const char *str, const char *suffix)
|
||||
{
|
||||
int str_len = strlen(str);
|
||||
int suffix_len = strlen(suffix);
|
||||
|
||||
return (str_len >= suffix_len) &&
|
||||
(0 == strcmp(str + (str_len - suffix_len), suffix));
|
||||
}
|
||||
|
||||
/* Prints 'format' on stderr, formatting it like printf() does. If 'err_no' is
|
||||
* nonzero, then it is formatted with ovs_retval_to_string() and appended to
|
||||
* the message inside parentheses. Then, terminates with abort().
|
||||
|
@ -159,6 +159,8 @@ void free_cacheline(void *);
|
||||
void ovs_strlcpy(char *dst, const char *src, size_t size);
|
||||
void ovs_strzcpy(char *dst, const char *src, size_t size);
|
||||
|
||||
int string_ends_with(const char *str, const char *suffix);
|
||||
|
||||
/* The C standards say that neither the 'dst' nor 'src' argument to
|
||||
* memcpy() may be null, even if 'n' is zero. This wrapper tolerates
|
||||
* the null case. */
|
||||
|
@ -3890,8 +3890,11 @@ append_port_stat(struct ofport *port, struct ovs_list *replies)
|
||||
* 'stats' to all-1s, which is correct for OpenFlow, and
|
||||
* netdev_get_stats() will log errors. */
|
||||
ofproto_port_get_stats(port, &ops.stats);
|
||||
netdev_get_custom_stats(port->netdev, &ops.custom_stats);
|
||||
|
||||
ofputil_append_port_stat(replies, &ops);
|
||||
|
||||
netdev_free_custom_stats_counters(&ops.custom_stats);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -111,6 +111,8 @@ OFPST_PORT reply (OF1.4): 1 ports
|
||||
port LOCAL: rx pkts=0, bytes=0, drop=?, errs=?, frame=?, over=?, crc=?
|
||||
tx pkts=0, bytes=0, drop=?, errs=?, coll=?
|
||||
duration=?s
|
||||
CUSTOM Statistics
|
||||
rx_custom_packets_1=0, rx_custom_packets_2=0,
|
||||
])
|
||||
OVS_VSWITCHD_STOP
|
||||
AT_CLEANUP
|
||||
|
@ -2347,6 +2347,11 @@ iface_refresh_cfm_stats(struct iface *iface)
|
||||
static void
|
||||
iface_refresh_stats(struct iface *iface)
|
||||
{
|
||||
struct netdev_custom_stats custom_stats;
|
||||
struct netdev_stats stats;
|
||||
int n;
|
||||
uint32_t i, counters_size;
|
||||
|
||||
#define IFACE_STATS \
|
||||
IFACE_STAT(rx_packets, "rx_packets") \
|
||||
IFACE_STAT(tx_packets, "tx_packets") \
|
||||
@ -2385,16 +2390,17 @@ iface_refresh_stats(struct iface *iface)
|
||||
#define IFACE_STAT(MEMBER, NAME) + 1
|
||||
enum { N_IFACE_STATS = IFACE_STATS };
|
||||
#undef IFACE_STAT
|
||||
int64_t values[N_IFACE_STATS];
|
||||
const char *keys[N_IFACE_STATS];
|
||||
int n;
|
||||
|
||||
struct netdev_stats stats;
|
||||
|
||||
if (iface_is_synthetic(iface)) {
|
||||
return;
|
||||
}
|
||||
|
||||
netdev_get_custom_stats(iface->netdev, &custom_stats);
|
||||
|
||||
counters_size = custom_stats.size + N_IFACE_STATS;
|
||||
int64_t *values = xmalloc(counters_size * sizeof(int64_t));
|
||||
const char **keys = xmalloc(counters_size * sizeof(char *));
|
||||
|
||||
/* Intentionally ignore return value, since errors will set 'stats' to
|
||||
* all-1s, and we will deal with that correctly below. */
|
||||
netdev_get_stats(iface->netdev, &stats);
|
||||
@ -2409,10 +2415,23 @@ iface_refresh_stats(struct iface *iface)
|
||||
}
|
||||
IFACE_STATS;
|
||||
#undef IFACE_STAT
|
||||
ovs_assert(n <= N_IFACE_STATS);
|
||||
|
||||
/* Copy custom statistics into keys[] and values[]. */
|
||||
if (custom_stats.size && custom_stats.counters) {
|
||||
for (i = 0 ; i < custom_stats.size ; i++) {
|
||||
values[n] = custom_stats.counters[i].value;
|
||||
keys[n] = custom_stats.counters[i].name;
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
ovs_assert(n <= counters_size);
|
||||
|
||||
ovsrec_interface_set_statistics(iface->cfg, keys, values, n);
|
||||
#undef IFACE_STATS
|
||||
|
||||
free(values);
|
||||
free(keys);
|
||||
}
|
||||
|
||||
static void
|
||||
|
Loading…
x
Reference in New Issue
Block a user