2
0
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:
Michal Weglicki 2018-01-09 07:55:37 +00:00 committed by Ben Pfaff
parent cd32509e4a
commit 971f4b394c
19 changed files with 498 additions and 18 deletions

View File

@ -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
View File

@ -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

View File

@ -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 */

View File

@ -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

View File

@ -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;
};

View File

@ -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);

View File

@ -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 */ \

View File

@ -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, \

View File

@ -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.

View File

@ -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 */ \

View File

@ -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)

View File

@ -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 {

View File

@ -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");
}
}
}

View File

@ -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));

View File

@ -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().

View File

@ -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. */

View File

@ -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

View File

@ -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

View File

@ -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