From patchwork Tue Dec 5 14:55:20 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Weglicki, MichalX" X-Patchwork-Id: 844807 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (mailfrom) smtp.mailfrom=openvswitch.org (client-ip=140.211.169.12; helo=mail.linuxfoundation.org; envelope-from=ovs-dev-bounces@openvswitch.org; receiver=) Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3yrlZN3nqrz9ryQ for ; Wed, 6 Dec 2017 02:11:24 +1100 (AEDT) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id ACF0D10CB; Tue, 5 Dec 2017 15:11:21 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp1.linuxfoundation.org (smtp1.linux-foundation.org [172.17.192.35]) by mail.linuxfoundation.org (Postfix) with ESMTPS id D3DA9101B for ; Tue, 5 Dec 2017 15:11:19 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from mga06.intel.com (mga06.intel.com [134.134.136.31]) by smtp1.linuxfoundation.org (Postfix) with ESMTPS id D3B3F4DD for ; Tue, 5 Dec 2017 15:11:17 +0000 (UTC) Received: from orsmga004.jf.intel.com ([10.7.209.38]) by orsmga104.jf.intel.com with ESMTP/TLS/DHE-RSA-AES256-GCM-SHA384; 05 Dec 2017 07:11:17 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.45,364,1508828400"; d="scan'208";a="156267883" Received: from irvmail001.ir.intel.com ([163.33.26.43]) by orsmga004.jf.intel.com with ESMTP; 05 Dec 2017 07:11:15 -0800 Received: from silpixa00389819.ir.intel.com (silpixa00389819.ir.intel.com [10.237.219.152]) by irvmail001.ir.intel.com (8.14.3/8.13.6/MailSET/Hub) with ESMTP id vB5FBEkM026721; Tue, 5 Dec 2017 15:11:14 GMT From: Michal Weglicki To: dev@openvswitch.org Date: Tue, 5 Dec 2017 14:55:20 +0000 Message-Id: <1512485720-77815-1-git-send-email-michalx.weglicki@intel.com> X-Mailer: git-send-email 1.8.3.1 X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, T_RP_MATCHES_RCVD autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp1.linux-foundation.org Subject: [ovs-dev] [PATCH v2] netdev: Custom statistics. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org - 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. v1->v2: - Buffer overrun check in parse_intel_port_custom_property. - ofputil_append_ofp14_port_stats uses "postappend" instead of "reserve" during message creation. - NEWS update. - DPDK documentation update. - Compilation and sparse warnings corrections. Signed-off-by: Michal Weglicki --- Documentation/howto/dpdk.rst | 15 ++-- NEWS | 6 ++ include/openflow/intel-ext.h | 28 ++++++ include/openvswitch/netdev.h | 17 ++++ include/openvswitch/ofp-util.h | 1 + lib/netdev-dpdk.c | 195 ++++++++++++++++++++++++++++++++++++++++- lib/netdev-dummy.c | 1 + lib/netdev-linux.c | 1 + lib/netdev-provider.h | 13 +++ lib/netdev-vport.c | 1 + lib/netdev.c | 27 ++++++ lib/netdev.h | 2 + lib/ofp-print.c | 18 ++++ lib/ofp-util.c | 119 +++++++++++++++++++++++-- lib/util.c | 13 +++ lib/util.h | 2 + ofproto/ofproto-dpif.c | 13 +++ ofproto/ofproto-provider.h | 4 + ofproto/ofproto.c | 21 +++++ ofproto/ofproto.h | 3 + vswitchd/bridge.c | 24 +++++ 21 files changed, 512 insertions(+), 12 deletions(-) diff --git a/Documentation/howto/dpdk.rst b/Documentation/howto/dpdk.rst index d123819..c99ec29 100644 --- a/Documentation/howto/dpdk.rst +++ b/Documentation/howto/dpdk.rst @@ -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" and "error" +counters from XSTATS. XSTATS counters list can be found here: +`__. 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: diff --git a/NEWS b/NEWS index 427c8f8..727142c 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,12 @@ Post-v2.8.0 * ovn-ctl: New commands run_nb_ovsdb and run_sb_ovsdb. - Linux kernel 4.13 * Add support for compiling OVS with the latest Linux 4.13 kernel + - DPDK: + * Custom statistics: + - DPDK physical ports now return custom set of "dropped" and "error" + statistics. + - ovs-ofctl dump-ports command now prints new of set custom statistics + if available (for OpenFlow 1.4+). v2.8.0 - 31 Aug 2017 -------------------- diff --git a/include/openflow/intel-ext.h b/include/openflow/intel-ext.h index 974e63e..3d73171 100644 --- a/include/openflow/intel-ext.h +++ b/include/openflow/intel-ext.h @@ -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 */ diff --git a/include/openvswitch/netdev.h b/include/openvswitch/netdev.h index 50bafc3..e25c241 100644 --- a/include/openvswitch/netdev.h +++ b/include/openvswitch/netdev.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 diff --git a/include/openvswitch/ofp-util.h b/include/openvswitch/ofp-util.h index a9e57ed..296078a 100644 --- a/include/openvswitch/ofp-util.h +++ b/include/openvswitch/ofp-util.h @@ -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; }; diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c index faff842..8acc5e0 100644 --- a/lib/netdev-dpdk.c +++ b/lib/netdev-dpdk.c @@ -415,6 +415,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 { @@ -897,6 +905,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; } @@ -1135,6 +1149,128 @@ 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 = xcalloc(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 and error counters */ + if (string_ends_with(name, "_errors") || + 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) { @@ -1307,6 +1443,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; } } @@ -2171,6 +2308,56 @@ out: } 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++) { + strncpy(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, enum netdev_features *advertised, @@ -3313,7 +3500,8 @@ unlock: #define NETDEV_DPDK_CLASS(NAME, INIT, CONSTRUCT, DESTRUCT, \ SET_CONFIG, SET_TX_MULTIQ, SEND, \ - GET_CARRIER, GET_STATS, \ + GET_CARRIER, GET_STATS, \ + GET_CUSTOM_STATS, \ GET_FEATURES, GET_STATUS, \ RECONFIGURE, RXQ_RECV) \ { \ @@ -3348,6 +3536,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 */ \ @@ -3397,6 +3586,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, @@ -3413,6 +3603,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, @@ -3431,6 +3622,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 = @@ -3446,6 +3638,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); diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c index 246cdf1..1731b77 100644 --- a/lib/netdev-dummy.c +++ b/lib/netdev-dummy.c @@ -1383,6 +1383,7 @@ netdev_dummy_update_flags(struct netdev *netdev_, NULL, /* get_carrier_resets */ \ NULL, /* get_miimon */ \ netdev_dummy_get_stats, \ + NULL, \ \ NULL, /* get_features */ \ NULL, /* set_advertisements */ \ diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index e809b88..4e9be30 100644 --- a/lib/netdev-linux.c +++ b/lib/netdev-linux.c @@ -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, \ diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index 1720deb..d66fd5b 100644 --- a/lib/netdev-provider.h +++ b/lib/netdev-provider.h @@ -459,6 +459,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. diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c index 518058a..1a3322b 100644 --- a/lib/netdev-vport.c +++ b/lib/netdev-vport.c @@ -906,6 +906,7 @@ netdev_vport_get_ifindex(const struct netdev *netdev_) NULL, /* get_carrier_resets */ \ NULL, /* get_miimon */ \ get_stats, \ + NULL, \ \ NULL, /* get_features */ \ NULL, /* set_advertisements */ \ diff --git a/lib/netdev.c b/lib/netdev.c index 2d69fe5..cd11930 100644 --- a/lib/netdev.c +++ b/lib/netdev.c @@ -1425,6 +1425,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. */ @@ -2380,6 +2395,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) diff --git a/lib/netdev.h b/lib/netdev.h index 3a545fe..34df7c9 100644 --- a/lib/netdev.h +++ b/lib/netdev.h @@ -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 { diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 151d618..9a5768d 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -1834,6 +1834,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; @@ -1946,6 +1947,23 @@ 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 (strlen(ps.custom_stats.counters[i].name)) { + if ((i % 3) == 0) { + ds_put_cstr(string, "\n"); + ds_put_cstr(string, " "); + } + ds_put_format(string, "%s=%"PRIu64", ", + ps.custom_stats.counters[i].name, + ps.custom_stats.counters[i].value); + } + } + ds_put_cstr(string, "\n"); + } } } diff --git a/lib/ofp-util.c b/lib/ofp-util.c index 47f30c7..0652588 100644 --- a/lib/ofp-util.c +++ b/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; + uint64_t 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,44 @@ 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); + memset(stats_custom->pad, 0, sizeof stats_custom->pad); + + for (i = 0 ; i < ops->custom_stats.size; i++) { + uint8_t counter_size = strnlen(ops->custom_stats.counters[i].name, + NETDEV_CUSTOM_STATS_NAME_SIZE); + /* 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)); + } + + ofpbuf_padto(reply, ROUND_UP(reply->size, 8)); + stats_custom = ofpbuf_at_assert(reply, custom_stats_start, + sizeof *stats_custom); + stats_custom->length = htons(reply->size - 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'. */ @@ -8239,6 +8280,63 @@ parse_intel_port_stats_rfc2819_property(const struct ofpbuf *payload, } static enum ofperr +parse_intel_port_custom_property(const struct ofpbuf *payload, + struct ofputil_port_stats *ops) +{ + uint16_t i; + + const struct intel_port_custom_stats *custom_stats = payload->data; + + ops->custom_stats.size = ntohs(custom_stats->stats_array_size); + + ops->custom_stats.counters = (struct netdev_custom_counter *) + xcalloc(ops->custom_stats.size, + sizeof(struct netdev_custom_counter)); + + 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; + uint64_t counter_value = 0; + + for (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_ERR("Custom statistics buffer overrun! " + "Further message parsing is aborted."); + break; + } + + current++; + current_len++; + /* Counter name */ + if (string_size > sizeof(ops->custom_stats.counters[i].name)) { + VLOG_WARN("Counter name size too big! Only part " + "of the name will be copied."); + memcpy(ops->custom_stats.counters[i].name, current, + sizeof(ops->custom_stats.counters[i].name)); + } else { + memcpy(ops->custom_stats.counters[i].name, current, string_size); + } + /* Counter value */ + memcpy(&counter_value, current + string_size, + value_size); + ops->custom_stats.counters[i].value = ntohll(counter_value); + } + + return 0; +} + +static enum ofperr parse_intel_port_stats_property(const struct ofpbuf *payload, uint32_t exp_type, struct ofputil_port_stats *ops) @@ -8249,6 +8347,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 +8409,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 +8460,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)); diff --git a/lib/util.c b/lib/util.c index 2965656..462c1fa 100644 --- a/lib/util.c +++ b/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(). diff --git a/lib/util.h b/lib/util.h index b01f421..e75f6ce 100644 --- a/lib/util.h +++ b/lib/util.h @@ -157,6 +157,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. */ diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c index b99f04f..e53ee35 100644 --- a/ofproto/ofproto-dpif.c +++ b/ofproto/ofproto-dpif.c @@ -3775,6 +3775,18 @@ port_get_stats(const struct ofport *ofport_, struct netdev_stats *stats) } static int +port_get_custom_stats(const struct ofport *ofport_, + struct netdev_custom_stats *custom_stats) +{ + struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); + int error; + + error = netdev_get_custom_stats(ofport->up.netdev, custom_stats); + + return error; +} + +static int port_get_lacp_stats(const struct ofport *ofport_, struct lacp_slave_stats *stats) { struct ofport_dpif *ofport = ofport_dpif_cast(ofport_); @@ -5785,6 +5797,7 @@ const struct ofproto_class ofproto_dpif_class = { port_del, port_set_config, port_get_stats, + port_get_custom_stats, port_dump_start, port_dump_next, port_dump_done, diff --git a/ofproto/ofproto-provider.h b/ofproto/ofproto-provider.h index 9dc73c4..55e772e 100644 --- a/ofproto/ofproto-provider.h +++ b/ofproto/ofproto-provider.h @@ -1049,6 +1049,10 @@ struct ofproto_class { int (*port_get_stats)(const struct ofport *port, struct netdev_stats *stats); + /* Get port custom stats */ + int (*port_get_custom_stats)(const struct ofport *port, + struct netdev_custom_stats *custom_stats); + /* Port iteration functions. * * The client might not be entirely in control of the ports within an diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c index 82c2bb2..c76a9a1 100644 --- a/ofproto/ofproto.c +++ b/ofproto/ofproto.c @@ -2605,6 +2605,24 @@ ofproto_port_get_stats(const struct ofport *port, struct netdev_stats *stats) return error; } +int +ofproto_port_get_custom_stats(const struct ofport *port, + struct netdev_custom_stats *custom_stats) +{ + struct ofproto *ofproto = port->ofproto; + int error; + + if (ofproto->ofproto_class->port_get_custom_stats) { + memset(custom_stats, 0, sizeof *custom_stats); + error = ofproto->ofproto_class->port_get_custom_stats(port, + custom_stats); + } else { + error = EOPNOTSUPP; + } + + return error; +} + static int update_port(struct ofproto *ofproto, const char *name) { @@ -3887,8 +3905,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); + ofproto_port_get_custom_stats(port, &ops.custom_stats); ofputil_append_port_stat(replies, &ops); + + netdev_free_custom_stats_counters(&ops.custom_stats); } static void diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h index 1e48e19..15e478d 100644 --- a/ofproto/ofproto.h +++ b/ofproto/ofproto.h @@ -48,6 +48,7 @@ struct shash; struct simap; struct smap; struct netdev_stats; +struct netdev_custom_stats; struct ovs_list; struct lldp_status; struct aa_settings; @@ -297,6 +298,8 @@ int ofproto_port_del(struct ofproto *, ofp_port_t ofp_port); void ofproto_port_set_config(struct ofproto *, ofp_port_t ofp_port, const struct smap *cfg); int ofproto_port_get_stats(const struct ofport *, struct netdev_stats *stats); +int ofproto_port_get_custom_stats(const struct ofport *port, + struct netdev_custom_stats *custom_stats); int ofproto_port_query_by_name(const struct ofproto *, const char *devname, struct ofproto_port *); diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c index 630c6fa..1856908 100644 --- a/vswitchd/bridge.c +++ b/vswitchd/bridge.c @@ -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; + uint32_t i; + int64_t *custom_values = NULL; + char **custom_keys = NULL; + #define IFACE_STATS \ IFACE_STAT(rx_packets, "rx_packets") \ IFACE_STAT(tx_packets, "tx_packets") \ @@ -2413,6 +2418,25 @@ iface_refresh_stats(struct iface *iface) ovsrec_interface_set_statistics(iface->cfg, keys, values, n); #undef IFACE_STATS + + netdev_get_custom_stats(iface->netdev, &custom_stats); + + if (custom_stats.size && custom_stats.counters) { + custom_values = xmalloc(custom_stats.size * sizeof *custom_values); + custom_keys = xmalloc(custom_stats.size * sizeof(char *)); + if (custom_values && custom_keys) { + for (i = 0 ; i < custom_stats.size ; i++) { + custom_values[i] = custom_stats.counters[i].value; + custom_keys[i] = custom_stats.counters[i].name; + } + ovsrec_interface_set_statistics(iface->cfg, + (const char **)custom_keys, + custom_values, custom_stats.size); + free(custom_values); + free(custom_keys); + } + netdev_free_custom_stats_counters(&custom_stats); + } } static void