[ovs-dev,RFC] ofproto: RFC extended statistics patch
diff mbox

Message ID 1444394973-13395-1-git-send-email-michalx.weglicki@intel.com
State RFC
Headers show

Commit Message

Weglicki, MichalX Oct. 9, 2015, 12:49 p.m. UTC
Implementation of new statistics extension:
- new counters definition based on RFC2819,
- new command line option for ovs-ofctl: port-dump-ext,
- new message definition has been created in nicira-ext.h,
- new raw types OFPRAW_NXST_PORT_REQUEST, OFPRAW_NXST_PORT_REPLY
  have been created.
- new extended statistics calculation is implemented only for
  dpdk-vhost-enabled ports.

Please note that this is just feature proposal, final patch will include
all relevant counters based on RFC2863, RFC3635 and RFC2819 in
nx_port_stats (defined in nicira-ext.h).

Signed-off-by: Michal Weglicki <michalx.weglicki@intel.com>
Signed-off-by: Timo Puha <timox.puha@intel.com>
---
 include/openflow/nicira-ext.h |  29 ++++++++
 lib/netdev-bsd.c              |  14 ++++
 lib/netdev-dpdk.c             |  44 +++++++++--
 lib/netdev-dummy.c            |  14 +++-
 lib/netdev-linux.c            |  19 +++++
 lib/netdev-vport.c            |  13 ++++
 lib/netdev.h                  |   8 ++
 lib/ofp-msgs.h                |  12 ++-
 lib/ofp-print.c               |  60 ++++++++++-----
 lib/ofp-util.c                | 166 ++++++++++++++++++++++++++++++++----------
 lib/ofp-util.h                |   2 +
 utilities/ovs-ofctl.c         |  69 ++++++++++++++----
 vswitchd/bridge.c             |  30 +++++---
 13 files changed, 386 insertions(+), 94 deletions(-)

Comments

Ben Pfaff Oct. 9, 2015, 2:05 p.m. UTC | #1
On Fri, Oct 09, 2015 at 01:49:33PM +0100, mweglicx wrote:
> Implementation of new statistics extension:
> - new counters definition based on RFC2819,
> - new command line option for ovs-ofctl: port-dump-ext,
> - new message definition has been created in nicira-ext.h,
> - new raw types OFPRAW_NXST_PORT_REQUEST, OFPRAW_NXST_PORT_REPLY
>   have been created.
> - new extended statistics calculation is implemented only for
>   dpdk-vhost-enabled ports.
> 
> Please note that this is just feature proposal, final patch will include
> all relevant counters based on RFC2863, RFC3635 and RFC2819 in
> nx_port_stats (defined in nicira-ext.h).
> 
> Signed-off-by: Michal Weglicki <michalx.weglicki@intel.com>
> Signed-off-by: Timo Puha <timox.puha@intel.com>

Wouldn't it make more sense to use the OF1.5 extensible port statistics
feature?
Weglicki, MichalX Oct. 12, 2015, 9:04 a.m. UTC | #2
Hello, 

"Wouldn't it make more sense to use the OF1.5 extensible port statistics
feature?"
[MW] To be honest I double checked the OF1.5 documentation, and did you mean "Extensible Flow Statistics - OXS"? 
At least I can't find anything about Extensible port statistics. I've also checked the code, however I can't find 
any generic way to implement it without protocol extension. If you meant, that this could be achieved by creating flow
statistics extension, this is not exactly what my patch implements, which is port-based statistics. 

Could you point me to any exact code/documentation if none of above answers are valid? 

Br, 
Michal.

-----Original Message-----
From: Ben Pfaff [mailto:blp@nicira.com] 
Sent: Friday, October 9, 2015 3:06 PM
To: Weglicki, MichalX
Cc: dev@openvswitch.org
Subject: Re: [ovs-dev] [PATCH RFC] ofproto: RFC extended statistics patch

On Fri, Oct 09, 2015 at 01:49:33PM +0100, mweglicx wrote:
> Implementation of new statistics extension:
> - new counters definition based on RFC2819,
> - new command line option for ovs-ofctl: port-dump-ext,
> - new message definition has been created in nicira-ext.h,
> - new raw types OFPRAW_NXST_PORT_REQUEST, OFPRAW_NXST_PORT_REPLY
>   have been created.
> - new extended statistics calculation is implemented only for
>   dpdk-vhost-enabled ports.
> 
> Please note that this is just feature proposal, final patch will include
> all relevant counters based on RFC2863, RFC3635 and RFC2819 in
> nx_port_stats (defined in nicira-ext.h).
> 
> Signed-off-by: Michal Weglicki <michalx.weglicki@intel.com>
> Signed-off-by: Timo Puha <timox.puha@intel.com>

Wouldn't it make more sense to use the OF1.5 extensible port statistics
feature?
--------------------------------------------------------------
Intel Shannon Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263
Business address: Dromore House, East Park, Shannon, Co. Clare

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.
Ben Pfaff Oct. 12, 2015, 3:13 p.m. UTC | #3
On Mon, Oct 12, 2015 at 09:04:54AM +0000, Weglicki, MichalX wrote:
> Hello, 
> 
> "Wouldn't it make more sense to use the OF1.5 extensible port statistics
> feature?"
> [MW] To be honest I double checked the OF1.5 documentation, and did you mean "Extensible Flow Statistics - OXS"? 
> At least I can't find anything about Extensible port statistics. I've also checked the code, however I can't find 
> any generic way to implement it without protocol extension. If you meant, that this could be achieved by creating flow
> statistics extension, this is not exactly what my patch implements, which is port-based statistics. 
> 
> Could you point me to any exact code/documentation if none of above answers are valid? 

Please see section 7.3.5.5, "Port Statistics", in OpenFlow 1.5.
Weglicki, MichalX Oct. 13, 2015, 12:25 p.m. UTC | #4
Hello, 

I did read this chapter quite carefully, however I have some additional questions. 

Did you mean, that I should use experimenter property type to send extended statistics? 

Because it seems that best option would be to add for example e.g. OFPPSPT_ RFC2819 to ofp_port_stats_prop_type along with proper C structures. However this would change OpenFlow protocol, so I guess I can't do it. 

So in simple words: 
Should I implement it exactly how it is currently done for ofputil_append_ofp14_port_stats (OF1.4) which contains Port statistics + ofp_port_stats_prop_ethernet, and append RFC2819 statistics at the end using Experimenter property type? 

Thank you in advance. 

Br, 
Michal.

-----Original Message-----
From: Ben Pfaff [mailto:blp@nicira.com] 
Sent: Monday, October 12, 2015 4:13 PM
To: Weglicki, MichalX
Cc: dev@openvswitch.org
Subject: Re: [ovs-dev] [PATCH RFC] ofproto: RFC extended statistics patch

On Mon, Oct 12, 2015 at 09:04:54AM +0000, Weglicki, MichalX wrote:
> Hello, 
> 
> "Wouldn't it make more sense to use the OF1.5 extensible port statistics
> feature?"
> [MW] To be honest I double checked the OF1.5 documentation, and did you mean "Extensible Flow Statistics - OXS"? 
> At least I can't find anything about Extensible port statistics. I've also checked the code, however I can't find 
> any generic way to implement it without protocol extension. If you meant, that this could be achieved by creating flow
> statistics extension, this is not exactly what my patch implements, which is port-based statistics. 
> 
> Could you point me to any exact code/documentation if none of above answers are valid? 

Please see section 7.3.5.5, "Port Statistics", in OpenFlow 1.5.
--------------------------------------------------------------
Intel Shannon Limited
Registered in Ireland
Registered Office: Collinstown Industrial Park, Leixlip, County Kildare
Registered Number: 308263
Business address: Dromore House, East Park, Shannon, Co. Clare

This e-mail and any attachments may contain confidential material for the sole use of the intended recipient(s). Any review or distribution by others is strictly prohibited. If you are not the intended recipient, please contact the sender and delete all copies.
Ben Pfaff Oct. 13, 2015, 2:38 p.m. UTC | #5
On Tue, Oct 13, 2015 at 12:25:26PM +0000, Weglicki, MichalX wrote:
> Did you mean, that I should use experimenter property type to send
> extended statistics?

Yes.

> Because it seems that best option would be to add for example e.g. OFPPSPT_ RFC2819 to ofp_port_stats_prop_type along with proper C structures. However this would change OpenFlow protocol, so I guess I can't do it. 
> 
> So in simple words: 
> Should I implement it exactly how it is currently done for ofputil_append_ofp14_port_stats (OF1.4) which contains Port statistics + ofp_port_stats_prop_ethernet, and append RFC2819 statistics at the end using Experimenter property type? 

Yes.

Patch
diff mbox

diff --git a/include/openflow/nicira-ext.h b/include/openflow/nicira-ext.h
index efb40fa..1cf45cb 100644
--- a/include/openflow/nicira-ext.h
+++ b/include/openflow/nicira-ext.h
@@ -576,6 +576,35 @@  struct nx_flow_removed {
 };
 OFP_ASSERT(sizeof(struct nx_flow_removed) == 40);
 
+/* Nicira vendor stats request of type NXST_PORT (analogous to OFPST_PORT
+ * request). */
+struct nx_port_stats_request {
+    ovs_be32 port_no;           /* OFPST_PORT message must request statistics
+                                 * either for a single port (specified in
+                                 * port_no) or for all ports (if port_no ==
+                                 * OFPP_ANY). */
+    uint8_t pad[4];             /* Align to 64-bits. */
+};
+
+OFP_ASSERT(sizeof (struct nx_port_stats_request) == 8);
+
+/* Body of reply to NXST_PORT request (analogous to OFPST_PORT reply).
+ * If a counter is unsupported, set the field to all ones. */
+struct nx_port_stats {
+    ovs_be32 port_no;
+    uint8_t pad[4];             /* Align to 64-bits. */
+    /* Below counters are just example used in RFC patch based on RFC2819.
+     * Final statistics extension should contain more counters based on
+     * RFC2819, RFC2863 and RFC3635. */
+    ovs_be64 rx_64_packets;
+    ovs_be64 rx_65_to_127_packets;
+    ovs_be64 rx_128_to_255_packets;
+    ovs_be64 rx_256_to_511_packets;
+    ovs_be64 rx_512_to_1023_packets;
+    ovs_be64 rx_1024_to_1518_packets;
+};
+OFP_ASSERT(sizeof(struct nx_port_stats) == 56);
+
 /* Nicira vendor stats request of type NXST_FLOW (analogous to OFPST_FLOW
  * request).
  *
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index 60e5615..153687a 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -896,6 +896,18 @@  netdev_bsd_get_carrier(const struct netdev *netdev_, bool *carrier)
     return error;
 }
 
+/* Initializes unused statistics to default values. */
+static void
+netdev_bsd_set_unused_stats(struct netdev_stats *dst)
+{
+    dst->rx_1024_to_1518_packets = UINT64_MAX;
+    dst->rx_512_to_1023_packets = UINT64_MAX;
+    dst->rx_256_to_511_packets = UINT64_MAX;
+    dst->rx_128_to_255_packets = UINT64_MAX;
+    dst->rx_65_to_127_packets = UINT64_MAX;
+    dst->rx_64_packets = UINT64_MAX;
+}
+
 static void
 convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd)
 {
@@ -923,6 +935,7 @@  convert_stats_system(struct netdev_stats *stats, const struct if_data *ifd)
     stats->tx_fifo_errors = UINT64_MAX;
     stats->tx_heartbeat_errors = UINT64_MAX;
     stats->tx_window_errors = UINT64_MAX;
+    netdev_bsd_set_unused_stats(stats);
 }
 
 static void
@@ -957,6 +970,7 @@  convert_stats_tap(struct netdev_stats *stats, const struct if_data *ifd)
     stats->tx_fifo_errors = UINT64_MAX;
     stats->tx_heartbeat_errors = UINT64_MAX;
     stats->tx_window_errors = UINT64_MAX;
+    netdev_bsd_set_unused_stats(stats);
 }
 
 static void
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index 93b0589..2b4f07e 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -950,13 +950,15 @@  netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
                                      struct dp_packet **packets, int count)
 {
     int i;
+    unsigned size;
     struct dp_packet *packet;
 
     stats->rx_packets += count;
     for (i = 0; i < count; i++) {
         packet = packets[i];
+        size = dp_packet_size(packet);
 
-        if (OVS_UNLIKELY(dp_packet_size(packet) < ETH_HEADER_LEN)) {
+        if (OVS_UNLIKELY(size < ETH_HEADER_LEN)) {
             /* This only protects the following multicast counting from
              * too short packets, but it does not stop the packet from
              * further processing. */
@@ -965,12 +967,32 @@  netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
             continue;
         }
 
+        /* Hard-coded binary search for the size bucket. */
+        if (size < 256) {
+            if (size >= 128) {
+                stats->rx_128_to_255_packets++;
+            } else if (size <= 64) {
+                stats->rx_64_packets++;
+            } else {
+                stats->rx_65_to_127_packets++;
+            }
+        } else {
+            if (size >= 1024) {
+                stats->rx_1024_to_1518_packets++;
+            } else if (size < 512) {
+                stats->rx_256_to_511_packets++;
+            } else {
+                stats->rx_512_to_1023_packets++;
+            }
+        }
+
         struct eth_header *eh = (struct eth_header *) dp_packet_data(packet);
+
         if (OVS_UNLIKELY(eth_addr_is_multicast(eh->eth_dst))) {
             stats->multicast++;
         }
 
-        stats->rx_bytes += dp_packet_size(packet);
+        stats->rx_bytes += size;
     }
 }
 
@@ -1412,7 +1434,6 @@  netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
     struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);
 
     ovs_mutex_lock(&dev->mutex);
-    memset(stats, 0, sizeof(*stats));
     /* Unsupported Stats */
     stats->collisions = UINT64_MAX;
     stats->rx_crc_errors = UINT64_MAX;
@@ -1438,6 +1459,14 @@  netdev_dpdk_vhost_get_stats(const struct netdev *netdev,
     stats->tx_bytes = dev->stats.tx_bytes;
     stats->rx_errors = dev->stats.rx_errors;
     stats->rx_length_errors = dev->stats.rx_length_errors;
+
+    stats->rx_64_packets = dev->stats.rx_64_packets;
+    stats->rx_65_to_127_packets = dev->stats.rx_65_to_127_packets;
+    stats->rx_128_to_255_packets = dev->stats.rx_128_to_255_packets;
+    stats->rx_256_to_511_packets = dev->stats.rx_256_to_511_packets;
+    stats->rx_512_to_1023_packets = dev->stats.rx_512_to_1023_packets;
+    stats->rx_1024_to_1518_packets = dev->stats.rx_1024_to_1518_packets;
+
     rte_spinlock_unlock(&dev->stats_lock);
 
     ovs_mutex_unlock(&dev->mutex);
@@ -1456,8 +1485,6 @@  netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
     ovs_mutex_lock(&dev->mutex);
     rte_eth_stats_get(dev->port_id, &rte_stats);
 
-    memset(stats, 0, sizeof(*stats));
-
     stats->rx_packets = rte_stats.ipackets;
     stats->tx_packets = rte_stats.opackets;
     stats->rx_bytes = rte_stats.ibytes;
@@ -1491,6 +1518,13 @@  netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 
     ovs_mutex_unlock(&dev->mutex);
 
+    stats->rx_64_packets = UINT64_MAX;
+    stats->rx_65_to_127_packets = UINT64_MAX;
+    stats->rx_128_to_255_packets = UINT64_MAX;
+    stats->rx_256_to_511_packets = UINT64_MAX;
+    stats->rx_512_to_1023_packets = UINT64_MAX;
+    stats->rx_1024_to_1518_packets = UINT64_MAX;
+
     return 0;
 }
 
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index 76815c2..b055abf 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -1020,6 +1020,18 @@  netdev_dummy_set_mtu(const struct netdev *netdev, int mtu)
     return 0;
 }
 
+/* Initializes unused statistics to default values. */
+static void
+netdev_dummy_set_unused_stats(struct netdev_stats *dst)
+{
+    dst->rx_1024_to_1518_packets = UINT64_MAX;
+    dst->rx_512_to_1023_packets = UINT64_MAX;
+    dst->rx_256_to_511_packets = UINT64_MAX;
+    dst->rx_128_to_255_packets = UINT64_MAX;
+    dst->rx_65_to_127_packets = UINT64_MAX;
+    dst->rx_64_packets = UINT64_MAX;
+}
+
 static int
 netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -1028,7 +1040,7 @@  netdev_dummy_get_stats(const struct netdev *netdev, struct netdev_stats *stats)
     ovs_mutex_lock(&dev->mutex);
     *stats = dev->stats;
     ovs_mutex_unlock(&dev->mutex);
-
+    netdev_dummy_set_unused_stats(stats);
     return 0;
 }
 
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index 584e804..d892517 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1550,6 +1550,18 @@  swap_uint64(uint64_t *a, uint64_t *b)
     *b = tmp;
 }
 
+/* Initializes unused statistics to default values. */
+static void
+netdev_linux_set_unused_stats(struct netdev_stats *dst)
+{
+    dst->rx_1024_to_1518_packets = UINT64_MAX;
+    dst->rx_512_to_1023_packets = UINT64_MAX;
+    dst->rx_256_to_511_packets = UINT64_MAX;
+    dst->rx_128_to_255_packets = UINT64_MAX;
+    dst->rx_65_to_127_packets = UINT64_MAX;
+    dst->rx_64_packets = UINT64_MAX;
+}
+
 /* Copies 'src' into 'dst', performing format conversion in the process.
  *
  * 'src' is allowed to be misaligned. */
@@ -1580,6 +1592,7 @@  netdev_stats_from_ovs_vport_stats(struct netdev_stats *dst,
     dst->tx_window_errors = 0;
 }
 
+
 static int
 get_stats_via_vport__(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -1671,6 +1684,8 @@  netdev_linux_get_stats(const struct netdev *netdev_,
     }
     ovs_mutex_unlock(&netdev->mutex);
 
+    netdev_linux_set_unused_stats(stats);
+
     return error;
 }
 
@@ -1733,6 +1748,8 @@  netdev_tap_get_stats(const struct netdev *netdev_, struct netdev_stats *stats)
     }
     ovs_mutex_unlock(&netdev->mutex);
 
+    netdev_linux_set_unused_stats(stats);
+
     return error;
 }
 
@@ -1748,6 +1765,8 @@  netdev_internal_get_stats(const struct netdev *netdev_,
     error = netdev->vport_stats_error;
     ovs_mutex_unlock(&netdev->mutex);
 
+    netdev_linux_set_unused_stats(stats);
+
     return error;
 }
 
diff --git a/lib/netdev-vport.c b/lib/netdev-vport.c
index ff50563..c1ecad1 100644
--- a/lib/netdev-vport.c
+++ b/lib/netdev-vport.c
@@ -812,6 +812,18 @@  set_patch_config(struct netdev *dev_, const struct smap *args)
     return 0;
 }
 
+/* Initializes unused statistics to default values. */
+static void
+netdev_vport_set_unused_stats(struct netdev_stats *dst)
+{
+    dst->rx_1024_to_1518_packets = UINT64_MAX;
+    dst->rx_512_to_1023_packets = UINT64_MAX;
+    dst->rx_256_to_511_packets = UINT64_MAX;
+    dst->rx_128_to_255_packets = UINT64_MAX;
+    dst->rx_65_to_127_packets = UINT64_MAX;
+    dst->rx_64_packets = UINT64_MAX;
+}
+
 static int
 get_stats(const struct netdev *netdev, struct netdev_stats *stats)
 {
@@ -820,6 +832,7 @@  get_stats(const struct netdev *netdev, struct netdev_stats *stats)
     ovs_mutex_lock(&dev->mutex);
     *stats = dev->stats;
     ovs_mutex_unlock(&dev->mutex);
+    netdev_vport_set_unused_stats(stats);
 
     return 0;
 }
diff --git a/lib/netdev.h b/lib/netdev.h
index 0fbcb65..107e81e 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -102,6 +102,14 @@  struct netdev_stats {
     uint64_t tx_fifo_errors;
     uint64_t tx_heartbeat_errors;
     uint64_t tx_window_errors;
+
+    /* Size bucket statistics. Based on RFC2819. */
+    uint64_t rx_64_packets;
+    uint64_t rx_65_to_127_packets;
+    uint64_t rx_128_to_255_packets;
+    uint64_t rx_256_to_511_packets;
+    uint64_t rx_512_to_1023_packets;
+    uint64_t rx_1024_to_1518_packets;
 };
 
 /* Configuration specific to tunnels. */
diff --git a/lib/ofp-msgs.h b/lib/ofp-msgs.h
index bce5283..4335ccd 100644
--- a/lib/ofp-msgs.h
+++ b/lib/ofp-msgs.h
@@ -443,6 +443,12 @@  enum ofpraw {
 
     /* NXT 1.0+ (26): struct nx_geneve_table_reply, struct nx_geneve_map[]. */
     OFPRAW_NXT_GENEVE_TABLE_REPLY,
+
+    /* NXST 1.0+ (3): struct nx_port_stats_request. */
+    OFPRAW_NXST_PORT_REQUEST,
+
+    /* NXST 1.0+ (3): struct nx_port_stats[]. */
+    OFPRAW_NXST_PORT_REPLY
 };
 
 /* Decoding messages into OFPRAW_* values. */
@@ -590,11 +596,13 @@  enum ofptype {
                                       * OFPRAW_OFPST12_TABLE_REPLY.
                                       * OFPRAW_OFPST13_TABLE_REPLY. */
     OFPTYPE_PORT_STATS_REQUEST,      /* OFPRAW_OFPST10_PORT_REQUEST.
-                                      * OFPRAW_OFPST11_PORT_REQUEST. */
+                                      * OFPRAW_OFPST11_PORT_REQUEST.
+                                      * OFPRAW_NXST_PORT_REQUEST. */
     OFPTYPE_PORT_STATS_REPLY,        /* OFPRAW_OFPST10_PORT_REPLY.
                                       * OFPRAW_OFPST11_PORT_REPLY.
                                       * OFPRAW_OFPST13_PORT_REPLY.
-                                      * OFPRAW_OFPST14_PORT_REPLY. */
+                                      * OFPRAW_OFPST14_PORT_REPLY.
+                                      * OFPRAW_NXST_PORT_REPLY */
     OFPTYPE_QUEUE_STATS_REQUEST,     /* OFPRAW_OFPST10_QUEUE_REQUEST.
                                       * OFPRAW_OFPST11_QUEUE_REQUEST. */
     OFPTYPE_QUEUE_STATS_REPLY,       /* OFPRAW_OFPST10_QUEUE_REPLY.
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index d0c94ce..c7863e5 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -1565,6 +1565,7 @@  ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
                            int verbosity)
 {
     struct ofpbuf b;
+    enum ofpraw raw;
 
     ds_put_format(string, " %"PRIuSIZE" ports\n", ofputil_count_port_stats(oh));
     if (verbosity < 1) {
@@ -1590,26 +1591,45 @@  ofp_print_ofpst_port_reply(struct ds *string, const struct ofp_header *oh,
         }
         ofputil_format_port(ps.port_no, string);
 
-        ds_put_cstr(string, ": rx ");
-        print_port_stat(string, "pkts=", ps.stats.rx_packets, 1);
-        print_port_stat(string, "bytes=", ps.stats.rx_bytes, 1);
-        print_port_stat(string, "drop=", ps.stats.rx_dropped, 1);
-        print_port_stat(string, "errs=", ps.stats.rx_errors, 1);
-        print_port_stat(string, "frame=", ps.stats.rx_frame_errors, 1);
-        print_port_stat(string, "over=", ps.stats.rx_over_errors, 1);
-        print_port_stat(string, "crc=", ps.stats.rx_crc_errors, 0);
-
-        ds_put_cstr(string, "           tx ");
-        print_port_stat(string, "pkts=", ps.stats.tx_packets, 1);
-        print_port_stat(string, "bytes=", ps.stats.tx_bytes, 1);
-        print_port_stat(string, "drop=", ps.stats.tx_dropped, 1);
-        print_port_stat(string, "errs=", ps.stats.tx_errors, 1);
-        print_port_stat(string, "coll=", ps.stats.collisions, 0);
-
-        if (ps.duration_sec != UINT32_MAX) {
-            ds_put_cstr(string, "           duration=");
-            ofp_print_duration(string, ps.duration_sec, ps.duration_nsec);
-            ds_put_char(string, '\n');
+        if (!ofpraw_decode(&raw, b.header)) {
+            if (raw != OFPRAW_NXST_PORT_REPLY) {
+                ds_put_cstr(string, ": rx ");
+                print_port_stat(string, "pkts=", ps.stats.rx_packets, 1);
+                print_port_stat(string, "bytes=", ps.stats.rx_bytes, 1);
+                print_port_stat(string, "drop=", ps.stats.rx_dropped, 1);
+                print_port_stat(string, "errs=", ps.stats.rx_errors, 1);
+                print_port_stat(string, "frame=", ps.stats.rx_frame_errors, 1);
+                print_port_stat(string, "over=", ps.stats.rx_over_errors, 1);
+                print_port_stat(string, "crc=", ps.stats.rx_crc_errors, 0);
+
+                ds_put_cstr(string, "           tx ");
+                print_port_stat(string, "pkts=", ps.stats.tx_packets, 1);
+                print_port_stat(string, "bytes=", ps.stats.tx_bytes, 1);
+                print_port_stat(string, "drop=", ps.stats.tx_dropped, 1);
+                print_port_stat(string, "errs=", ps.stats.tx_errors, 1);
+                print_port_stat(string, "coll=", ps.stats.collisions, 0);
+
+                if (ps.duration_sec != UINT32_MAX) {
+                    ds_put_cstr(string, "           duration=");
+                    ofp_print_duration(string, ps.duration_sec,
+                                       ps.duration_nsec);
+                    ds_put_char(string, '\n');
+                }
+            } else {
+                ds_put_cstr(string, ": rx ");
+                print_port_stat(string, "64_packets=", ps.stats.rx_64_packets,
+                                1);
+                print_port_stat(string, "65_to_127_packets=",
+                                ps.stats.rx_65_to_127_packets, 1);
+                print_port_stat(string, "128_to_255_packets=",
+                                ps.stats.rx_128_to_255_packets, 1);
+                print_port_stat(string, "256_to_511_packets=",
+                                ps.stats.rx_256_to_511_packets, 1);
+                print_port_stat(string, "512_to_1023_packets=",
+                                ps.stats.rx_512_to_1023_packets, 1);
+                print_port_stat(string, "1024_to_1518_packets=",
+                                ps.stats.rx_1024_to_1518_packets, 0);
+            }
         }
     }
 }
diff --git a/lib/ofp-util.c b/lib/ofp-util.c
index b9dbcda..cae432a 100644
--- a/lib/ofp-util.c
+++ b/lib/ofp-util.c
@@ -6798,6 +6798,25 @@  ofputil_encode_dump_ports_request(enum ofp_version ofp_version, ofp_port_t port)
     return request;
 }
 
+/* Encode a dump ports request for 'port', the encoded message
+ * will be for OpenFlow version 'ofp_version'. Returns message
+ * as a struct ofpbuf. Returns encoded message on success, NULL on error */
+struct ofpbuf *
+ofputil_encode_dump_ports_ext_request(enum ofp_version ofp_version,
+                                      ofp_port_t port)
+{
+    struct ofpbuf *request;
+    struct nx_port_stats_request *npsr;
+
+    request =
+        ofpraw_alloc(OFPRAW_NXST_PORT_REQUEST, ofp_version, NXM_TYPICAL_LEN);
+    ofpbuf_put_zeros(request, sizeof *npsr);
+    npsr = request->msg;
+    npsr->port_no = htons(ofp_to_u16(port));
+
+    return request;
+}
+
 static void
 ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops,
                             struct ofp10_port_stats *ps10)
@@ -6819,6 +6838,20 @@  ofputil_port_stats_to_ofp10(const struct ofputil_port_stats *ops,
 }
 
 static void
+ofputil_port_stats_to_nx10(const struct ofputil_port_stats *ops,
+                            struct nx_port_stats *nx10stats)
+{
+    nx10stats->port_no = htons(ofp_to_u16(ops->port_no));
+    memset(nx10stats->pad, 0, sizeof nx10stats->pad);
+    nx10stats->rx_64_packets = htonll(ops->stats.rx_64_packets);
+    nx10stats->rx_65_to_127_packets = htonll(ops->stats.rx_65_to_127_packets);
+    nx10stats->rx_128_to_255_packets = htonll(ops->stats.rx_128_to_255_packets);
+    nx10stats->rx_256_to_511_packets = htonll(ops->stats.rx_256_to_511_packets);
+    nx10stats->rx_512_to_1023_packets = htonll(ops->stats.rx_512_to_1023_packets);
+    nx10stats->rx_1024_to_1518_packets = htonll(ops->stats.rx_1024_to_1518_packets);
+}
+
+static void
 ofputil_port_stats_to_ofp11(const struct ofputil_port_stats *ops,
                             struct ofp11_port_stats *ps11)
 {
@@ -6887,32 +6920,44 @@  void
 ofputil_append_port_stat(struct ovs_list *replies,
                          const struct ofputil_port_stats *ops)
 {
-    switch (ofpmp_version(replies)) {
-    case OFP13_VERSION: {
-        struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply);
-        ofputil_port_stats_to_ofp13(ops, reply);
-        break;
-    }
-    case OFP12_VERSION:
-    case OFP11_VERSION: {
-        struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply);
-        ofputil_port_stats_to_ofp11(ops, reply);
-        break;
-    }
+    enum ofpraw raw = OFPRAW_OFPST10_PORT_REQUEST;
 
-    case OFP10_VERSION: {
-        struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply);
-        ofputil_port_stats_to_ofp10(ops, reply);
-        break;
-    }
+    raw = ofpmp_decode_raw(replies);
 
-    case OFP14_VERSION:
-    case OFP15_VERSION:
-        ofputil_append_ofp14_port_stats(ops, replies);
-        break;
+    if (OFPRAW_NXST_PORT_REPLY == raw) {
+        struct nx_port_stats *reply;
 
-    default:
-        OVS_NOT_REACHED();
+        reply = ofpmp_append(replies, sizeof *reply);
+        ofputil_port_stats_to_nx10(ops, reply);
+    } else {
+
+        switch (ofpmp_version(replies)) {
+        case OFP13_VERSION: {
+            struct ofp13_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+            ofputil_port_stats_to_ofp13(ops, reply);
+            break;
+        }
+        case OFP12_VERSION:
+        case OFP11_VERSION: {
+            struct ofp11_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+            ofputil_port_stats_to_ofp11(ops, reply);
+            break;
+        }
+
+        case OFP10_VERSION: {
+            struct ofp10_port_stats *reply = ofpmp_append(replies, sizeof *reply);
+            ofputil_port_stats_to_ofp10(ops, reply);
+            break;
+        }
+
+        case OFP14_VERSION:
+        case OFP15_VERSION:
+            ofputil_append_ofp14_port_stats(ops, replies);
+            break;
+
+        default:
+            OVS_NOT_REACHED();
+        }
     }
 }
 
@@ -6942,6 +6987,27 @@  ofputil_port_stats_from_ofp10(struct ofputil_port_stats *ops,
 }
 
 static enum ofperr
+ofputil_port_stats_from_nx10(struct ofputil_port_stats *ops,
+                             const struct nx_port_stats *nx10stats)
+{
+    memset(ops, 0, sizeof *ops);
+
+    ops->port_no = u16_to_ofp(ntohs(nx10stats->port_no));
+    ops->stats.rx_64_packets = ntohll(nx10stats->rx_64_packets);
+    ops->stats.rx_65_to_127_packets = ntohll(nx10stats->rx_65_to_127_packets);
+    ops->stats.rx_128_to_255_packets =
+        ntohll(nx10stats->rx_128_to_255_packets);
+    ops->stats.rx_256_to_511_packets =
+        ntohll(nx10stats->rx_256_to_511_packets);
+    ops->stats.rx_512_to_1023_packets =
+        ntohll(nx10stats->rx_512_to_1023_packets);
+    ops->stats.rx_1024_to_1518_packets =
+        ntohll(nx10stats->rx_1024_to_1518_packets);
+
+    return 0;
+}
+
+static enum ofperr
 ofputil_port_stats_from_ofp11(struct ofputil_port_stats *ops,
                               const struct ofp11_port_stats *ps11)
 {
@@ -7115,13 +7181,13 @@  ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
         return ofputil_pull_ofp14_port_stats(ps, msg);
     } else if (raw == OFPRAW_OFPST13_PORT_REPLY) {
         const struct ofp13_port_stats *ps13;
-
         ps13 = ofpbuf_try_pull(msg, sizeof *ps13);
         if (!ps13) {
             goto bad_len;
         }
         return ofputil_port_stats_from_ofp13(ps, ps13);
     } else if (raw == OFPRAW_OFPST11_PORT_REPLY) {
+
         const struct ofp11_port_stats *ps11;
 
         ps11 = ofpbuf_try_pull(msg, sizeof *ps11);
@@ -7137,6 +7203,15 @@  ofputil_decode_port_stats(struct ofputil_port_stats *ps, struct ofpbuf *msg)
             goto bad_len;
         }
         return ofputil_port_stats_from_ofp10(ps, ps10);
+    } else if (raw == OFPRAW_NXST_PORT_REPLY) {
+        const struct nx_port_stats *nxstats;
+
+        nxstats = ofpbuf_try_pull(msg, sizeof *nxstats);
+        if (!nxstats) {
+            goto bad_len;
+        }
+        return ofputil_port_stats_from_nx10(ps, nxstats);
+
     } else {
         OVS_NOT_REACHED();
     }
@@ -7154,24 +7229,35 @@  enum ofperr
 ofputil_decode_port_stats_request(const struct ofp_header *request,
                                   ofp_port_t *ofp10_port)
 {
-    switch ((enum ofp_version)request->version) {
-    case OFP15_VERSION:
-    case OFP14_VERSION:
-    case OFP13_VERSION:
-    case OFP12_VERSION:
-    case OFP11_VERSION: {
-        const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request);
-        return ofputil_port_from_ofp11(psr11->port_no, ofp10_port);
-    }
+    enum ofpraw raw;
+    struct ofpbuf b;
 
-    case OFP10_VERSION: {
-        const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request);
-        *ofp10_port = u16_to_ofp(ntohs(psr10->port_no));
-        return 0;
-    }
+    ofpbuf_use_const(&b, request, ntohs(request->length));
+    raw = ofpraw_pull_assert(&b);
 
-    default:
-        OVS_NOT_REACHED();
+    if (OFPRAW_NXST_PORT_REQUEST == raw) {
+        const struct nx_port_stats_request *nxpsr = ofpmsg_body(request);
+
+        *ofp10_port = u16_to_ofp(ntohs(nxpsr->port_no));
+        return 0;
+    } else {
+        switch ((enum ofp_version)request->version) {
+        case OFP15_VERSION:
+        case OFP14_VERSION:
+        case OFP13_VERSION:
+        case OFP12_VERSION:
+        case OFP11_VERSION: {
+            const struct ofp11_port_stats_request *psr11 = ofpmsg_body(request);
+            return ofputil_port_from_ofp11(psr11->port_no, ofp10_port);
+        }
+        case OFP10_VERSION: {
+            const struct ofp10_port_stats_request *psr10 = ofpmsg_body(request);
+            *ofp10_port = u16_to_ofp(ntohs(psr10->port_no));
+            return 0;
+        }
+        default:
+            OVS_NOT_REACHED();
+        }
     }
 }
 
diff --git a/lib/ofp-util.h b/lib/ofp-util.h
index 8914342..3211c9e 100644
--- a/lib/ofp-util.h
+++ b/lib/ofp-util.h
@@ -999,6 +999,8 @@  struct ofputil_port_stats {
 
 struct ofpbuf *ofputil_encode_dump_ports_request(enum ofp_version ofp_version,
                                                  ofp_port_t port);
+struct ofpbuf *ofputil_encode_dump_ports_ext_request(enum ofp_version ofp_version,
+                                                 ofp_port_t port);
 void ofputil_append_port_stat(struct ovs_list *replies,
                               const struct ofputil_port_stats *ops);
 size_t ofputil_count_port_stats(const struct ofp_header *);
diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
index 0c315c1..2dd8783 100644
--- a/utilities/ovs-ofctl.c
+++ b/utilities/ovs-ofctl.c
@@ -349,6 +349,7 @@  usage(void)
            "  set-frags SWITCH FRAG_MODE  set fragment handling behavior\n"
            "      FRAG_MODE: normal, drop, reassemble, nx-match\n"
            "  dump-ports SWITCH [PORT]    print port statistics\n"
+           "  dump-ports-ext SWITCH [PORT]    print port extended statistics\n"
            "  dump-ports-desc SWITCH [PORT]  print port descriptions\n"
            "  dump-flows SWITCH           print all flow entries\n"
            "  dump-flows SWITCH FLOW      print matching FLOWs\n"
@@ -1036,12 +1037,28 @@  try_set_protocol(struct vconn *vconn, enum ofputil_protocol want,
     }
 }
 
+static void
+set_protocol_print_error(enum ofputil_protocol usable_protocols)
+{
+    char *usable_s;
+
+    usable_s = ofputil_protocols_to_string(usable_protocols);
+    if (usable_protocols & allowed_protocols) {
+        ovs_fatal(0, "switch does not support any of the usable flow "
+                  "formats (%s)", usable_s);
+    } else {
+        char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
+
+        ovs_fatal(0, "none of the usable flow formats (%s) is among the "
+                  "allowed flow formats (%s)", usable_s, allowed_s);
+    }
+}
+
 static enum ofputil_protocol
 set_protocol_for_flow_dump(struct vconn *vconn,
                            enum ofputil_protocol cur_protocol,
                            enum ofputil_protocol usable_protocols)
 {
-    char *usable_s;
     int i;
 
     for (i = 0; i < ofputil_n_flow_dump_protocols; i++) {
@@ -1052,15 +1069,8 @@  set_protocol_for_flow_dump(struct vconn *vconn,
         }
     }
 
-    usable_s = ofputil_protocols_to_string(usable_protocols);
-    if (usable_protocols & allowed_protocols) {
-        ovs_fatal(0, "switch does not support any of the usable flow "
-                  "formats (%s)", usable_s);
-    } else {
-        char *allowed_s = ofputil_protocols_to_string(allowed_protocols);
-        ovs_fatal(0, "none of the usable flow formats (%s) is among the "
-                  "allowed flow formats (%s)", usable_s, allowed_s);
-    }
+    set_protocol_print_error(usable_protocols);
+    return OFPUTIL_P_NONE;
 }
 
 static struct vconn *
@@ -1782,21 +1792,50 @@  ofctl_snoop(struct ovs_cmdl_context *ctx)
     monitor_vconn(vconn, false);
 }
 
+static struct vconn *
+prepare_dump_ports(const char *name, ofp_port_t port,
+                   struct ofpbuf **requestp, bool extended_stats)
+{
+    struct vconn *vconn;
+
+    open_vconn(name, &vconn);
+    if (extended_stats) {
+        *requestp =
+            ofputil_encode_dump_ports_ext_request(vconn_get_version(vconn),
+                                                  port);
+    } else {
+        *requestp =
+            ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
+    }
+    return vconn;
+}
+
 static void
-ofctl_dump_ports(struct ovs_cmdl_context *ctx)
+ofctl_dump_ports__(int argc, char *argv[], bool extended_stats)
 {
     struct ofpbuf *request;
     struct vconn *vconn;
     ofp_port_t port;
 
-    open_vconn(ctx->argv[1], &vconn);
-    port = ctx->argc > 2 ? str_to_port_no(ctx->argv[1], ctx->argv[2]) : OFPP_ANY;
-    request = ofputil_encode_dump_ports_request(vconn_get_version(vconn), port);
+    port = argc > 2 ? str_to_port_no(argv[1], argv[2]) : OFPP_ANY;
+    vconn = prepare_dump_ports(argv[1], port, &request, extended_stats);
     dump_stats_transaction(vconn, request);
     vconn_close(vconn);
 }
 
 static void
+ofctl_dump_ports(struct ovs_cmdl_context *ctx)
+{
+    ofctl_dump_ports__(ctx->argc, ctx->argv, false);
+}
+
+static void
+ofctl_dump_ports_ext(struct ovs_cmdl_context *ctx)
+{
+    ofctl_dump_ports__(ctx->argc, ctx->argv, true);
+}
+
+static void
 ofctl_dump_ports_desc(struct ovs_cmdl_context *ctx)
 {
     struct ofpbuf *request;
@@ -3738,6 +3777,8 @@  static const struct ovs_cmdl_command all_commands[] = {
       4, INT_MAX, ofctl_packet_out },
     { "dump-ports", "switch [port]",
       1, 2, ofctl_dump_ports },
+    { "dump-ports-ext", "switch [port]",
+      1, 2, ofctl_dump_ports_ext },
     { "dump-ports-desc", "switch [port]",
       1, 2, ofctl_dump_ports_desc },
     { "mod-port", "switch iface act",
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 232a334..714c617 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -2335,18 +2335,24 @@  static void
 iface_refresh_stats(struct iface *iface)
 {
 #define IFACE_STATS                             \
-    IFACE_STAT(rx_packets,      "rx_packets")   \
-    IFACE_STAT(tx_packets,      "tx_packets")   \
-    IFACE_STAT(rx_bytes,        "rx_bytes")     \
-    IFACE_STAT(tx_bytes,        "tx_bytes")     \
-    IFACE_STAT(rx_dropped,      "rx_dropped")   \
-    IFACE_STAT(tx_dropped,      "tx_dropped")   \
-    IFACE_STAT(rx_errors,       "rx_errors")    \
-    IFACE_STAT(tx_errors,       "tx_errors")    \
-    IFACE_STAT(rx_frame_errors, "rx_frame_err") \
-    IFACE_STAT(rx_over_errors,  "rx_over_err")  \
-    IFACE_STAT(rx_crc_errors,   "rx_crc_err")   \
-    IFACE_STAT(collisions,      "collisions")
+    IFACE_STAT(rx_packets,              "rx_packets")               \
+    IFACE_STAT(tx_packets,              "tx_packets")               \
+    IFACE_STAT(rx_bytes,                "rx_bytes")                 \
+    IFACE_STAT(tx_bytes,                "tx_bytes")                 \
+    IFACE_STAT(rx_dropped,              "rx_dropped")               \
+    IFACE_STAT(tx_dropped,              "tx_dropped")               \
+    IFACE_STAT(rx_errors,               "rx_errors")                \
+    IFACE_STAT(tx_errors,               "tx_errors")                \
+    IFACE_STAT(rx_frame_errors,         "rx_frame_err")             \
+    IFACE_STAT(rx_over_errors,          "rx_over_err")              \
+    IFACE_STAT(rx_crc_errors,           "rx_crc_err")               \
+    IFACE_STAT(collisions,              "collisions")               \
+    IFACE_STAT(rx_64_packets,           "rx_64_packets")            \
+    IFACE_STAT(rx_65_to_127_packets,    "rx_65_to_127_packets")     \
+    IFACE_STAT(rx_128_to_255_packets,   "rx_128_to_255_packets")    \
+    IFACE_STAT(rx_256_to_511_packets,   "rx_256_to_511_packets")    \
+    IFACE_STAT(rx_512_to_1023_packets,  "rx_512_to_1023_packets")   \
+    IFACE_STAT(rx_1024_to_1518_packets, "rx_1024_to_1518_packets")
 
 #define IFACE_STAT(MEMBER, NAME) + 1
     enum { N_IFACE_STATS = IFACE_STATS };