Merge branch 'mainstream'
authorGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 26 Sep 2013 08:22:55 +0000 (10:22 +0200)
committerGiuseppe Lettieri <g.lettieri@iet.unipi.it>
Thu, 26 Sep 2013 08:22:55 +0000 (10:22 +0200)
58 files changed:
INSTALL.RHEL
NEWS
OPENFLOW-1.1+
datapath/linux/.gitignore
datapath/linux/compat/gso.c
datapath/linux/compat/include/linux/netdevice.h
datapath/vport-gre.c
datapath/vport-internal_dev.c
datapath/vport-netdev.c
datapath/vport-vxlan.c
datapath/vport.c
debian/control
debian/openvswitch-switch.install
debian/openvswitch-switch.manpages
include/openvswitch/types.h
lib/cfm.c
lib/cfm.h
lib/hmap.c
lib/hmap.h
lib/learn.c
lib/match.c
lib/meta-flow.c
lib/netdev-vport.c
lib/nx-match.c
lib/odp-util.c
lib/ofp-actions.c
lib/ofp-parse.c
lib/ofp-print.c
lib/ofp-util.c
lib/packets.c
lib/packets.h
manpages.mk
ofproto/ofproto-dpif-upcall.c
ofproto/ofproto-dpif-upcall.h
ofproto/ofproto-dpif.c
ofproto/ofproto-provider.h
ofproto/ofproto.c
ofproto/ofproto.h
ofproto/tunnel.c
ovsdb/file.c
ovsdb/ovsdb-tool.c
python/ovs/vlog.py
rhel/openvswitch.spec.in
tests/automake.mk
tests/cfm.at [new file with mode: 0644]
tests/ovsdb-server.at
tests/test-classifier.c
tests/test-util.c
tests/testsuite.at
utilities/.gitignore
utilities/automake.mk
utilities/ovs-dpctl-top.8.in [new file with mode: 0644]
utilities/ovs-dpctl-top.in [new file with mode: 0755]
utilities/ovs-dpctl.c
utilities/ovs-ofctl.8.in
utilities/ovs-ofctl.c
vswitchd/vswitch.xml
xenserver/openvswitch-xen.spec.in

index cbb91de..26b47d9 100644 (file)
@@ -17,12 +17,27 @@ Before you begin, note the RPM source directory on your version of
 RHEL.  On RHEL 5, the default RPM source directory is
 /usr/src/redhat/SOURCES.  On RHEL 6, it is $HOME/rpmbuild/SOURCES.
 
-1. Install build prerequisites:
+1. If you are building from a distribution tarball, proceed to step 2.
+   Otherwise, if you are building from an Open vSwitch Git tree,
+   determine the version of Autoconf available in the RHEL version you
+   are using.  If it is not at least version 2.64, then you have two
+   choices:
+
+     a. Install Autoconf 2.64 or later, one way or another.
+
+     b. Create a distribution tarball on some other machine, by
+        running "./boot.sh; ./configure; make dist" in the Git tree.
+        You must run this on a machine that has the tools listed in
+        INSTALL as prerequisites for building from a Git tree.
+        Afterward, proceed with the rest of the instructions using the
+        distribution tarball.
+
+2. Install build prerequisites:
 
    yum install gcc make python-devel openssl-devel kernel-devel \
-       kernel-debug-devel
+       kernel-debug-devel autoconf automake rpm-build redhat-rpm-config
 
-2. Some versions of the RHEL 6 kernel-devel package contain a broken
+3. Some versions of the RHEL 6 kernel-devel package contain a broken
    "build" symlink.  If you are using such a version, you must fix
    the problem before continuing.
 
@@ -48,16 +63,16 @@ RHEL.  On RHEL 5, the default RPM source directory is
    contain some extra parts.  Once you have done this, verify the fix with
    the same procedure you used above to check for the problem.
 
-3. If you are building from an Open vSwitch Git tree, then you will
+4. If you are building from an Open vSwitch Git tree, then you will
    need to first create a distribution tarball by running "./boot.sh;
    ./configure; make dist" in the Git tree.
 
-4. Copy the distribution tarball into the RPM source directory.
+5. Copy the distribution tarball into the RPM source directory.
 
-5. Unpack the distribution tarball into a temporary directory and "cd"
+6. Unpack the distribution tarball into a temporary directory and "cd"
    into the root of the distribution tarball.
 
-6. To build Open vSwitch userspace, run:
+7. To build Open vSwitch userspace, run:
 
        rpmbuild -bb rhel/openvswitch.spec
 
@@ -68,7 +83,7 @@ RHEL.  On RHEL 5, the default RPM source directory is
    then the kernel-devel package is missing or buggy.  Go back to step
    1 or 2 and fix the problem.
 
-7. On RHEL 6, to build the Open vSwitch kernel module, copy
+8. On RHEL 6, to build the Open vSwitch kernel module, copy
     rhel/openvswitch-kmod.files into the RPM source directory and run:
 
        rpmbuild -bb rhel/openvswitch-kmod-rhel6.spec
diff --git a/NEWS b/NEWS
index 4dd9568..eae1146 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,5 @@
 Post-v2.0.0
 ---------------------
-    - Log files now report times with millisecond resolution.  (Previous
-      versions only reported whole seconds.)
 
 
 v2.0.0 - xx xxx xxxx
@@ -37,6 +35,9 @@ v2.0.0 - xx xxx xxxx
       * New commands for OpenFlow 1.1+ groups.
     - Added configurable flow caching support to IPFIX exporter.
     - Dropped support for Linux pre-2.6.32.
+    - Log file timestamps and ovsdb commit timestamps are now reported
+      with millisecond resolution.  (Previous versions only reported
+      whole seconds.)
 
 
 v1.11.0 - 28 Aug 2013
index 7a75c44..90f811f 100644 (file)
@@ -168,6 +168,95 @@ didn't compare the specs carefully yet.)
       optimization in some cases for the software switch.
       [optional for OF1.3+]
 
+ONF OpenFlow Exensions for 1.3.X Pack1
+--------------------------------------
+
+OpenFlow 1.3 has a bunch of ONF extentions.
+Many of them are necessary for OpenFlow 1.4 as well.
+
+    * Flow entry notifications
+      This seems to be modelled after OVS's NXST_FLOW_MONITOR.
+      [EXT-187]
+      [required for OF1.4+]
+
+    * Role Status
+      [EXT-191]
+      [required for OF1.4+]
+
+    * Flow entry eviction
+      OVS has flow eviction functionality.
+      table_mod OFPTC_EVICTION, flow_mod 'importance', and
+      table_desc ofp_table_mod_prop_eviction need to be implemented.
+      [EXT-192-e]
+      [optional for OF1.4+]
+
+    * Vacancy events
+      [EXT-192-v]
+      [optional for OF1.4+]
+
+    * Bundle
+      Transactional modification.  OpenFlow 1.4 requires to support
+      flow_mods and port_mods in a bundle.
+      (Not related to OVS's 'ofbundle' stuff.)
+      [EXT-230]
+      [required for OF1.4+]
+
+    * Table synchronisation
+      [EXT-232]
+      [optional for OF1.4+]
+
+    * Group notifications
+      [EXT-235]
+      [optional for OF1.4+]
+
+    * Bad flow entry priority error
+      Probably not so useful to the software switch.
+      [EXT-236]
+      [optional for OF1.4+]
+
+    * Set async config error
+      [EXT-237]
+      [optional for OF1.4+]
+
+    * PBB UCA header field
+      [EXT-256]
+      [optional for OF1.4+]
+
+    * Duplicate instruction error
+      We already have ONFBIC_DUP_INSTRUCTION.
+      [EXT-260]
+      [required for OF1.4+]
+
+    * Multipart timeout error
+      [EXT-264]
+      [required for OF1.4+]
+
+OpenFlow 1.4
+------------
+
+    * More extensible wire protocol
+      Many on-wire structures got TLVs.
+      [EXT-262]
+      [required for OF1.4+]
+
+    * More descriptive reasons for packet-in
+      Distinguish OFPR_APPLY_ACTION, OFPR_ACTION_SET, OFPR_GROUP,
+      OFPR_PACKET_OUT.  NO_MATCH was renamed to OFPR_TABLE_MISS.
+      [EXT-136]
+      [required for OF1.4+]
+
+    * Optical port properties
+      [EXT-154]
+      [optional for OF1.4+]
+
+    * Flow-removed reason for meter delete
+      Report flow removal due to meter deletion with OFPRR_METER_DELETE.
+      [EXT-261]
+      [optional for OF1.4+]
+
+    * Meter notifications
+      [EXT-235]
+      [optional for OF1.4+]
 
 How to contribute
 -----------------
index 8748613..d74ad3c 100644 (file)
@@ -34,6 +34,7 @@
 /time.c
 /tmp
 /tunnel.c
+/utils.c
 /vlan.c
 /vport-generic.c
 /vport-gre.c
index 30332a2..32f906c 100644 (file)
 
 #include "gso.h"
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
+       !defined(HAVE_VLAN_BUG_WORKAROUND)
+#include <linux/module.h>
+
+static int vlan_tso __read_mostly;
+module_param(vlan_tso, int, 0644);
+MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
+#else
+#define vlan_tso true
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+static bool dev_supports_vlan_tx(struct net_device *dev)
+{
+#if defined(HAVE_VLAN_BUG_WORKAROUND)
+       return dev->features & NETIF_F_HW_VLAN_TX;
+#else
+       /* Assume that the driver is buggy. */
+       return false;
+#endif
+}
+
+int rpl_dev_queue_xmit(struct sk_buff *skb)
+{
+#undef dev_queue_xmit
+       int err = -ENOMEM;
+
+       if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
+               int features;
+
+               features = netif_skb_features(skb);
+
+               if (!vlan_tso)
+                       features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
+                                     NETIF_F_UFO | NETIF_F_FSO);
+
+               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
+               if (unlikely(!skb))
+                       return err;
+               vlan_set_tci(skb, 0);
+
+               if (netif_needs_gso(skb, features)) {
+                       struct sk_buff *nskb;
+
+                       nskb = skb_gso_segment(skb, features);
+                       if (!nskb) {
+                               if (unlikely(skb_cloned(skb) &&
+                                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
+                                       goto drop;
+
+                               skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
+                               goto xmit;
+                       }
+
+                       if (IS_ERR(nskb)) {
+                               err = PTR_ERR(nskb);
+                               goto drop;
+                       }
+                       consume_skb(skb);
+                       skb = nskb;
+
+                       do {
+                               nskb = skb->next;
+                               skb->next = NULL;
+                               err = dev_queue_xmit(skb);
+                               skb = nskb;
+                       } while (skb);
+
+                       return err;
+               }
+       }
+xmit:
+       return dev_queue_xmit(skb);
+
+drop:
+       kfree_skb(skb);
+       return err;
+}
+#endif /* kernel version < 2.6.37 */
+
 static __be16 __skb_network_protocol(struct sk_buff *skb)
 {
        __be16 type = skb->protocol;
index 4e2b7f5..2b2c855 100644 (file)
@@ -120,4 +120,9 @@ static inline void netdev_upper_dev_unlink(struct net_device *dev,
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37)
+#define dev_queue_xmit rpl_dev_queue_xmit
+int dev_queue_xmit(struct sk_buff *skb);
+#endif
+
 #endif
index b6c1d6f..8737b63 100644 (file)
@@ -26,8 +26,6 @@
 #include <linux/if_tunnel.h>
 #include <linux/if_vlan.h>
 #include <linux/in.h>
-#include <linux/if_vlan.h>
-#include <linux/in.h>
 #include <linux/in_route.h>
 #include <linux/inetdevice.h>
 #include <linux/jhash.h>
index 8e65f71..738710e 100644 (file)
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,1,0)
-#define HAVE_NET_DEVICE_OPS
-#endif
-
 struct internal_dev {
        struct vport *vport;
 };
@@ -133,7 +129,6 @@ static void internal_dev_destructor(struct net_device *dev)
        free_netdev(dev);
 }
 
-#ifdef HAVE_NET_DEVICE_OPS
 static const struct net_device_ops internal_dev_netdev_ops = {
        .ndo_open = internal_dev_open,
        .ndo_stop = internal_dev_stop,
@@ -146,22 +141,12 @@ static const struct net_device_ops internal_dev_netdev_ops = {
        .ndo_get_stats = internal_dev_sys_stats,
 #endif
 };
-#endif
 
 static void do_setup(struct net_device *netdev)
 {
        ether_setup(netdev);
 
-#ifdef HAVE_NET_DEVICE_OPS
        netdev->netdev_ops = &internal_dev_netdev_ops;
-#else
-       netdev->get_stats = internal_dev_sys_stats;
-       netdev->hard_start_xmit = internal_dev_xmit;
-       netdev->open = internal_dev_open;
-       netdev->stop = internal_dev_stop;
-       netdev->set_mac_address = eth_mac_addr;
-       netdev->change_mtu = internal_dev_change_mtu;
-#endif
 
        netdev->priv_flags &= ~IFF_TX_SKB_SHARING;
        netdev->priv_flags |= IFF_LIVE_ADDR_CHANGE;
@@ -293,11 +278,7 @@ const struct vport_ops ovs_internal_vport_ops = {
 
 int ovs_is_internal_dev(const struct net_device *netdev)
 {
-#ifdef HAVE_NET_DEVICE_OPS
        return netdev->netdev_ops == &internal_dev_netdev_ops;
-#else
-       return netdev->open == internal_dev_open;
-#endif
 }
 
 struct vport *ovs_internal_dev_get_vport(struct net_device *netdev)
index 215a47e..2a83f73 100644 (file)
 #include "vport-internal_dev.h"
 #include "vport-netdev.h"
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) && \
-       !defined(HAVE_VLAN_BUG_WORKAROUND)
-#include <linux/module.h>
-
-static int vlan_tso __read_mostly;
-module_param(vlan_tso, int, 0644);
-MODULE_PARM_DESC(vlan_tso, "Enable TSO for VLAN packets");
-#else
-#define vlan_tso true
-#endif
-
 static void netdev_port_receive(struct vport *vport, struct sk_buff *skb);
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,39)
@@ -259,19 +248,6 @@ static unsigned int packet_length(const struct sk_buff *skb)
        return length;
 }
 
-static bool dev_supports_vlan_tx(struct net_device *dev)
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,37)
-       /* Software fallback means every device supports vlan_tci on TX. */
-       return true;
-#elif defined(HAVE_VLAN_BUG_WORKAROUND)
-       return dev->features & NETIF_F_HW_VLAN_TX;
-#else
-       /* Assume that the driver is buggy. */
-       return false;
-#endif
-}
-
 static int netdev_send(struct vport *vport, struct sk_buff *skb)
 {
        struct netdev_vport *netdev_vport = netdev_vport_priv(vport);
@@ -286,59 +262,6 @@ static int netdev_send(struct vport *vport, struct sk_buff *skb)
        }
 
        skb->dev = netdev_vport->dev;
-
-       if (vlan_tx_tag_present(skb) && !dev_supports_vlan_tx(skb->dev)) {
-               int features;
-
-               features = netif_skb_features(skb);
-
-               if (!vlan_tso)
-                       features &= ~(NETIF_F_TSO | NETIF_F_TSO6 |
-                                     NETIF_F_UFO | NETIF_F_FSO);
-
-               if (netif_needs_gso(skb, features)) {
-                       struct sk_buff *nskb;
-
-                       nskb = skb_gso_segment(skb, features);
-                       if (!nskb) {
-                               if (unlikely(skb_cloned(skb) &&
-                                   pskb_expand_head(skb, 0, 0, GFP_ATOMIC)))
-                                       goto drop;
-
-                               skb_shinfo(skb)->gso_type &= ~SKB_GSO_DODGY;
-                               goto tag;
-                       }
-
-                       if (IS_ERR(nskb))
-                               goto drop;
-                       consume_skb(skb);
-                       skb = nskb;
-
-                       len = 0;
-                       do {
-                               nskb = skb->next;
-                               skb->next = NULL;
-
-                               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
-                               if (likely(skb)) {
-                                       len += skb->len;
-                                       vlan_set_tci(skb, 0);
-                                       dev_queue_xmit(skb);
-                               }
-
-                               skb = nskb;
-                       } while (skb);
-
-                       return len;
-               }
-
-tag:
-               skb = __vlan_put_tag(skb, skb->vlan_proto, vlan_tx_tag_get(skb));
-               if (unlikely(!skb))
-                       return 0;
-               vlan_set_tci(skb, 0);
-       }
-
        len = skb->len;
        dev_queue_xmit(skb);
 
index 3401dfd..ab2b6f7 100644 (file)
@@ -31,7 +31,6 @@
 #include <net/ip.h>
 #include <net/udp.h>
 #include <net/ip_tunnels.h>
-#include <net/udp.h>
 #include <net/rtnetlink.h>
 #include <net/route.h>
 #include <net/dsfield.h>
index f62201d..2882cc7 100644 (file)
@@ -239,7 +239,7 @@ void ovs_vport_del(struct vport *vport)
  * @stats: stats to set
  *
  * Provides a set of transmit, receive, and error stats to be added as an
- * offset to the collect data when stats are retreived.  Some devices may not
+ * offset to the collected data when stats are retrieved.  Some devices may not
  * support setting the stats, in which case the result will always be
  * -EOPNOTSUPP.
  *
index fe58b31..46b5630 100644 (file)
@@ -66,7 +66,7 @@ Description: Open vSwitch common components
 Package: openvswitch-switch
 Architecture: linux-any
 Suggests: openvswitch-datapath-module
-Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime, netbase
+Depends: ${shlibs:Depends}, ${misc:Depends}, ${python:Depends}, openvswitch-common (= ${binary:Version}), module-init-tools, procps, uuid-runtime, netbase, python-argparse
 Description: Open vSwitch switch implementations
  Open vSwitch is a production quality, multilayer, software-based,
  Ethernet virtual switch. It is designed to enable massive network
index 4d7a15b..c4f1426 100644 (file)
@@ -1,4 +1,5 @@
 usr/bin/ovs-dpctl
+usr/bin/ovs-dpctl-top
 usr/bin/ovs-pcap
 usr/bin/ovs-tcpundump
 usr/bin/ovs-vlan-test
index a0a331c..0afb675 100644 (file)
@@ -1,5 +1,6 @@
 _debian/ovsdb/ovsdb-server.1
 _debian/utilities/ovs-dpctl.8
+_debian/utilities/ovs-dpctl-top.8
 _debian/utilities/ovs-pcap.1
 _debian/utilities/ovs-tcpundump.1
 _debian/utilities/ovs-vlan-test.8
index d5644a1..d9a5dc8 100644 (file)
 typedef __be16 ovs_be16;
 typedef __be32 ovs_be32;
 typedef __be64 ovs_be64;
+
+#define OVS_BE16_MAX ((OVS_FORCE ovs_be16) 0xffff)
+#define OVS_BE32_MAX ((OVS_FORCE ovs_be32) 0xffffffff)
+#define OVS_BE64_MAX ((OVS_FORCE ovs_be64) 0xffffffffffffffffULL)
 \f
 /* These types help with a few funny situations:
  *
index 4a46c05..e8f86dc 100644 (file)
--- a/lib/cfm.c
+++ b/lib/cfm.c
@@ -255,9 +255,13 @@ cfm_fault_interval(struct cfm *cfm) OVS_REQUIRES(mutex)
      * as a fault (likely due to a configuration error).  Thus we can check all
      * MPs at once making this quite a bit simpler.
      *
-     * According to the specification we should check when (ccm_interval_ms *
-     * 3.5)ms have passed. */
-    return (cfm->ccm_interval_ms * 7) / 2;
+     * When cfm is not in demand mode, we check when (ccm_interval_ms * 3.5) ms
+     * have passed.  When cfm is in demand mode, we check when
+     * (MAX(ccm_interval_ms, 500) * 3.5) ms have passed.  This ensures that
+     * ovs-vswitchd has enough time to pull statistics from the datapath. */
+
+    return (MAX(cfm->ccm_interval_ms, cfm->demand ? 500 : cfm->ccm_interval_ms)
+            * 7) / 2;
 }
 
 static uint8_t
@@ -611,7 +615,6 @@ cfm_configure(struct cfm *cfm, const struct cfm_settings *s)
     }
 
     if (s->extended && s->demand) {
-        interval_ms = MAX(interval_ms, 500);
         if (!cfm->demand) {
             cfm->demand = true;
             cfm->rx_packets = cfm_rx_packets(cfm);
@@ -727,7 +730,6 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
         ccm_seq = ntohl(ccm->seq);
 
         if (ccm_interval != cfm->ccm_interval) {
-            cfm_fault |= CFM_FAULT_INTERVAL;
             VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected interval"
                          " (%"PRIu8") from RMP %"PRIu64, cfm->name,
                          ccm_interval, ccm_mpid);
@@ -735,7 +737,6 @@ cfm_process_heartbeat(struct cfm *cfm, const struct ofpbuf *p)
 
         if (extended && ccm_interval == 0
             && ccm_interval_ms_x != cfm->ccm_interval_ms) {
-            cfm_fault |= CFM_FAULT_INTERVAL;
             VLOG_WARN_RL(&rl, "%s: received a CCM with an unexpected extended"
                          " interval (%"PRIu16"ms) from RMP %"PRIu64, cfm->name,
                          ccm_interval_ms_x, ccm_mpid);
index 0f3e97c..cff713f 100644 (file)
--- a/lib/cfm.h
+++ b/lib/cfm.h
@@ -34,8 +34,7 @@ struct flow_wildcards;
     CFM_FAULT_REASON(MAID, maid)           \
     CFM_FAULT_REASON(LOOPBACK, loopback)   \
     CFM_FAULT_REASON(OVERFLOW, overflow)   \
-    CFM_FAULT_REASON(OVERRIDE, override)   \
-    CFM_FAULT_REASON(INTERVAL, interval)
+    CFM_FAULT_REASON(OVERRIDE, override)
 
 enum cfm_fault_bit_index {
 #define CFM_FAULT_REASON(NAME, STR) CFM_FAULT_INDEX_##NAME,
index f15e72c..a559a77 100644 (file)
@@ -21,6 +21,9 @@
 #include "coverage.h"
 #include "random.h"
 #include "util.h"
+#include "vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(hmap);
 
 COVERAGE_DEFINE(hmap_pathological);
 COVERAGE_DEFINE(hmap_expand);
@@ -85,7 +88,7 @@ hmap_moved(struct hmap *hmap)
 }
 
 static void
-resize(struct hmap *hmap, size_t new_mask)
+resize(struct hmap *hmap, size_t new_mask, const char *where)
 {
     struct hmap tmp;
     size_t i;
@@ -109,7 +112,10 @@ resize(struct hmap *hmap, size_t new_mask)
             count++;
         }
         if (count > 5) {
+            static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(10, 10);
             COVERAGE_INC(hmap_pathological);
+            VLOG_DBG_RL(&rl, "%s: %d nodes in bucket (%zu nodes, %zu buckets)",
+                        where, count, hmap->n, hmap->mask + 1);
         }
     }
     hmap_swap(hmap, &tmp);
@@ -136,38 +142,50 @@ calc_mask(size_t capacity)
     return mask;
 }
 
-/* Expands 'hmap', if necessary, to optimize the performance of searches. */
+/* Expands 'hmap', if necessary, to optimize the performance of searches.
+ *
+ * ('where' is used in debug logging.  Commonly one would use hmap_expand() to
+ * automatically provide the caller's source file and line number for
+ * 'where'.) */
 void
-hmap_expand(struct hmap *hmap)
+hmap_expand_at(struct hmap *hmap, const char *where)
 {
     size_t new_mask = calc_mask(hmap->n);
     if (new_mask > hmap->mask) {
         COVERAGE_INC(hmap_expand);
-        resize(hmap, new_mask);
+        resize(hmap, new_mask, where);
     }
 }
 
-/* Shrinks 'hmap', if necessary, to optimize the performance of iteration. */
+/* Shrinks 'hmap', if necessary, to optimize the performance of iteration.
+ *
+ * ('where' is used in debug logging.  Commonly one would use hmap_shrink() to
+ * automatically provide the caller's source file and line number for
+ * 'where'.) */
 void
-hmap_shrink(struct hmap *hmap)
+hmap_shrink_at(struct hmap *hmap, const char *where)
 {
     size_t new_mask = calc_mask(hmap->n);
     if (new_mask < hmap->mask) {
         COVERAGE_INC(hmap_shrink);
-        resize(hmap, new_mask);
+        resize(hmap, new_mask, where);
     }
 }
 
 /* Expands 'hmap', if necessary, to optimize the performance of searches when
  * it has up to 'n' elements.  (But iteration will be slow in a hash map whose
- * allocated capacity is much higher than its current number of nodes.)  */
+ * allocated capacity is much higher than its current number of nodes.)
+ *
+ * ('where' is used in debug logging.  Commonly one would use hmap_reserve() to
+ * automatically provide the caller's source file and line number for
+ * 'where'.) */
 void
-hmap_reserve(struct hmap *hmap, size_t n)
+hmap_reserve_at(struct hmap *hmap, size_t n, const char *where)
 {
     size_t new_mask = calc_mask(n);
     if (new_mask > hmap->mask) {
         COVERAGE_INC(hmap_reserve);
-        resize(hmap, new_mask);
+        resize(hmap, new_mask, where);
     }
 }
 
index ab7d3ae..76a73ac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009, 2010, 2012 Nicira, Inc.
+ * Copyright (c) 2008, 2009, 2010, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -78,14 +78,24 @@ static inline size_t hmap_count(const struct hmap *);
 static inline bool hmap_is_empty(const struct hmap *);
 
 /* Adjusting capacity. */
-void hmap_expand(struct hmap *);
-void hmap_shrink(struct hmap *);
-void hmap_reserve(struct hmap *, size_t capacity);
+void hmap_expand_at(struct hmap *, const char *where);
+#define hmap_expand(HMAP) hmap_expand_at(HMAP, SOURCE_LOCATOR)
+
+void hmap_shrink_at(struct hmap *, const char *where);
+#define hmap_shrink(HMAP) hmap_shrink_at(HMAP, SOURCE_LOCATOR)
+
+void hmap_reserve_at(struct hmap *, size_t capacity, const char *where);
+#define hmap_reserve(HMAP, CAPACITY) \
+    hmap_reserve_at(HMAP, CAPACITY, SOURCE_LOCATOR)
 
 /* Insertion and deletion. */
+static inline void hmap_insert_at(struct hmap *, struct hmap_node *,
+                                  size_t hash, const char *where);
+#define hmap_insert(HMAP, NODE, HASH) \
+    hmap_insert_at(HMAP, NODE, HASH, SOURCE_LOCATOR)
+
 static inline void hmap_insert_fast(struct hmap *,
                                     struct hmap_node *, size_t hash);
-static inline void hmap_insert(struct hmap *, struct hmap_node *, size_t hash);
 static inline void hmap_remove(struct hmap *, struct hmap_node *);
 
 void hmap_node_moved(struct hmap *, struct hmap_node *, struct hmap_node *);
@@ -199,13 +209,18 @@ hmap_insert_fast(struct hmap *hmap, struct hmap_node *node, size_t hash)
 }
 
 /* Inserts 'node', with the given 'hash', into 'hmap', and expands 'hmap' if
- * necessary to optimize search performance. */
+ * necessary to optimize search performance.
+ *
+ * ('where' is used in debug logging.  Commonly one would use hmap_insert() to
+ * automatically provide the caller's source file and line number for
+ * 'where'.) */
 static inline void
-hmap_insert(struct hmap *hmap, struct hmap_node *node, size_t hash)
+hmap_insert_at(struct hmap *hmap, struct hmap_node *node, size_t hash,
+               const char *where)
 {
     hmap_insert_fast(hmap, node, hash);
     if (hmap->n / 2 > hmap->mask) {
-        hmap_expand(hmap);
+        hmap_expand_at(hmap, where);
     }
 }
 
index 68d95cb..61799c9 100644 (file)
@@ -314,7 +314,7 @@ learn_execute(const struct ofpact_learn *learn, const struct flow *flow,
     fm->cookie = htonll(0);
     fm->cookie_mask = htonll(0);
     fm->new_cookie = htonll(learn->cookie);
-    fm->modify_cookie = fm->new_cookie != htonll(UINT64_MAX);
+    fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX;
     fm->table_id = learn->table_id;
     fm->command = OFPFC_MODIFY_STRICT;
     fm->idle_timeout = learn->idle_timeout;
index 51ed1b9..03413fa 100644 (file)
@@ -181,7 +181,7 @@ match_set_reg_masked(struct match *match, unsigned int reg_idx,
 void
 match_set_metadata(struct match *match, ovs_be64 metadata)
 {
-    match_set_metadata_masked(match, metadata, htonll(UINT64_MAX));
+    match_set_metadata_masked(match, metadata, OVS_BE64_MAX);
 }
 
 void
@@ -195,7 +195,7 @@ match_set_metadata_masked(struct match *match,
 void
 match_set_tun_id(struct match *match, ovs_be64 tun_id)
 {
-    match_set_tun_id_masked(match, tun_id, htonll(UINT64_MAX));
+    match_set_tun_id_masked(match, tun_id, OVS_BE64_MAX);
 }
 
 void
@@ -208,7 +208,7 @@ match_set_tun_id_masked(struct match *match, ovs_be64 tun_id, ovs_be64 mask)
 void
 match_set_tun_src(struct match *match, ovs_be32 src)
 {
-    match_set_tun_src_masked(match, src, htonl(UINT32_MAX));
+    match_set_tun_src_masked(match, src, OVS_BE32_MAX);
 }
 
 void
@@ -221,7 +221,7 @@ match_set_tun_src_masked(struct match *match, ovs_be32 src, ovs_be32 mask)
 void
 match_set_tun_dst(struct match *match, ovs_be32 dst)
 {
-    match_set_tun_dst_masked(match, dst, htonl(UINT32_MAX));
+    match_set_tun_dst_masked(match, dst, OVS_BE32_MAX);
 }
 
 void
@@ -300,7 +300,7 @@ match_set_pkt_mark_masked(struct match *match, uint32_t pkt_mark, uint32_t mask)
 void
 match_set_dl_type(struct match *match, ovs_be16 dl_type)
 {
-    match->wc.masks.dl_type = htons(UINT16_MAX);
+    match->wc.masks.dl_type = OVS_BE16_MAX;
     match->flow.dl_type = dl_type;
 }
 
@@ -411,7 +411,7 @@ match_set_dl_vlan(struct match *match, ovs_be16 dl_vlan)
 {
     flow_set_dl_vlan(&match->flow, dl_vlan);
     if (dl_vlan == htons(OFP10_VLAN_NONE)) {
-        match->wc.masks.vlan_tci = htons(UINT16_MAX);
+        match->wc.masks.vlan_tci = OVS_BE16_MAX;
     } else {
         match->wc.masks.vlan_tci |= htons(VLAN_VID_MASK | VLAN_CFI);
     }
@@ -518,7 +518,7 @@ match_set_mpls_bos(struct match *match, uint8_t mpls_bos)
 void
 match_set_tp_src(struct match *match, ovs_be16 tp_src)
 {
-    match_set_tp_src_masked(match, tp_src, htons(UINT16_MAX));
+    match_set_tp_src_masked(match, tp_src, OVS_BE16_MAX);
 }
 
 void
@@ -531,7 +531,7 @@ match_set_tp_src_masked(struct match *match, ovs_be16 port, ovs_be16 mask)
 void
 match_set_tp_dst(struct match *match, ovs_be16 tp_dst)
 {
-    match_set_tp_dst_masked(match, tp_dst, htons(UINT16_MAX));
+    match_set_tp_dst_masked(match, tp_dst, OVS_BE16_MAX);
 }
 
 void
@@ -552,7 +552,7 @@ void
 match_set_nw_src(struct match *match, ovs_be32 nw_src)
 {
     match->flow.nw_src = nw_src;
-    match->wc.masks.nw_src = htonl(UINT32_MAX);
+    match->wc.masks.nw_src = OVS_BE32_MAX;
 }
 
 void
@@ -567,7 +567,7 @@ void
 match_set_nw_dst(struct match *match, ovs_be32 nw_dst)
 {
     match->flow.nw_dst = nw_dst;
-    match->wc.masks.nw_dst = htonl(UINT32_MAX);
+    match->wc.masks.nw_dst = OVS_BE32_MAX;
 }
 
 void
@@ -692,7 +692,7 @@ match_set_ipv6_dst_masked(struct match *match, const struct in6_addr *dst,
 void
 match_set_ipv6_label(struct match *match, ovs_be32 ipv6_label)
 {
-    match->wc.masks.ipv6_label = htonl(UINT32_MAX);
+    match->wc.masks.ipv6_label = OVS_BE32_MAX;
     match->flow.ipv6_label = ipv6_label;
 }
 
@@ -779,7 +779,7 @@ format_be16_masked(struct ds *s, const char *name,
 {
     if (mask != htons(0)) {
         ds_put_format(s, "%s=", name);
-        if (mask == htons(UINT16_MAX)) {
+        if (mask == OVS_BE16_MAX) {
             ds_put_format(s, "%"PRIu16, ntohs(value));
         } else {
             ds_put_format(s, "0x%"PRIx16"/0x%"PRIx16,
@@ -798,7 +798,7 @@ format_flow_tunnel(struct ds *s, const struct match *match)
     switch (wc->masks.tunnel.tun_id) {
     case 0:
         break;
-    case CONSTANT_HTONLL(UINT64_MAX):
+    case OVS_BE64_MAX:
         ds_put_format(s, "tun_id=%#"PRIx64",", ntohll(tnl->tun_id));
         break;
     default:
@@ -926,7 +926,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
     switch (wc->masks.metadata) {
     case 0:
         break;
-    case CONSTANT_HTONLL(UINT64_MAX):
+    case OVS_BE64_MAX:
         ds_put_format(s, "metadata=%#"PRIx64",", ntohll(f->metadata));
         break;
     default:
@@ -972,7 +972,7 @@ match_format(const struct match *match, struct ds *s, unsigned int priority)
         format_ipv6_netmask(s, "ipv6_src", &f->ipv6_src, &wc->masks.ipv6_src);
         format_ipv6_netmask(s, "ipv6_dst", &f->ipv6_dst, &wc->masks.ipv6_dst);
         if (wc->masks.ipv6_label) {
-            if (wc->masks.ipv6_label == htonl(UINT32_MAX)) {
+            if (wc->masks.ipv6_label == OVS_BE32_MAX) {
                 ds_put_format(s, "ipv6_label=0x%05"PRIx32",",
                               ntohl(f->ipv6_label));
             } else {
index 2f7dfb8..3a31c29 100644 (file)
@@ -2310,12 +2310,12 @@ mf_from_ipv4_string(const struct mf_field *mf, const char *s,
             return xasprintf("%s: network prefix bits not between 1 and "
                              "32", s);
         } else if (prefix == 32) {
-            *mask = htonl(UINT32_MAX);
+            *mask = OVS_BE32_MAX;
         } else {
             *mask = htonl(((1u << prefix) - 1) << (32 - prefix));
         }
     } else if (sscanf(s, IP_SCAN_FMT, IP_SCAN_ARGS(ip)) == IP_SCAN_COUNT) {
-        *mask = htonl(UINT32_MAX);
+        *mask = OVS_BE32_MAX;
     } else {
         return xasprintf("%s: invalid IP address", s);
     }
@@ -2373,7 +2373,7 @@ mf_from_ofp_port_string(const struct mf_field *mf, const char *s,
 
     if (ofputil_port_from_string(s, &port)) {
         *valuep = htons(ofp_to_u16(port));
-        *maskp = htons(UINT16_MAX);
+        *maskp = OVS_BE16_MAX;
         return NULL;
     }
     return xasprintf("%s: port value out of range for %s", s, mf->name);
@@ -2388,7 +2388,7 @@ mf_from_ofp_port_string32(const struct mf_field *mf, const char *s,
     ovs_assert(mf->n_bytes == sizeof(ovs_be32));
     if (ofputil_port_from_string(s, &port)) {
         *valuep = ofputil_port_to_ofp11(port);
-        *maskp = htonl(UINT32_MAX);
+        *maskp = OVS_BE32_MAX;
         return NULL;
     }
     return xasprintf("%s: port value out of range for %s", s, mf->name);
@@ -2493,7 +2493,7 @@ static char *
 mf_from_tun_flags_string(const char *s, ovs_be16 *valuep, ovs_be16 *maskp)
 {
     if (!parse_flow_tun_flags(s, flow_tun_flag_to_string, valuep)) {
-        *maskp = htons(UINT16_MAX);
+        *maskp = OVS_BE16_MAX;
         return NULL;
     }
 
@@ -2661,8 +2661,7 @@ mf_format(const struct mf_field *mf,
         break;
 
     case MFS_IPV4:
-        ip_format_masked(value->be32, mask ? mask->be32 : htonl(UINT32_MAX),
-                         s);
+        ip_format_masked(value->be32, mask ? mask->be32 : OVS_BE32_MAX, s);
         break;
 
     case MFS_IPV6:
index af50597..0374ae3 100644 (file)
@@ -603,7 +603,7 @@ netdev_vport_patch_peer(const struct netdev *netdev_)
 
 void
 netdev_vport_inc_rx(const struct netdev *netdev,
-                          const struct dpif_flow_stats *stats)
+                    const struct dpif_flow_stats *stats)
 {
     if (is_vport_class(netdev_get_class(netdev))) {
         struct netdev_vport *dev = netdev_vport_cast(netdev);
index 3bb71e2..2d7ee34 100644 (file)
@@ -183,7 +183,7 @@ nx_pull_raw(const uint8_t *p, unsigned int match_len, bool strict,
                 if (NXM_HASMASK(header)) {
                     memcpy(cookie_mask, p + 4 + width, width);
                 } else {
-                    *cookie_mask = htonll(UINT64_MAX);
+                    *cookie_mask = OVS_BE64_MAX;
                 }
                 error = 0;
             }
@@ -361,7 +361,7 @@ nxm_put_16m(struct ofpbuf *b, uint32_t header, ovs_be16 value, ovs_be16 mask)
     case 0:
         break;
 
-    case CONSTANT_HTONS(UINT16_MAX):
+    case OVS_BE16_MAX:
         nxm_put_16(b, header, value);
         break;
 
@@ -393,7 +393,7 @@ nxm_put_32m(struct ofpbuf *b, uint32_t header, ovs_be32 value, ovs_be32 mask)
     case 0:
         break;
 
-    case CONSTANT_HTONL(UINT32_MAX):
+    case OVS_BE32_MAX:
         nxm_put_32(b, header, value);
         break;
 
@@ -425,7 +425,7 @@ nxm_put_64m(struct ofpbuf *b, uint32_t header, ovs_be64 value, ovs_be64 mask)
     case 0:
         break;
 
-    case CONSTANT_HTONLL(UINT64_MAX):
+    case OVS_BE64_MAX:
         nxm_put_64(b, header, value);
         break;
 
index f20bd8a..aec4196 100644 (file)
@@ -1407,7 +1407,7 @@ odp_flow_format(const struct nlattr *key, size_t key_len,
 
         if (left) {
             int i;
-            
+
             if (left == key_len) {
                 ds_put_cstr(ds, "<empty>");
             }
@@ -1665,7 +1665,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         int name_len;
 
         name = s + 8;
-        name_len = strcspn(s, ")");
+        name_len = strcspn(name, ")");
         node = simap_find_len(port_names, name, name_len);
         if (node) {
             nl_msg_put_u32(key, OVS_KEY_ATTR_IN_PORT, node->data);
@@ -1735,7 +1735,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                                   (pcp << VLAN_PCP_SHIFT) |
                                   VLAN_CFI));
             if (mask) {
-                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(UINT16_MAX));
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
             }
             return n;
         } else if (mask && (sscanf(s, "vlan(vid=%"SCNi16"/%"SCNi16",pcp=%i/%i,cfi=%i/%i)%n",
@@ -1756,7 +1756,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
                                   (pcp << VLAN_PCP_SHIFT) |
                                   (cfi ? VLAN_CFI : 0)));
             if (mask) {
-                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, htons(UINT16_MAX));
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_VLAN, OVS_BE16_MAX);
             }
             return n;
         }
@@ -1777,8 +1777,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
         } else if (sscanf(s, "eth_type(%i)%n", &eth_type, &n) > 0 && n > 0) {
             nl_msg_put_be16(key, OVS_KEY_ATTR_ETHERTYPE, htons(eth_type));
             if (mask) {
-                nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE,
-                                htons(UINT16_MAX));
+                nl_msg_put_be16(mask, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
             }
             return n;
         }
@@ -1813,7 +1812,7 @@ parse_odp_key_mask_attr(const char *s, const struct simap *port_names,
             if (mask) {
                 mpls = nl_msg_put_unspec_uninit(mask, OVS_KEY_ATTR_MPLS,
                                             sizeof *mpls);
-                mpls->mpls_lse = htonl(UINT32_MAX);
+                mpls->mpls_lse = OVS_BE32_MAX;
             }
             return n;
         }
@@ -2434,7 +2433,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
 
     if (flow->vlan_tci != htons(0) || flow->dl_type == htons(ETH_TYPE_VLAN)) {
         if (is_mask) {
-            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(UINT16_MAX));
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
         } else {
             nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(ETH_TYPE_VLAN));
         }
@@ -2460,7 +2459,7 @@ odp_flow_key_from_flow__(struct ofpbuf *buf, const struct flow *data,
          *                    802.3 SNAP packet with valid eth_type).
          */
         if (is_mask) {
-            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, htons(UINT16_MAX));
+            nl_msg_put_be16(buf, OVS_KEY_ATTR_ETHERTYPE, OVS_BE16_MAX);
         }
         goto unencap;
     }
index 54df17f..dcc82db 100644 (file)
@@ -2428,7 +2428,7 @@ ofpact_format(const struct ofpact *a, struct ds *s)
                       ovs_instruction_name_from_type(
                           OVSINST_OFPIT11_WRITE_METADATA),
                       ntohll(metadata->metadata));
-        if (metadata->mask != htonll(UINT64_MAX)) {
+        if (metadata->mask != OVS_BE64_MAX) {
             ds_put_format(s, "/%#"PRIx64, ntohll(metadata->mask));
         }
         break;
index e28bf02..522bd95 100644 (file)
@@ -544,7 +544,7 @@ parse_metadata(struct ofpbuf *b, char *arg)
             return error;
         }
     } else {
-        om->mask = htonll(UINT64_MAX);
+        om->mask = OVS_BE64_MAX;
     }
 
     return str_to_be64(arg, &om->metadata);
@@ -1149,7 +1149,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
     fm->cookie_mask = htonll(0);
     if (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT) {
         /* For modify, by default, don't update the cookie. */
-        fm->new_cookie = htonll(UINT64_MAX);
+        fm->new_cookie = OVS_BE64_MAX;
     } else{
         fm->new_cookie = htonll(0);
     }
@@ -1290,7 +1290,7 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
             *usable_protocols &= OFPUTIL_P_NXM_OXM_ANY;
         }
     }
-    if (!fm->cookie_mask && fm->new_cookie == htonll(UINT64_MAX)
+    if (!fm->cookie_mask && fm->new_cookie == OVS_BE64_MAX
         && (command == OFPFC_MODIFY || command == OFPFC_MODIFY_STRICT)) {
         /* On modifies without a mask, we are supposed to add a flow if
          * one does not exist.  If a cookie wasn't been specified, use a
index c0553af..6fe1cee 100644 (file)
@@ -817,7 +817,7 @@ ofp_print_flow_mod(struct ds *s, const struct ofp_header *oh, int verbosity)
     if (ds_last(s) != ' ') {
         ds_put_char(s, ' ');
     }
-    if (fm.new_cookie != htonll(0) && fm.new_cookie != htonll(UINT64_MAX)) {
+    if (fm.new_cookie != htonll(0) && fm.new_cookie != OVS_BE64_MAX) {
         ds_put_format(s, "cookie:0x%"PRIx64" ", ntohll(fm.new_cookie));
     }
     if (fm.cookie_mask != htonll(0)) {
index 9edfe9e..6a2bf5b 100644 (file)
@@ -106,10 +106,10 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
                                                  >> OFPFW10_NW_DST_SHIFT);
 
     if (!(ofpfw & OFPFW10_TP_SRC)) {
-        wc->masks.tp_src = htons(UINT16_MAX);
+        wc->masks.tp_src = OVS_BE16_MAX;
     }
     if (!(ofpfw & OFPFW10_TP_DST)) {
-        wc->masks.tp_dst = htons(UINT16_MAX);
+        wc->masks.tp_dst = OVS_BE16_MAX;
     }
 
     if (!(ofpfw & OFPFW10_DL_SRC)) {
@@ -119,7 +119,7 @@ ofputil_wildcard_from_ofpfw10(uint32_t ofpfw, struct flow_wildcards *wc)
         memset(wc->masks.dl_dst, 0xff, ETH_ADDR_LEN);
     }
     if (!(ofpfw & OFPFW10_DL_TYPE)) {
-        wc->masks.dl_type = htons(UINT16_MAX);
+        wc->masks.dl_type = OVS_BE16_MAX;
     }
 
     /* VLAN TCI mask. */
@@ -335,7 +335,7 @@ ofputil_match_from_ofp11_match(const struct ofp11_match *ofmatch,
         if (ofmatch->dl_vlan == htons(OFPVID11_NONE)) {
             /* Match only packets without a VLAN tag. */
             match->flow.vlan_tci = htons(0);
-            match->wc.masks.vlan_tci = htons(UINT16_MAX);
+            match->wc.masks.vlan_tci = OVS_BE16_MAX;
         } else {
             if (ofmatch->dl_vlan == htons(OFPVID11_ANY)) {
                 /* Match any packet with a VLAN tag regardless of VID. */
@@ -1525,7 +1525,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
         } else {
             fm->cookie = ofm->cookie;
             fm->cookie_mask = ofm->cookie_mask;
-            fm->new_cookie = htonll(UINT64_MAX);
+            fm->new_cookie = OVS_BE64_MAX;
         }
         fm->modify_cookie = false;
         fm->command = ofm->command;
@@ -1617,7 +1617,7 @@ ofputil_decode_flow_mod(struct ofputil_flow_mod *fm,
             NOT_REACHED();
         }
 
-        fm->modify_cookie = fm->new_cookie != htonll(UINT64_MAX);
+        fm->modify_cookie = fm->new_cookie != OVS_BE64_MAX;
         if (protocol & OFPUTIL_P_TID) {
             fm->command = command & 0xff;
             fm->table_id = command >> 8;
@@ -3838,7 +3838,7 @@ ofputil_decode_role_message(const struct ofp_header *oh,
         rr->role = ntohl(orr->role);
         if (raw == OFPRAW_OFPT12_ROLE_REQUEST
             ? orr->role == htonl(OFPCR12_ROLE_NOCHANGE)
-            : orr->generation_id == htonll(UINT64_MAX)) {
+            : orr->generation_id == OVS_BE64_MAX) {
             rr->have_generation_id = false;
             rr->generation_id = 0;
         } else {
@@ -6096,8 +6096,8 @@ ofputil_queue_stats_to_ofp13(const struct ofputil_queue_stats *oqs,
         qs13->duration_sec = htonl(oqs->duration_sec);
         qs13->duration_nsec = htonl(oqs->duration_nsec);
     } else {
-        qs13->duration_sec = htonl(UINT32_MAX);
-        qs13->duration_nsec = htonl(UINT32_MAX);
+        qs13->duration_sec = OVS_BE32_MAX;
+        qs13->duration_nsec = OVS_BE32_MAX;
     }
 }
 
index d15c402..922c5db 100644 (file)
@@ -218,32 +218,6 @@ eth_pop_vlan(struct ofpbuf *packet)
     }
 }
 
-/* Return depth of mpls stack.
- *
- * 'packet->l2_5' should initially point to 'packet''s outer-most MPLS header
- * or may be NULL if there are no MPLS headers. */
-uint16_t
-eth_mpls_depth(const struct ofpbuf *packet)
-{
-    struct mpls_hdr *mh = packet->l2_5;
-    uint16_t depth;
-
-    if (!mh) {
-        return 0;
-    }
-
-    depth = 0;
-    while (packet->size >= ((char *)mh - (char *)packet->data) + sizeof *mh) {
-        depth++;
-        if (mh->mpls_lse & htonl(MPLS_BOS_MASK)) {
-            break;
-        }
-        mh++;
-    }
-
-    return depth;
-}
-
 /* Set ethertype of the packet. */
 void
 set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type)
@@ -459,7 +433,7 @@ void
 ip_format_masked(ovs_be32 ip, ovs_be32 mask, struct ds *s)
 {
     ds_put_format(s, IP_FMT, IP_ARGS(ip));
-    if (mask != htonl(UINT32_MAX)) {
+    if (mask != OVS_BE32_MAX) {
         if (ip_is_cidr(mask)) {
             ds_put_format(s, "/%d", ip_count_cidr_bits(mask));
         } else {
index b776914..7388152 100644 (file)
@@ -143,8 +143,6 @@ void compose_rarp(struct ofpbuf *, const uint8_t eth_src[ETH_ADDR_LEN]);
 void eth_push_vlan(struct ofpbuf *, ovs_be16 tci);
 void eth_pop_vlan(struct ofpbuf *);
 
-uint16_t eth_mpls_depth(const struct ofpbuf *packet);
-
 void set_ethertype(struct ofpbuf *packet, ovs_be16 eth_type);
 
 const char *eth_from_hex(const char *hex, struct ofpbuf **packetp);
index 263f2ea..811d2f9 100644 (file)
@@ -124,6 +124,10 @@ utilities/ovs-dpctl.8.in:
 lib/common.man:
 lib/vlog.man:
 
+utilities/ovs-dpctl-top.8: \
+       utilities/ovs-dpctl-top.8.in
+utilities/ovs-dpctl-top.8.in:
+
 utilities/ovs-l3ping.8: \
        utilities/ovs-l3ping.8.in \
        lib/common-syn.man \
index bc1e884..180b87e 100644 (file)
@@ -254,7 +254,7 @@ udpif_revalidate(struct udpif *udpif)
     udpif_drop_key_clear(udpif);
 }
 
-/* Retreives the next upcall which ofproto-dpif is responsible for handling.
+/* Retrieves the next upcall which ofproto-dpif is responsible for handling.
  * The caller is responsible for destroying the returned upcall with
  * upcall_destroy(). */
 struct upcall *
@@ -274,7 +274,7 @@ upcall_destroy(struct upcall *upcall)
     }
 }
 
-/* Retreives the next batch of processed flow misses for 'udpif' to install.
+/* Retrieves the next batch of processed flow misses for 'udpif' to install.
  * The caller is responsible for destroying it with flow_miss_batch_destroy().
  */
 struct flow_miss_batch *
@@ -309,6 +309,7 @@ void
 flow_miss_batch_destroy(struct flow_miss_batch *fmb)
 {
     struct flow_miss *miss, *next;
+    struct upcall *upcall, *next_upcall;
 
     if (!fmb) {
         return;
@@ -319,11 +320,16 @@ flow_miss_batch_destroy(struct flow_miss_batch *fmb)
         miss_destroy(miss);
     }
 
+    LIST_FOR_EACH_SAFE (upcall, next_upcall, list_node, &fmb->upcalls) {
+        list_remove(&upcall->list_node);
+        upcall_destroy(upcall);
+    }
+
     hmap_destroy(&fmb->misses);
     free(fmb);
 }
 
-/* Retreives the next drop key which ofproto-dpif needs to process.  The caller
+/* Retrieves the next drop key which ofproto-dpif needs to process.  The caller
  * is responsible for destroying it with drop_key_destroy(). */
 struct drop_key *
 drop_key_next(struct udpif *udpif)
@@ -332,7 +338,7 @@ drop_key_next(struct udpif *udpif)
     return next ? CONTAINER_OF(next, struct drop_key, list_node) : NULL;
 }
 
-/* Destorys and deallocates 'drop_key'. */
+/* Destroys and deallocates 'drop_key'. */
 void
 drop_key_destroy(struct drop_key *drop_key)
 {
@@ -375,17 +381,17 @@ udpif_dispatcher(void *arg)
     return NULL;
 }
 
-/* The miss handler thread is responsible for processing miss upcalls retreived
+/* The miss handler thread is responsible for processing miss upcalls retrieved
  * by the dispatcher thread.  Once finished it passes the processed miss
  * upcalls to ofproto-dpif where they're installed in the datapath. */
 static void *
 udpif_miss_handler(void *arg)
 {
-    struct list misses = LIST_INITIALIZER(&misses);
     struct handler *handler = arg;
 
     set_subprogram_name("miss_handler");
     for (;;) {
+        struct list misses = LIST_INITIALIZER(&misses);
         size_t i;
 
         ovs_mutex_lock(&handler->mutex);
@@ -416,12 +422,6 @@ udpif_miss_handler(void *arg)
 static void
 miss_destroy(struct flow_miss *miss)
 {
-    struct upcall *upcall, *next;
-
-    LIST_FOR_EACH_SAFE (upcall, next, list_node, &miss->upcalls) {
-        list_remove(&upcall->list_node);
-        upcall_destroy(upcall);
-    }
     xlate_out_uninit(&miss->xout);
 }
 
@@ -598,100 +598,6 @@ flow_miss_find(struct hmap *todo, const struct ofproto_dpif *ofproto,
     return NULL;
 }
 
-/* Executes flow miss 'miss'.  May add any required datapath operations
- * to 'ops', incrementing '*n_ops' for each new op. */
-static void
-execute_flow_miss(struct flow_miss *miss, struct dpif_op *ops, size_t *n_ops)
-{
-    struct ofproto_dpif *ofproto = miss->ofproto;
-    struct flow_wildcards wc;
-    struct rule_dpif *rule;
-    struct ofpbuf *packet;
-    struct xlate_in xin;
-
-    memset(&miss->stats, 0, sizeof miss->stats);
-    miss->stats.used = time_msec();
-    LIST_FOR_EACH (packet, list_node, &miss->packets) {
-        miss->stats.tcp_flags |= packet_get_tcp_flags(packet, &miss->flow);
-        miss->stats.n_bytes += packet->size;
-        miss->stats.n_packets++;
-    }
-
-    flow_wildcards_init_catchall(&wc);
-    rule_dpif_lookup(ofproto, &miss->flow, &wc, &rule);
-    rule_dpif_credit_stats(rule, &miss->stats);
-    xlate_in_init(&xin, ofproto, &miss->flow, rule, miss->stats.tcp_flags,
-                  NULL);
-    xin.may_learn = true;
-    xin.resubmit_stats = &miss->stats;
-    xlate_actions(&xin, &miss->xout);
-    flow_wildcards_or(&miss->xout.wc, &miss->xout.wc, &wc);
-
-    if (rule_dpif_fail_open(rule)) {
-        LIST_FOR_EACH (packet, list_node, &miss->packets) {
-            struct ofputil_packet_in *pin;
-
-            /* Extra-special case for fail-open mode.
-             *
-             * We are in fail-open mode and the packet matched the fail-open
-             * rule, but we are connected to a controller too.  We should send
-             * the packet up to the controller in the hope that it will try to
-             * set up a flow and thereby allow us to exit fail-open.
-             *
-             * See the top-level comment in fail-open.c for more information. */
-            pin = xmalloc(sizeof(*pin));
-            pin->packet = xmemdup(packet->data, packet->size);
-            pin->packet_len = packet->size;
-            pin->reason = OFPR_NO_MATCH;
-            pin->controller_id = 0;
-            pin->table_id = 0;
-            pin->cookie = 0;
-            pin->send_len = 0; /* Not used for flow table misses. */
-            flow_get_metadata(&miss->flow, &pin->fmd);
-            ofproto_dpif_send_packet_in(ofproto, pin);
-        }
-    }
-
-    if (miss->xout.slow) {
-        LIST_FOR_EACH (packet, list_node, &miss->packets) {
-            struct xlate_in xin;
-
-            xlate_in_init(&xin, miss->ofproto, &miss->flow, rule, 0, packet);
-            xlate_actions_for_side_effects(&xin);
-        }
-    }
-    rule_dpif_unref(rule);
-
-    if (miss->xout.odp_actions.size) {
-        LIST_FOR_EACH (packet, list_node, &miss->packets) {
-            struct dpif_op *op = &ops[*n_ops];
-            struct dpif_execute *execute = &op->u.execute;
-
-            if (miss->flow.in_port.ofp_port
-                != vsp_realdev_to_vlandev(miss->ofproto,
-                                          miss->flow.in_port.ofp_port,
-                                          miss->flow.vlan_tci)) {
-                /* This packet was received on a VLAN splinter port.  We
-                 * added a VLAN to the packet to make the packet resemble
-                 * the flow, but the actions were composed assuming that
-                 * the packet contained no VLAN.  So, we must remove the
-                 * VLAN header from the packet before trying to execute the
-                 * actions. */
-                eth_pop_vlan(packet);
-            }
-
-            op->type = DPIF_OP_EXECUTE;
-            execute->key = miss->key;
-            execute->key_len = miss->key_len;
-            execute->packet = packet;
-            execute->actions = miss->xout.odp_actions.data;
-            execute->actions_len = miss->xout.odp_actions.size;
-
-            (*n_ops)++;
-        }
-    }
-}
-
 static void
 handle_miss_upcalls(struct udpif *udpif, struct list *upcalls)
 {
@@ -699,98 +605,189 @@ handle_miss_upcalls(struct udpif *udpif, struct list *upcalls)
     struct dpif_op ops[FLOW_MISS_MAX_BATCH];
     struct upcall *upcall, *next;
     struct flow_miss_batch *fmb;
-    size_t n_upcalls, n_ops, i;
+    size_t n_misses, n_ops, i;
     struct flow_miss *miss;
     unsigned int reval_seq;
+    bool fail_open;
 
-    /* Construct the to-do list.
+    /* Extract the flow from each upcall.  Construct in fmb->misses a hash
+     * table that maps each unique flow to a 'struct flow_miss'.
      *
-     * This just amounts to extracting the flow from each packet and sticking
-     * the packets that have the same flow in the same "flow_miss" structure so
-     * that we can process them together. */
+     * Most commonly there is a single packet per flow_miss, but there are
+     * several reasons why there might be more than one, e.g.:
+     *
+     *   - The dpif packet interface does not support TSO (or UFO, etc.), so a
+     *     large packet sent to userspace is split into a sequence of smaller
+     *     ones.
+     *
+     *   - A stream of quickly arriving packets in an established "slow-pathed"
+     *     flow.
+     *
+     *   - Rarely, a stream of quickly arriving packets in a flow not yet
+     *     established.  (This is rare because most protocols do not send
+     *     multiple back-to-back packets before receiving a reply from the
+     *     other end of the connection, which gives OVS a chance to set up a
+     *     datapath flow.)
+     */
     fmb = xmalloc(sizeof *fmb);
     atomic_read(&udpif->reval_seq, &fmb->reval_seq);
     hmap_init(&fmb->misses);
-    n_upcalls = 0;
+    list_init(&fmb->upcalls);
+    n_misses = 0;
     LIST_FOR_EACH_SAFE (upcall, next, list_node, upcalls) {
         struct dpif_upcall *dupcall = &upcall->dpif_upcall;
-        struct flow_miss *miss = &fmb->miss_buf[n_upcalls];
+        struct ofpbuf *packet = dupcall->packet;
+        struct flow_miss *miss = &fmb->miss_buf[n_misses];
         struct flow_miss *existing_miss;
         struct ofproto_dpif *ofproto;
         odp_port_t odp_in_port;
         struct flow flow;
-        uint32_t hash;
         int error;
 
-        error = xlate_receive(udpif->backer, dupcall->packet, dupcall->key,
+        error = xlate_receive(udpif->backer, packet, dupcall->key,
                               dupcall->key_len, &flow, &miss->key_fitness,
                               &ofproto, &odp_in_port);
 
-        if (error == ENODEV) {
-            struct drop_key *drop_key;
-
-            /* Received packet on datapath port for which we couldn't
-             * associate an ofproto.  This can happen if a port is removed
-             * while traffic is being received.  Print a rate-limited message
-             * in case it happens frequently.  Install a drop flow so
-             * that future packets of the flow are inexpensively dropped
-             * in the kernel. */
-            VLOG_INFO_RL(&rl, "received packet on unassociated datapath port "
-                              "%"PRIu32, odp_in_port);
-
-            drop_key = xmalloc(sizeof *drop_key);
-            drop_key->key = xmemdup(dupcall->key, dupcall->key_len);
-            drop_key->key_len = dupcall->key_len;
-
-            if (guarded_list_push_back(&udpif->drop_keys, &drop_key->list_node,
-                                       MAX_QUEUE_LENGTH)) {
-                seq_change(udpif->wait_seq);
+        if (!error) {
+            uint32_t hash;
+
+            flow_extract(packet, flow.skb_priority, flow.pkt_mark,
+                         &flow.tunnel, &flow.in_port, &miss->flow);
+
+            hash = flow_hash(&miss->flow, 0);
+            existing_miss = flow_miss_find(&fmb->misses, ofproto, &miss->flow,
+                                           hash);
+            if (!existing_miss) {
+                hmap_insert(&fmb->misses, &miss->hmap_node, hash);
+                miss->ofproto = ofproto;
+                miss->key = dupcall->key;
+                miss->key_len = dupcall->key_len;
+                miss->upcall_type = dupcall->type;
+                miss->stats.n_packets = 0;
+                miss->stats.n_bytes = 0;
+                miss->stats.used = time_msec();
+                miss->stats.tcp_flags = 0;
+
+                n_misses++;
             } else {
-                COVERAGE_INC(drop_queue_overflow);
-                drop_key_destroy(drop_key);
+                miss = existing_miss;
             }
-            continue;
-        } else if (error) {
-            continue;
-        }
+            miss->stats.tcp_flags |= packet_get_tcp_flags(packet, &miss->flow);
+            miss->stats.n_bytes += packet->size;
+            miss->stats.n_packets++;
 
-        flow_extract(dupcall->packet, flow.skb_priority, flow.pkt_mark,
-                     &flow.tunnel, &flow.in_port, &miss->flow);
-
-        /* Add other packets to a to-do list. */
-        hash = flow_hash(&miss->flow, 0);
-        existing_miss = flow_miss_find(&fmb->misses, ofproto, &miss->flow, hash);
-        if (!existing_miss) {
-            hmap_insert(&fmb->misses, &miss->hmap_node, hash);
-            miss->ofproto = ofproto;
-            miss->key = dupcall->key;
-            miss->key_len = dupcall->key_len;
-            miss->upcall_type = dupcall->type;
-            list_init(&miss->packets);
-            list_init(&miss->upcalls);
-
-            n_upcalls++;
+            upcall->flow_miss = miss;
         } else {
-            miss = existing_miss;
+            if (error == ENODEV) {
+                struct drop_key *drop_key;
+
+                /* Received packet on datapath port for which we couldn't
+                 * associate an ofproto.  This can happen if a port is removed
+                 * while traffic is being received.  Print a rate-limited
+                 * message in case it happens frequently.  Install a drop flow
+                 * so that future packets of the flow are inexpensively dropped
+                 * in the kernel. */
+                VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
+                             "port %"PRIu32, odp_in_port);
+
+                drop_key = xmalloc(sizeof *drop_key);
+                drop_key->key = xmemdup(dupcall->key, dupcall->key_len);
+                drop_key->key_len = dupcall->key_len;
+
+                if (guarded_list_push_back(&udpif->drop_keys,
+                                           &drop_key->list_node,
+                                           MAX_QUEUE_LENGTH)) {
+                    seq_change(udpif->wait_seq);
+                } else {
+                    COVERAGE_INC(drop_queue_overflow);
+                    drop_key_destroy(drop_key);
+                }
+            }
+            list_remove(&upcall->list_node);
+            upcall_destroy(upcall);
         }
-        list_push_back(&miss->packets, &dupcall->packet->list_node);
-
-        list_remove(&upcall->list_node);
-        list_push_back(&miss->upcalls, &upcall->list_node);
     }
 
-    LIST_FOR_EACH_SAFE (upcall, next, list_node, upcalls) {
-        list_remove(&upcall->list_node);
-        upcall_destroy(upcall);
-    }
-
-    /* Process each element in the to-do list, constructing the set of
-     * operations to batch. */
-    n_ops = 0;
+    /* Initialize each 'struct flow_miss's ->xout.
+     *
+     * We do this per-flow_miss rather than per-packet because, most commonly,
+     * all the packets in a flow can use the same translation.
+     *
+     * We can't do this in the previous loop because we need the TCP flags for
+     * all the packets in each miss. */
+    fail_open = false;
     HMAP_FOR_EACH (miss, hmap_node, &fmb->misses) {
-        execute_flow_miss(miss, ops, &n_ops);
+        struct flow_wildcards wc;
+        struct rule_dpif *rule;
+        struct xlate_in xin;
+
+        flow_wildcards_init_catchall(&wc);
+        rule_dpif_lookup(miss->ofproto, &miss->flow, &wc, &rule);
+        if (rule_dpif_fail_open(rule)) {
+            fail_open = true;
+        }
+        rule_dpif_credit_stats(rule, &miss->stats);
+        xlate_in_init(&xin, miss->ofproto, &miss->flow, rule,
+                      miss->stats.tcp_flags, NULL);
+        xin.may_learn = true;
+        xin.resubmit_stats = &miss->stats;
+        xlate_actions(&xin, &miss->xout);
+        flow_wildcards_or(&miss->xout.wc, &miss->xout.wc, &wc);
+        rule_dpif_unref(rule);
+    }
+
+    /* Now handle the packets individually in order of arrival.  In the common
+     * case each packet of a miss can share the same actions, but slow-pathed
+     * packets need to be translated individually:
+     *
+     *   - For SLOW_CFM, SLOW_LACP, SLOW_STP, and SLOW_BFD, translation is what
+     *     processes received packets for these protocols.
+     *
+     *   - For SLOW_CONTROLLER, translation sends the packet to the OpenFlow
+     *     controller.
+     *
+     * The loop fills 'ops' with an array of operations to execute in the
+     * datapath. */
+    n_ops = 0;
+    LIST_FOR_EACH (upcall, list_node, upcalls) {
+        struct flow_miss *miss = upcall->flow_miss;
+        struct ofpbuf *packet = upcall->dpif_upcall.packet;
+
+        if (miss->xout.slow) {
+            struct rule_dpif *rule;
+            struct xlate_in xin;
+
+            rule_dpif_lookup(miss->ofproto, &miss->flow, NULL, &rule);
+            xlate_in_init(&xin, miss->ofproto, &miss->flow, rule, 0, packet);
+            xlate_actions_for_side_effects(&xin);
+            rule_dpif_unref(rule);
+        }
+
+        if (miss->xout.odp_actions.size) {
+            struct dpif_op *op;
+
+            if (miss->flow.in_port.ofp_port
+                != vsp_realdev_to_vlandev(miss->ofproto,
+                                          miss->flow.in_port.ofp_port,
+                                          miss->flow.vlan_tci)) {
+                /* This packet was received on a VLAN splinter port.  We
+                 * added a VLAN to the packet to make the packet resemble
+                 * the flow, but the actions were composed assuming that
+                 * the packet contained no VLAN.  So, we must remove the
+                 * VLAN header from the packet before trying to execute the
+                 * actions. */
+                eth_pop_vlan(packet);
+            }
+
+            op = &ops[n_ops++];
+            op->type = DPIF_OP_EXECUTE;
+            op->u.execute.key = miss->key;
+            op->u.execute.key_len = miss->key_len;
+            op->u.execute.packet = packet;
+            op->u.execute.actions = miss->xout.odp_actions.data;
+            op->u.execute.actions_len = miss->xout.odp_actions.size;
+        }
     }
-    ovs_assert(n_ops <= ARRAY_SIZE(ops));
 
     /* Execute batch. */
     for (i = 0; i < n_ops; i++) {
@@ -798,6 +795,34 @@ handle_miss_upcalls(struct udpif *udpif, struct list *upcalls)
     }
     dpif_operate(udpif->dpif, opsp, n_ops);
 
+    /* Special case for fail-open mode.
+     *
+     * If we are in fail-open mode, but we are connected to a controller too,
+     * then we should send the packet up to the controller in the hope that it
+     * will try to set up a flow and thereby allow us to exit fail-open.
+     *
+     * See the top-level comment in fail-open.c for more information. */
+    if (fail_open) {
+        LIST_FOR_EACH (upcall, list_node, upcalls) {
+            struct flow_miss *miss = upcall->flow_miss;
+            struct ofpbuf *packet = upcall->dpif_upcall.packet;
+            struct ofputil_packet_in *pin;
+
+            pin = xmalloc(sizeof *pin);
+            pin->packet = xmemdup(packet->data, packet->size);
+            pin->packet_len = packet->size;
+            pin->reason = OFPR_NO_MATCH;
+            pin->controller_id = 0;
+            pin->table_id = 0;
+            pin->cookie = 0;
+            pin->send_len = 0; /* Not used for flow table misses. */
+            flow_get_metadata(&miss->flow, &pin->fmd);
+            ofproto_dpif_send_packet_in(miss->ofproto, pin);
+        }
+    }
+
+    list_move(&fmb->upcalls, upcalls);
+
     atomic_read(&udpif->reval_seq, &reval_seq);
     if (reval_seq != fmb->reval_seq) {
         COVERAGE_INC(fmb_queue_revalidated);
index 57d462d..cd97e79 100644 (file)
@@ -59,6 +59,7 @@ enum upcall_type {
 /* An upcall. */
 struct upcall {
     struct list list_node;          /* For queuing upcalls. */
+    struct flow_miss *flow_miss;    /* This upcall's flow_miss. */
 
     enum upcall_type type;          /* Classification. */
 
@@ -90,13 +91,10 @@ struct flow_miss {
     enum odp_key_fitness key_fitness;
     const struct nlattr *key;
     size_t key_len;
-    struct list packets;
     enum dpif_upcall_type upcall_type;
     struct dpif_flow_stats stats;
 
     struct xlate_out xout;
-
-    struct list upcalls;
 };
 
 struct flow_miss_batch {
@@ -106,6 +104,11 @@ struct flow_miss_batch {
     struct hmap misses;
 
     unsigned int reval_seq;
+
+    /* Flow misses refer to the memory held by "struct upcall"s,
+     * so we need to keep track of the upcalls to be able to
+     * free them when done. */
+    struct list upcalls;        /* Contains "struct upcall"s. */
 };
 
 struct flow_miss_batch *flow_miss_batch_next(struct udpif *);
index b91b3df..80874b8 100644 (file)
@@ -175,7 +175,6 @@ struct subfacet {
     struct facet *facet;        /* Owning facet. */
     struct dpif_backer *backer; /* Owning backer. */
 
-    enum odp_key_fitness key_fitness;
     struct nlattr *key;
     int key_len;
 
@@ -1079,8 +1078,6 @@ dealloc(struct ofproto *ofproto_)
 static void
 close_dpif_backer(struct dpif_backer *backer)
 {
-    struct shash_node *node;
-
     ovs_assert(backer->refcount > 0);
 
     if (--backer->refcount) {
@@ -1090,13 +1087,13 @@ close_dpif_backer(struct dpif_backer *backer)
     drop_key_clear(backer);
     hmap_destroy(&backer->drop_keys);
 
+    udpif_destroy(backer->udpif);
+
     simap_destroy(&backer->tnl_backers);
     ovs_rwlock_destroy(&backer->odp_to_ofport_lock);
     hmap_destroy(&backer->odp_to_ofport_map);
-    node = shash_find(&all_dpif_backers, backer->type);
+    shash_find_and_delete(&all_dpif_backers, backer->type);
     free(backer->type);
-    shash_delete(&all_dpif_backers, node);
-    udpif_destroy(backer->udpif);
     dpif_close(backer->dpif);
 
     ovs_assert(hmap_is_empty(&backer->subfacets));
@@ -3244,7 +3241,7 @@ flow_miss_should_make_facet(struct flow_miss *miss)
 
     hash = flow_hash_in_wildcards(&miss->flow, &miss->xout.wc, 0);
     return governor_should_install_flow(backer->governor, hash,
-                                        list_size(&miss->packets));
+                                        miss->stats.n_packets);
 }
 
 /* Handles 'miss', which matches 'facet'.  May add any required datapath
@@ -3322,7 +3319,7 @@ handle_flow_miss(struct flow_miss *miss, struct flow_miss_op *ops,
 {
     struct facet *facet;
 
-    miss->ofproto->n_missed += list_size(&miss->packets);
+    miss->ofproto->n_missed += miss->stats.n_packets;
 
     facet = facet_lookup_valid(miss->ofproto, &miss->flow);
     if (!facet) {
@@ -4502,7 +4499,6 @@ static struct subfacet *
 subfacet_create(struct facet *facet, struct flow_miss *miss)
 {
     struct dpif_backer *backer = miss->ofproto->backer;
-    enum odp_key_fitness key_fitness = miss->key_fitness;
     const struct nlattr *key = miss->key;
     size_t key_len = miss->key_len;
     uint32_t key_hash;
@@ -4530,7 +4526,6 @@ subfacet_create(struct facet *facet, struct flow_miss *miss)
     hmap_insert(&backer->subfacets, &subfacet->hmap_node, key_hash);
     list_push_back(&facet->subfacets, &subfacet->list_node);
     subfacet->facet = facet;
-    subfacet->key_fitness = key_fitness;
     subfacet->key = xmemdup(key, key_len);
     subfacet->key_len = key_len;
     subfacet->used = miss->stats.used;
index bbb9ba1..03b19c8 100644 (file)
@@ -415,10 +415,11 @@ struct rule_actions {
      * lifetime.  */
     struct ofpact *ofpacts;     /* Sequence of "struct ofpacts". */
     unsigned int ofpacts_len;   /* Size of 'ofpacts', in bytes. */
-    uint32_t meter_id;          /* Non-zero OF meter_id, or zero. */
+    uint32_t provider_meter_id; /* Datapath meter_id, or UINT32_MAX. */
 };
 
-struct rule_actions *rule_actions_create(const struct ofpact *, size_t);
+struct rule_actions *rule_actions_create(const struct ofproto *,
+                                         const struct ofpact *, size_t);
 void rule_actions_ref(struct rule_actions *);
 void rule_actions_unref(struct rule_actions *);
 
@@ -1648,10 +1649,9 @@ struct ofproto_class {
      * implementation.
      *
      * If '*id' is a value other than UINT32_MAX, modifies the existing meter
-     * with that meter provider ID to have configuration 'config'.  On failure,
-     * the existing meter configuration is left intact.  Regardless of success,
-     * any change to '*id' updates the provider meter id used for this
-     * meter. */
+     * with that meter provider ID to have configuration 'config', while
+     * leaving '*id' unchanged.  On failure, the existing meter configuration
+     * is left intact. */
     enum ofperr (*meter_set)(struct ofproto *ofproto, ofproto_meter_id *id,
                              const struct ofputil_meter_config *config);
 
index 1127900..ae39283 100644 (file)
@@ -2443,10 +2443,14 @@ ofproto_rule_destroy__(struct rule *rule)
     rule->ofproto->ofproto_class->rule_dealloc(rule);
 }
 
+static uint32_t get_provider_meter_id(const struct ofproto *,
+                                      uint32_t of_meter_id);
+
 /* Creates and returns a new 'struct rule_actions', with a ref_count of 1,
  * whose actions are a copy of from the 'ofpacts_len' bytes of 'ofpacts'. */
 struct rule_actions *
-rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len)
+rule_actions_create(const struct ofproto *ofproto,
+                    const struct ofpact *ofpacts, size_t ofpacts_len)
 {
     struct rule_actions *actions;
 
@@ -2454,7 +2458,10 @@ rule_actions_create(const struct ofpact *ofpacts, size_t ofpacts_len)
     atomic_init(&actions->ref_count, 1);
     actions->ofpacts = xmemdup(ofpacts, ofpacts_len);
     actions->ofpacts_len = ofpacts_len;
-    actions->meter_id = ofpacts_get_meter(ofpacts, ofpacts_len);
+    actions->provider_meter_id
+        = get_provider_meter_id(ofproto,
+                                ofpacts_get_meter(ofpacts, ofpacts_len));
+
     return actions;
 }
 
@@ -2480,6 +2487,7 @@ rule_actions_unref(struct rule_actions *actions)
 
         atomic_sub(&actions->ref_count, 1, &orig);
         if (orig == 1) {
+            free(actions->ofpacts);
             free(actions);
         } else {
             ovs_assert(orig != 0);
@@ -2744,7 +2752,7 @@ ofproto_check_ofpacts(struct ofproto *ofproto,
     }
 
     mid = ofpacts_get_meter(ofpacts, ofpacts_len);
-    if (mid && ofproto_get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
+    if (mid && get_provider_meter_id(ofproto, mid) == UINT32_MAX) {
         return OFPERR_OFPMMFC_INVALID_METER;
     }
     return 0;
@@ -2923,8 +2931,8 @@ handle_table_stats_request(struct ofconn *ofconn,
         ots[i].apply_actions = htonl(OFPAT11_OUTPUT);
         ots[i].write_setfields = htonll(OFPXMT13_MASK);
         ots[i].apply_setfields = htonll(OFPXMT13_MASK);
-        ots[i].metadata_match = htonll(UINT64_MAX);
-        ots[i].metadata_write = htonll(UINT64_MAX);
+        ots[i].metadata_match = OVS_BE64_MAX;
+        ots[i].metadata_write = OVS_BE64_MAX;
         ots[i].instructions = htonl(OFPIT11_ALL);
         ots[i].config = htonl(OFPTC11_TABLE_MISS_MASK);
         ots[i].max_entries = htonl(1000000); /* An arbitrary big number. */
@@ -3232,7 +3240,13 @@ collect_rule(struct rule *rule, const struct rule_criteria *c,
              struct rule_collection *rules)
     OVS_REQUIRES(ofproto_mutex)
 {
-    if (ofproto_rule_is_hidden(rule)) {
+    /* We ordinarily want to skip hidden rules, but there has to be a way for
+     * code internal to OVS to modify and delete them, so if the criteria
+     * specify a priority that can only be for a hidden flow, then allow hidden
+     * rules to be selected.  (This doesn't allow OpenFlow clients to meddle
+     * with hidden flows because OpenFlow uses only a 16-bit field to specify
+     * priority.) */
+    if (ofproto_rule_is_hidden(rule) && c->cr.priority <= UINT16_MAX) {
         return 0;
     } else if (rule->pending) {
         return OFPROTO_POSTPONE;
@@ -3271,7 +3285,7 @@ collect_rules_loose(struct ofproto *ofproto,
         goto exit;
     }
 
-    if (criteria->cookie_mask == htonll(UINT64_MAX)) {
+    if (criteria->cookie_mask == OVS_BE64_MAX) {
         struct rule *rule;
 
         HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node,
@@ -3332,7 +3346,7 @@ collect_rules_strict(struct ofproto *ofproto,
         goto exit;
     }
 
-    if (criteria->cookie_mask == htonll(UINT64_MAX)) {
+    if (criteria->cookie_mask == OVS_BE64_MAX) {
         struct rule *rule;
 
         HINDEX_FOR_EACH_WITH_HASH (rule, cookie_node,
@@ -3476,7 +3490,6 @@ flow_stats_ds(struct rule *rule, struct ds *results)
         ds_put_format(results, "table_id=%"PRIu8", ", rule->table_id);
     }
     ds_put_format(results, "duration=%llds, ", (time_msec() - created) / 1000);
-    ds_put_format(results, "priority=%u, ", rule->cr.priority);
     ds_put_format(results, "n_packets=%"PRIu64", ", packet_count);
     ds_put_format(results, "n_bytes=%"PRIu64", ", byte_count);
     cls_rule_format(&rule->cr, results);
@@ -3898,7 +3911,7 @@ add_flow(struct ofproto *ofproto, struct ofconn *ofconn,
 
     *CONST_CAST(uint8_t *, &rule->table_id) = table - ofproto->tables;
     rule->flags = fm->flags & OFPUTIL_FF_STATE;
-    rule->actions = rule_actions_create(fm->ofpacts, fm->ofpacts_len);
+    rule->actions = rule_actions_create(ofproto, fm->ofpacts, fm->ofpacts_len);
     list_init(&rule->meter_list_node);
     rule->eviction_group = NULL;
     list_init(&rule->expirable);
@@ -3975,7 +3988,7 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
 
         op = ofoperation_create(group, rule, type, 0);
 
-        if (fm->modify_cookie && fm->new_cookie != htonll(UINT64_MAX)) {
+        if (fm->modify_cookie && fm->new_cookie != OVS_BE64_MAX) {
             ofproto_rule_change_cookie(ofproto, rule, fm->new_cookie);
         }
         if (type == OFOPERATION_REPLACE) {
@@ -3999,7 +4012,8 @@ modify_flows__(struct ofproto *ofproto, struct ofconn *ofconn,
             struct rule_actions *new_actions;
 
             op->actions = rule->actions;
-            new_actions = rule_actions_create(fm->ofpacts, fm->ofpacts_len);
+            new_actions = rule_actions_create(ofproto,
+                                              fm->ofpacts, fm->ofpacts_len);
 
             ovs_mutex_lock(&rule->mutex);
             rule->actions = new_actions;
@@ -4021,7 +4035,7 @@ modify_flows_add(struct ofproto *ofproto, struct ofconn *ofconn,
                  struct ofputil_flow_mod *fm, const struct ofp_header *request)
     OVS_REQUIRES(ofproto_mutex)
 {
-    if (fm->cookie_mask != htonll(0) || fm->new_cookie == htonll(UINT64_MAX)) {
+    if (fm->cookie_mask != htonll(0) || fm->new_cookie == OVS_BE64_MAX) {
         return 0;
     }
     return add_flow(ofproto, ofconn, fm, request);
@@ -4842,13 +4856,10 @@ struct meter {
 /*
  * This is used in instruction validation at flow set-up time,
  * as flows may not use non-existing meters.
- * This is also used by ofproto-providers to translate OpenFlow meter_ids
- * in METER instructions to the corresponding provider meter IDs.
  * Return value of UINT32_MAX signifies an invalid meter.
  */
-uint32_t
-ofproto_get_provider_meter_id(const struct ofproto * ofproto,
-                              uint32_t of_meter_id)
+static uint32_t
+get_provider_meter_id(const struct ofproto *ofproto, uint32_t of_meter_id)
 {
     if (of_meter_id && of_meter_id <= ofproto->meter_features.max_meters) {
         const struct meter *meter = ofproto->meters[of_meter_id];
@@ -4920,7 +4931,7 @@ handle_add_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm)
         ovs_assert(provider_meter_id.uint32 != UINT32_MAX);
         *meterp = meter_create(&mm->meter, provider_meter_id);
     }
-    return 0;
+    return error;
 }
 
 static enum ofperr
@@ -4928,15 +4939,17 @@ handle_modify_meter(struct ofproto *ofproto, struct ofputil_meter_mod *mm)
 {
     struct meter *meter = ofproto->meters[mm->meter.meter_id];
     enum ofperr error;
+    uint32_t provider_meter_id;
 
     if (!meter) {
         return OFPERR_OFPMMFC_UNKNOWN_METER;
     }
 
+    provider_meter_id = meter->provider_meter_id.uint32;
     error = ofproto->ofproto_class->meter_set(ofproto,
                                               &meter->provider_meter_id,
                                               &mm->meter);
-    ovs_assert(meter->provider_meter_id.uint32 != UINT32_MAX);
+    ovs_assert(meter->provider_meter_id.uint32 == provider_meter_id);
     if (!error) {
         meter_update(meter, &mm->meter);
     }
@@ -6533,8 +6546,10 @@ oftable_insert_rule(struct rule *rule)
 
     cookies_insert(ofproto, rule);
 
-    if (rule->actions->meter_id) {
-        struct meter *meter = ofproto->meters[rule->actions->meter_id];
+    if (rule->actions->provider_meter_id != UINT32_MAX) {
+        uint32_t meter_id = ofpacts_get_meter(rule->actions->ofpacts,
+                                              rule->actions->ofpacts_len);
+        struct meter *meter = ofproto->meters[meter_id];
         list_insert(&meter->rules, &rule->meter_list_node);
     }
     ovs_rwlock_wrlock(&table->cls.rwlock);
index 9adda2c..c42b068 100644 (file)
@@ -434,9 +434,6 @@ bool ofproto_has_vlan_usage_changed(const struct ofproto *);
 int ofproto_port_set_realdev(struct ofproto *, ofp_port_t vlandev_ofp_port,
                              ofp_port_t realdev_ofp_port, int vid);
 
-uint32_t ofproto_get_provider_meter_id(const struct ofproto *,
-                                       uint32_t of_meter_id);
-
 #ifdef  __cplusplus
 }
 #endif
index 202358b..b238cd0 100644 (file)
@@ -67,7 +67,7 @@ static struct hmap *ofport_map OVS_GUARDED_BY(rwlock) = &ofport_map__;
 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
 static struct vlog_rate_limit dbg_rl = VLOG_RATE_LIMIT_INIT(60, 60);
 
-static struct tnl_port *tnl_find(struct tnl_match *) OVS_REQ_RDLOCK(rwlock);
+static struct tnl_port *tnl_find(const struct flow *) OVS_REQ_RDLOCK(rwlock);
 static struct tnl_port *tnl_find_exact(struct tnl_match *)
     OVS_REQ_RDLOCK(rwlock);
 static struct tnl_port *tnl_find_ofport(const struct ofport_dpif *)
@@ -209,24 +209,15 @@ tnl_port_receive(const struct flow *flow) OVS_EXCLUDED(rwlock)
     char *pre_flow_str = NULL;
     const struct ofport_dpif *ofport;
     struct tnl_port *tnl_port;
-    struct tnl_match match;
-
-    memset(&match, 0, sizeof match);
-    match.odp_port = flow->in_port.odp_port;
-    match.ip_src = flow->tunnel.ip_dst;
-    match.ip_dst = flow->tunnel.ip_src;
-    match.in_key = flow->tunnel.tun_id;
-    match.pkt_mark = flow->pkt_mark;
 
     ovs_rwlock_rdlock(&rwlock);
-    tnl_port = tnl_find(&match);
+    tnl_port = tnl_find(flow);
     ofport = tnl_port ? tnl_port->ofport : NULL;
     if (!tnl_port) {
-        struct ds ds = DS_EMPTY_INITIALIZER;
+        char *flow_str = flow_to_string(flow);
 
-        tnl_match_fmt(&match, &ds);
-        VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", ds_cstr(&ds));
-        ds_destroy(&ds);
+        VLOG_WARN_RL(&rl, "receive tunnel port not found (%s)", flow_str);
+        free(flow_str);
         goto out;
     }
 
@@ -413,54 +404,55 @@ tnl_find_exact(struct tnl_match *match) OVS_REQ_RDLOCK(rwlock)
     return NULL;
 }
 
+/* Returns the tnl_port that is the best match for the tunnel data in 'flow',
+ * or NULL if no tnl_port matches 'flow'. */
 static struct tnl_port *
-tnl_find(struct tnl_match *match_) OVS_REQ_RDLOCK(rwlock)
+tnl_find(const struct flow *flow) OVS_REQ_RDLOCK(rwlock)
 {
-    struct tnl_match match = *match_;
-    struct tnl_port *tnl_port;
+    enum ip_src_type {
+        IP_SRC_CFG,             /* ip_src must equal configured address. */
+        IP_SRC_ANY,             /* Any ip_src is acceptable. */
+        IP_SRC_FLOW             /* ip_src is handled in flow table. */
+    };
+
+    struct tnl_match_pattern {
+        bool in_key_flow;
+        bool ip_dst_flow;
+        enum ip_src_type ip_src;
+    };
+
+    static const struct tnl_match_pattern patterns[] = {
+        { false, false, IP_SRC_CFG },  /* remote_ip, local_ip, in_key. */
+        { false, false, IP_SRC_ANY },  /* remote_ip, in_key. */
+        { true,  false, IP_SRC_CFG },  /* remote_ip, local_ip. */
+        { true,  false, IP_SRC_ANY },  /* remote_ip. */
+        { true,  true,  IP_SRC_ANY },  /* Flow-based remote. */
+        { true,  true,  IP_SRC_FLOW }, /* Flow-based everything. */
+    };
+
+    const struct tnl_match_pattern *p;
+    struct tnl_match match;
 
-    /* remote_ip, local_ip, in_key */
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
-    }
+    memset(&match, 0, sizeof match);
+    match.odp_port = flow->in_port.odp_port;
+    match.pkt_mark = flow->pkt_mark;
 
-    /* remote_ip, in_key */
-    match.ip_src = 0;
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
-    }
-    match.ip_src = match_->ip_src;
+    for (p = patterns; p < &patterns[ARRAY_SIZE(patterns)]; p++) {
+        struct tnl_port *tnl_port;
 
-    /* remote_ip, local_ip */
-    match.in_key = 0;
-    match.in_key_flow = true;
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
-    }
+        match.in_key_flow = p->in_key_flow;
+        match.in_key = p->in_key_flow ? 0 : flow->tunnel.tun_id;
 
-    /* remote_ip */
-    match.ip_src = 0;
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
-    }
+        match.ip_dst_flow = p->ip_dst_flow;
+        match.ip_dst = p->ip_dst_flow ? 0 : flow->tunnel.ip_src;
 
-    /* Flow-based remote */
-    match.ip_dst = 0;
-    match.ip_dst_flow = true;
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
-    }
+        match.ip_src_flow = p->ip_src == IP_SRC_FLOW;
+        match.ip_src = p->ip_src == IP_SRC_CFG ? flow->tunnel.ip_dst : 0;
 
-    /* Flow-based everything */
-    match.ip_src_flow = true;
-    tnl_port = tnl_find_exact(&match);
-    if (tnl_port) {
-        return tnl_port;
+        tnl_port = tnl_find_exact(&match);
+        if (tnl_port) {
+            return tnl_port;
+        }
     }
 
     return NULL;
index b02d5a3..7c8ac6f 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+/* Copyright (c) 2009, 2010, 2011, 2012, 2013 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -68,12 +68,10 @@ static struct ovsdb_error *ovsdb_file_open__(const char *file_name,
                                              bool read_only, struct ovsdb **,
                                              struct ovsdb_file **);
 static struct ovsdb_error *ovsdb_file_txn_from_json(
-    struct ovsdb *, const struct json *, bool converting,
-    long long int *date, struct ovsdb_txn **);
+    struct ovsdb *, const struct json *, bool converting, struct ovsdb_txn **);
 static struct ovsdb_error *ovsdb_file_create(struct ovsdb *,
                                              struct ovsdb_log *,
                                              const char *file_name,
-                                             long long int oldest_commit,
                                              unsigned int n_transactions,
                                              struct ovsdb_file **filep);
 
@@ -184,7 +182,6 @@ ovsdb_file_open__(const char *file_name,
                   struct ovsdb_file **filep)
 {
     enum ovsdb_log_open_mode open_mode;
-    long long int oldest_commit;
     unsigned int n_transactions;
     struct ovsdb_schema *schema = NULL;
     struct ovsdb_error *error;
@@ -204,14 +201,12 @@ ovsdb_file_open__(const char *file_name,
 
     db = ovsdb_create(schema ? schema : ovsdb_schema_clone(alternate_schema));
 
-    oldest_commit = LLONG_MAX;
     n_transactions = 0;
     while ((error = ovsdb_log_read(log, &json)) == NULL && json) {
         struct ovsdb_txn *txn;
-        long long int date;
 
         error = ovsdb_file_txn_from_json(db, json, alternate_schema != NULL,
-                                         &date, &txn);
+                                         &txn);
         json_destroy(json);
         if (error) {
             ovsdb_log_unread(log);
@@ -219,10 +214,6 @@ ovsdb_file_open__(const char *file_name,
         }
 
         n_transactions++;
-        if (date < oldest_commit) {
-            oldest_commit = date;
-        }
-
         error = ovsdb_txn_commit(txn, false);
         if (error) {
             ovsdb_log_unread(log);
@@ -243,8 +234,7 @@ ovsdb_file_open__(const char *file_name,
     if (!read_only) {
         struct ovsdb_file *file;
 
-        error = ovsdb_file_create(db, log, file_name, oldest_commit,
-                                  n_transactions, &file);
+        error = ovsdb_file_create(db, log, file_name, n_transactions, &file);
         if (error) {
             goto error;
         }
@@ -376,22 +366,16 @@ ovsdb_file_txn_table_from_json(struct ovsdb_txn *txn,
  *
  * If 'converting' is true, then unknown table and column names are ignored
  * (which can ease upgrading and downgrading schemas); otherwise, they are
- * treated as errors.
- *
- * If successful, the date associated with the transaction, as the number of
- * milliseconds since the epoch, is stored in '*date'.  If the transaction does
- * not include a date, LLONG_MAX is stored. */
+ * treated as errors. */
 static struct ovsdb_error *
 ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json,
-                         bool converting, long long int *date,
-                         struct ovsdb_txn **txnp)
+                         bool converting, struct ovsdb_txn **txnp)
 {
     struct ovsdb_error *error;
     struct shash_node *node;
     struct ovsdb_txn *txn;
 
     *txnp = NULL;
-    *date = LLONG_MAX;
 
     if (json->type != JSON_OBJECT) {
         return ovsdb_syntax_error(json, NULL, "object expected");
@@ -407,7 +391,6 @@ ovsdb_file_txn_from_json(struct ovsdb *db, const struct json *json,
         if (!table) {
             if (!strcmp(table_name, "_date")
                 && node_json->type == JSON_INTEGER) {
-                *date = json_integer(node_json);
                 continue;
             } else if (!strcmp(table_name, "_comment") || converting) {
                 continue;
@@ -514,7 +497,7 @@ struct ovsdb_file {
     struct ovsdb *db;
     struct ovsdb_log *log;
     char *file_name;
-    long long int oldest_commit;
+    long long int last_compact;
     long long int next_compact;
     unsigned int n_transactions;
 };
@@ -524,11 +507,9 @@ static const struct ovsdb_replica_class ovsdb_file_class;
 static struct ovsdb_error *
 ovsdb_file_create(struct ovsdb *db, struct ovsdb_log *log,
                   const char *file_name,
-                  long long int oldest_commit,
                   unsigned int n_transactions,
                   struct ovsdb_file **filep)
 {
-    long long int now = time_msec();
     struct ovsdb_file *file;
     char *deref_name;
     char *abs_name;
@@ -549,8 +530,8 @@ ovsdb_file_create(struct ovsdb *db, struct ovsdb_log *log,
     file->db = db;
     file->log = log;
     file->file_name = abs_name;
-    file->oldest_commit = MIN(oldest_commit, now);
-    file->next_compact = file->oldest_commit + COMPACT_MIN_MSEC;
+    file->last_compact = time_msec();
+    file->next_compact = file->last_compact + COMPACT_MIN_MSEC;
     file->n_transactions = n_transactions;
     ovsdb_add_replica(db, &file->replica);
 
@@ -634,7 +615,7 @@ ovsdb_file_compact(struct ovsdb_file *file)
 
     comment = xasprintf("compacting database online "
                         "(%.3f seconds old, %u transactions, %llu bytes)",
-                        (time_msec() - file->oldest_commit) / 1000.0,
+                        (time_wall_msec() - file->last_compact) / 1000.0,
                         file->n_transactions,
                         (unsigned long long) ovsdb_log_get_offset(file->log));
     VLOG_INFO("%s: %s", file->file_name, comment);
@@ -679,8 +660,8 @@ exit:
     if (!error) {
         ovsdb_log_close(file->log);
         file->log = new_log;
-        file->oldest_commit = time_msec();
-        file->next_compact = file->oldest_commit + COMPACT_MIN_MSEC;
+        file->last_compact = time_msec();
+        file->next_compact = file->last_compact + COMPACT_MIN_MSEC;
         file->n_transactions = 1;
     } else {
         ovsdb_log_close(new_log);
@@ -787,7 +768,7 @@ ovsdb_file_txn_commit(struct json *json, const char *comment,
     if (comment) {
         json_object_put_string(json, "_comment", comment);
     }
-    json_object_put(json, "_date", json_integer_create(time_wall()));
+    json_object_put(json, "_date", json_integer_create(time_wall_msec()));
 
     error = ovsdb_log_write(log, json);
     json_destroy(json);
index 8670127..077e7f5 100644 (file)
@@ -518,9 +518,15 @@ do_show_log(int argc, char *argv[])
 
             date = shash_find_data(json_object(json), "_date");
             if (date && date->type == JSON_INTEGER) {
-                time_t t = json_integer(date);
-                char *s = xastrftime_msec(" %Y-%m-%d %H:%M:%S",
-                                          t * 1000LL, true);
+                long long int t = json_integer(date);
+                char *s;
+
+                if (t < INT32_MAX) {
+                    /* Older versions of ovsdb wrote timestamps in seconds. */
+                    t *= 1000;
+                }
+
+               s = xastrftime_msec(" %Y-%m-%d %H:%M:%S.###", t, true);
                 fputs(s, stdout);
                 free(s);
             }
index 14679d9..8447c0c 100644 (file)
@@ -1,5 +1,5 @@
 
-# Copyright (c) 2011, 2012 Nicira, Inc.
+# Copyright (c) 2011, 2012, 2013 Nicira, Inc.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -61,7 +61,7 @@ class Vlog:
             return
 
         dt = datetime.datetime.utcnow();
-        now = dt.strftime("%Y-%m-%dT%H:%M:%S.%%iZ") % (dt.microsecond/1000)
+        now = dt.strftime("%Y-%m-%dT%H:%M:%S.%%03iZ") % (dt.microsecond/1000)
         syslog_message = ("%s|%s|%s|%s"
                            % (Vlog.__msg_num, self.name, level, message))
 
index 0fd5200..f77cd3a 100644 (file)
@@ -114,6 +114,7 @@ exit 0
 /usr/bin/ovs-appctl
 /usr/bin/ovs-benchmark
 /usr/bin/ovs-dpctl
+/usr/bin/ovs-dpctl-top
 /usr/bin/ovs-ofctl
 /usr/bin/ovs-parse-backtrace
 /usr/bin/ovs-pcap
@@ -137,6 +138,7 @@ exit 0
 /usr/share/man/man8/ovs-bugtool.8.gz
 /usr/share/man/man8/ovs-ctl.8.gz
 /usr/share/man/man8/ovs-dpctl.8.gz
+/usr/share/man/man8/ovs-dpctl-top.8.gz
 /usr/share/man/man8/ovs-ofctl.8.gz
 /usr/share/man/man8/ovs-parse-backtrace.8.gz
 /usr/share/man/man8/ovs-pki.8.gz
index 60d3640..8f51a65 100644 (file)
@@ -22,6 +22,7 @@ TESTSUITE_AT = \
        tests/odp.at \
        tests/multipath.at \
        tests/bfd.at \
+       tests/cfm.at \
        tests/lacp.at \
        tests/learn.at \
        tests/vconn.at \
diff --git a/tests/cfm.at b/tests/cfm.at
new file mode 100644 (file)
index 0000000..638e03f
--- /dev/null
@@ -0,0 +1,57 @@
+AT_BANNER([cfm])
+
+m4_define([CFM_CHECK_EXTENDED], [
+AT_CHECK([ovs-appctl cfm/show $1 | sed -e '/next CCM tx:/d' | sed -e '/next fault check:/d' | sed -e  '/recv since check:/d'],[0],
+[dnl
+---- $1 ----
+MPID $2: extended
+       average health: $3
+       opstate: $4
+       remote_opstate: $5
+       interval: $6
+Remote MPID $7
+       opstate: $8
+])
+])
+
+# test cfm under demand mode.
+AT_SETUP([cfm - demand mode])
+#Create 2 bridges connected by patch ports and enable BFD
+OVS_VSWITCHD_START([add-br br1 -- \
+                    set bridge br1 datapath-type=dummy \
+                    other-config:hwaddr=aa:55:aa:56:00:00 -- \
+                    add-port br1 p1 -- set Interface p1 type=patch \
+                    options:peer=p0 -- \
+                    add-port br0 p0 -- set Interface p0 type=patch \
+                    options:peer=p1 -- \
+                    set Interface p0 cfm_mpid=1 other_config:cfm_interval=300 other_config:cfm_extended=true -- \
+                    set Interface p1 cfm_mpid=2 other_config:cfm_interval=300 other_config:cfm_extended=true ])
+
+ovs-appctl time/stop
+# wait for a while to stablize cfm.
+for i in `seq 0 100`; do ovs-appctl time/warp 100; done
+CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up])
+CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up])
+
+# turn on demand mode on one end.
+AT_CHECK([ovs-vsctl set interface p0 other_config:cfm_demand=true])
+
+# cfm should never go down.
+for i in `seq 0 100`
+do
+    ovs-appctl time/warp 100
+    CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up])
+    CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up])
+done
+
+# turn on demand mode on the other end.
+AT_CHECK([ovs-vsctl set interface p1 other_config:cfm_demand=true])
+for i in `seq 0 100`
+do
+    ovs-appctl time/warp 100
+    CFM_CHECK_EXTENDED([p0], [1], [100], [up], [up], [300ms], [2], [up])
+    CFM_CHECK_EXTENDED([p1], [2], [100], [up], [up], [300ms], [1], [up])
+done
+
+OVS_VSWITCHD_STOP
+AT_CLEANUP
index 8a01f0a..c87d9c4 100644 (file)
@@ -243,7 +243,7 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
 AT_CHECK([ovsdb-client list-tables unix:socket constraints], [0], [ignore], [ignore])
 AT_CLEANUP
 
-AT_SETUP([ovsdb-server/add-db and remove-db with --monitor])
+AT_SETUP([ovsdb-server/add-db with --monitor])
 AT_KEYWORDS([ovsdb server positive])
 # Start ovsdb-server, initially with one db.
 OVS_RUNDIR=`pwd`; export OVS_RUNDIR
@@ -273,8 +273,21 @@ AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
   [0], [constraints
 ordinals
 ])
+AT_CLEANUP
+
+AT_SETUP([ovsdb-server/add-db and remove-db with --monitor])
+AT_KEYWORDS([ovsdb server positive])
+# Start ovsdb-server, initially with one db.
+OVS_RUNDIR=`pwd`; export OVS_RUNDIR
+OVS_LOGDIR=`pwd`; export OVS_LOGDIR
+ordinal_schema > schema
+AT_CHECK([ovsdb-tool create db1 schema], [0], [ignore], [ignore])
+constraint_schema > schema2
+AT_CHECK([ovsdb-tool create db2 schema2], [0], [ignore], [ignore])
+ON_EXIT([kill `cat *.pid`])
+AT_CHECK([ovsdb-server -v -vvlog:off --monitor --detach --no-chdir --pidfile --log-file db1 db2])
 
-# Remove the recently added database.
+# Remove the second database.
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/remove-db constraints])
 AT_CHECK([ovs-appctl -t ovsdb-server ovsdb-server/list-dbs],
   [0], [ordinals
index 1d3d2d4..1e04550 100644 (file)
@@ -528,29 +528,29 @@ make_rule(int wc_fields, unsigned int priority, int value_pat)
                values[f_idx][value_idx], f->len);
 
         if (f_idx == CLS_F_IDX_NW_SRC) {
-            match.wc.masks.nw_src = htonl(UINT32_MAX);
+            match.wc.masks.nw_src = OVS_BE32_MAX;
         } else if (f_idx == CLS_F_IDX_NW_DST) {
-            match.wc.masks.nw_dst = htonl(UINT32_MAX);
+            match.wc.masks.nw_dst = OVS_BE32_MAX;
         } else if (f_idx == CLS_F_IDX_TP_SRC) {
-            match.wc.masks.tp_src = htons(UINT16_MAX);
+            match.wc.masks.tp_src = OVS_BE16_MAX;
         } else if (f_idx == CLS_F_IDX_TP_DST) {
-            match.wc.masks.tp_dst = htons(UINT16_MAX);
+            match.wc.masks.tp_dst = OVS_BE16_MAX;
         } else if (f_idx == CLS_F_IDX_DL_SRC) {
             memset(match.wc.masks.dl_src, 0xff, ETH_ADDR_LEN);
         } else if (f_idx == CLS_F_IDX_DL_DST) {
             memset(match.wc.masks.dl_dst, 0xff, ETH_ADDR_LEN);
         } else if (f_idx == CLS_F_IDX_VLAN_TCI) {
-            match.wc.masks.vlan_tci = htons(UINT16_MAX);
+            match.wc.masks.vlan_tci = OVS_BE16_MAX;
         } else if (f_idx == CLS_F_IDX_TUN_ID) {
-            match.wc.masks.tunnel.tun_id = htonll(UINT64_MAX);
+            match.wc.masks.tunnel.tun_id = OVS_BE64_MAX;
         } else if (f_idx == CLS_F_IDX_METADATA) {
-            match.wc.masks.metadata = htonll(UINT64_MAX);
+            match.wc.masks.metadata = OVS_BE64_MAX;
         } else if (f_idx == CLS_F_IDX_NW_DSCP) {
             match.wc.masks.nw_tos |= IP_DSCP_MASK;
         } else if (f_idx == CLS_F_IDX_NW_PROTO) {
             match.wc.masks.nw_proto = UINT8_MAX;
         } else if (f_idx == CLS_F_IDX_DL_TYPE) {
-            match.wc.masks.dl_type = htons(UINT16_MAX);
+            match.wc.masks.dl_type = OVS_BE16_MAX;
         } else if (f_idx == CLS_F_IDX_IN_PORT) {
             match.wc.masks.in_port.ofp_port = u16_to_ofp(UINT16_MAX);
         } else {
index 6bf8ed5..6ed2f77 100644 (file)
@@ -319,7 +319,7 @@ test_bitwise_one(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
             ovs_be64 expect;
 
             if (n_bits == 64) {
-                expect = htonll(UINT64_MAX);
+                expect = OVS_BE64_MAX;
             } else {
                 uint64_t mask = (UINT64_C(1) << n_bits) - 1;
                 expect = orig_dst | htonll(mask << dst_ofs);
index 43efc09..9e16f94 100644 (file)
@@ -68,6 +68,7 @@ m4_include([tests/ovsdb-macros.at])
 m4_include([tests/ofproto-macros.at])
 
 m4_include([tests/bfd.at])
+m4_include([tests/cfm.at])
 m4_include([tests/lacp.at])
 m4_include([tests/library.at])
 m4_include([tests/heap.at])
index a0bd97f..4972685 100644 (file)
@@ -13,6 +13,8 @@
 /ovs-ctl
 /ovs-dpctl
 /ovs-dpctl.8
+/ovs-dpctl-top
+/ovs-dpctl-top.8
 /ovs-l3ping
 /ovs-l3ping.8
 /ovs-lib
index 9f2bb63..ff50a34 100644 (file)
@@ -7,6 +7,7 @@ bin_PROGRAMS += \
 bin_SCRIPTS += utilities/ovs-pki
 if HAVE_PYTHON
 bin_SCRIPTS += \
+       utilities/ovs-dpctl-top \
        utilities/ovs-l3ping \
        utilities/ovs-parse-backtrace \
        utilities/ovs-pcap \
@@ -24,6 +25,7 @@ EXTRA_DIST += \
        utilities/ovs-check-dead-ifs.in \
        utilities/ovs-ctl.in \
        utilities/ovs-dev.py \
+       utilities/ovs-dpctl-top.in \
        utilities/ovs-l3ping.in \
        utilities/ovs-lib.in \
        utilities/ovs-parse-backtrace.in \
@@ -39,6 +41,7 @@ MAN_ROOTS += \
        utilities/ovs-controller.8.in \
        utilities/ovs-ctl.8 \
        utilities/ovs-dpctl.8.in \
+       utilities/ovs-dpctl-top.8.in \
        utilities/ovs-l3ping.8.in \
        utilities/ovs-ofctl.8.in \
        utilities/ovs-parse-backtrace.8 \
@@ -57,6 +60,8 @@ DISTCLEANFILES += \
        utilities/ovs-check-dead-ifs \
        utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
+       utilities/ovs-dpctl-top \
+       utilities/ovs-dpctl-top.8 \
        utilities/ovs-l3ping \
        utilities/ovs-l3ping.8 \
        utilities/ovs-lib \
@@ -80,6 +85,7 @@ man_MANS += \
        utilities/ovs-benchmark.1 \
        utilities/ovs-controller.8 \
        utilities/ovs-dpctl.8 \
+       utilities/ovs-dpctl-top.8 \
        utilities/ovs-l3ping.8 \
        utilities/ovs-ofctl.8 \
        utilities/ovs-parse-backtrace.8 \
diff --git a/utilities/ovs-dpctl-top.8.in b/utilities/ovs-dpctl-top.8.in
new file mode 100644 (file)
index 0000000..410e999
--- /dev/null
@@ -0,0 +1,140 @@
+.de IQ
+.  br
+.  ns
+.  IP "\\$1"
+..
+.TH ovs\-dpctl\-top "8" "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
+.
+.SH NAME
+\fBovs\-dpctl\-top\fR \- Top like behavior for ovs\-dpctl dump\-flows
+.
+.SH SYNOPSIS
+\fBovs\-dpctl\-top\fR [\-h] [\-v] [\-f FLOWFILES] [\-V] [\-s] [\-\-host HOST]
+[\-a | \-\-accumulate] [\-\-accumulate\-decay ACCUMULATEDECAY] [\-d DELAY]
+.
+.SH DESCRIPTION
+.PP
+This program summarizes \fBovs\-dpctl\fR flow content by aggregating the number
+of packets, total bytes and occurrence of the following fields:
+.IP
+\- Datapath in_port
+.IP
+\- Ethernet type
+.IP
+\- Source and destination MAC addresses
+.IP
+\- IP protocol
+.IP
+\- Source and destination IPv4 addresses
+.IP
+\- Source and destination IPv6 addresses
+.IP
+\- UDP and TCP destination port
+.IP
+\- Tunnel source and destination addresses
+.
+.SS "Output shows four values:"
+.IP
+\- FIELDS: the flow fields for example in_port(1).
+.IP
+\- COUNT: the number of lines in the dump\-flow output contain the flow field.
+.IP
+\- PACKETS: the total number of packets containing the flow field.
+.IP
+\- BYTES: the total number of bytes containing the flow field.  If units are
+not present then values are in bytes.
+.IP
+\- AVERAGE: the average packets size (BYTES/PACKET).
+.PP
+.SS "Top Behavior"
+.PP
+While in top mode, the default behavior, the following single character commands
+are supported:
+.IP
+a \- toggles top in accumulate and live mode.  Accumulate mode is described
+below.
+.IP
+s \- toggles which column is used to sort content in decreasing order.  A
+DESC title is placed over the column.
+.IP
+_ \- a space indicating to collect dump\-flow content again
+.IP
+h \- halt output.  Any character will restart sampling
+.IP
+f \- cycle through flow fields
+.IP
+q \- q for quit.
+.PP
+.SS "Accumulate Mode"
+.PP
+There are two supported modes: live and accumulate.  The default is live.
+The parameter \fB\-\-accumulate\fR  or the 'a' character in top mode enables the
+latter.  In live mode, recent dump\-flow content is presented.
+Where as accumulate mode keeps track of the prior historical
+information until the flow is reset not when the flow is purged.  Reset
+flows are determined when the packet count for a flow has decreased from
+its previous sample.  There is one caveat, eventually the system will
+run out of memory if, after the accumulate\-decay period any flows that
+have not been refreshed are purged.  The goal here is to free memory
+of flows that are not active.  Statistics are not decremented.  Their purpose
+is to reflect the overall history of the flow fields.
+.PP
+.SS "Debugging Errors"
+.PP
+Parsing errors are counted and displayed in the status line at the beginning
+of the output.  Use the \fB\-\-verbose\fR option with \fB\-\-script to see
+what output was not parsed, like this:
+.PP
+$ ovs\-dpctl dump\-flows | ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-verbose\fR
+.PP
+Error messages will identify content that failed to parse.
+.PP
+.SS "Access Remote Hosts"
+.PP
+The \fB\-\-host\fR must follow the format user@hostname.  This script simply
+calls \&'ssh user@Hostname' without checking for login credentials therefore
+public keys should be installed on the system identified by hostname, such as:
+.PP
+$ ssh\-copy\-id user@hostname
+.PP
+Consult ssh\-copy\-id man pages for more details.
+.PP
+.SS "Expected usage"
+.PP
+$ ovs\-dpctl\-top
+.PP
+or to run as a script:
+.PP
+$ ovs\-dpctl dump\-flows > dump\-flows.log
+.PP
+$ ovs\-dpctl\-top \fB\-\-script\fR \fB\-\-flow\-file\fR dump\-flows.log
+.SS "OPTIONS"
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+show this help message and exit.
+.TP
+\fB\-v\fR, \fB\-\-version\fR
+show program's version number and exit.
+.TP
+\fB\-f\fR FLOWFILES, \fB\-\-flow\-file\fR FLOWFILES
+file containing flows from ovs\-dpctl dump\-flow.
+.TP
+\fB\-V\fR, \fB\-\-verbose\fR
+enable debug level verbosity.
+.TP
+\fB\-s\fR, \fB\-\-script\fR
+Run from a script (no user interface).
+.TP
+\fB\-\-host\fR HOST
+Specify a user@host for retrieving flows see Accessing
+Remote Hosts for more information.
+.TP
+\fB\-a\fR, \fB\-\-accumulate\fR
+Accumulate dump\-flow content.
+.TP
+\fB\-\-accumulate\-decay\fR ACCUMULATEDECAY
+Decay old accumulated flows.  The default is 5 minutes. A value of 0 disables
+decay.
+.TP
+\fB\-d\fR DELAY, \fB\-\-delay\fR DELAY
+Delay in milliseconds to collect dump\-flow content (sample rate).
diff --git a/utilities/ovs-dpctl-top.in b/utilities/ovs-dpctl-top.in
new file mode 100755 (executable)
index 0000000..f43fdeb
--- /dev/null
@@ -0,0 +1,1687 @@
+#! @PYTHON@
+#
+# Copyright (c) 2013 Nicira, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at:
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# The approximate_size code was copied from
+# http://getpython3.com/diveintopython3/your-first-python-program.html#divingin
+# which is licensed under # "Dive Into Python 3," Copyright 2011 Mark Pilgrim,
+# used under a Creative Commons Attribution-Share-Alike license:
+# http://creativecommons.org/licenses/by-sa/3.0/
+#
+#
+
+"""Top like behavior for ovs-dpctl dump-flows output.
+
+This program summarizes ovs-dpctl flow content by aggregating the number
+of packets, total bytes and occurrence of the following fields:
+
+  - Datapath in_port
+
+  - Ethernet type
+
+  - Source and destination MAC addresses
+
+  - IP protocol
+
+  - Source and destination IPv4 addresses
+
+  - Source and destination IPv6 addresses
+
+  - UDP and TCP destination port
+
+  - Tunnel source and destination addresses
+
+
+Output shows four values:
+  - FIELDS: the flow fields for example in_port(1).
+
+  - PACKETS: the total number of packets containing the flow field.
+
+  - BYTES: the total number of bytes containing the flow field. If units are
+  not present then values are in bytes.
+
+  - AVERAGE: the average packets size (BYTES/PACKET).
+
+  - COUNT: the number of lines in the dump-flow output contain the flow field.
+
+Top Behavior
+
+While in top mode, the default behavior, the following single character
+commands are supported:
+
+  a - toggles top in accumulate and live mode. Accumulate mode is described
+  below.
+
+  s - toggles which column is used to sort content in decreasing order. A
+  DESC title is placed over the column.
+
+  _ - a space indicating to collect dump-flow content again
+
+  h - halt output. Any character will restart sampling
+
+  f - cycle through flow fields
+
+  q - q for quit.
+
+Accumulate Mode
+
+There are two supported modes: live and accumulate. The default is live.
+The parameter --accumulate  or the 'a' character in top mode enables the
+latter. In live mode, recent dump-flow content is presented.
+Where as accumulate mode keeps track of the prior historical
+information until the flow is reset not when the flow is purged. Reset
+flows are determined when the packet count for a flow has decreased from
+its previous sample. There is one caveat, eventually the system will
+run out of memory if, after the accumulate-decay period any flows that
+have not been refreshed are purged. The goal here is to free memory
+of flows that are not active. Statistics are not decremented. Their purpose
+is to reflect the overall history of the flow fields.
+
+
+Debugging Errors
+
+Parsing errors are counted and displayed in the status line at the beginning
+of the output. Use the --verbose option with --script to see what output
+ was not parsed, like this:
+$ ovs-dpctl dump-flows | ovs-dpctl-top --script --verbose
+
+Error messages will identify content that failed to parse.
+
+
+Access Remote Hosts
+
+The --host must follow the format user@hostname. This script simply calls
+'ssh user@Hostname' without checking for login credentials therefore public
+keys should be installed on the system identified by hostname, such as:
+
+$ ssh-copy-id user@hostname
+
+Consult ssh-copy-id man pages for more details.
+
+
+Expected usage
+
+$ ovs-dpctl-top
+
+or to run as a script:
+$ ovs-dpctl dump-flows > dump-flows.log
+$ ovs-dpctl-top --script --flow-file dump-flows.log
+
+"""
+
+# pylint: disable-msg=C0103
+# pylint: disable-msg=C0302
+# pylint: disable-msg=R0902
+# pylint: disable-msg=R0903
+# pylint: disable-msg=R0904
+# pylint: disable-msg=R0912
+# pylint: disable-msg=R0913
+# pylint: disable-msg=R0914
+
+import sys
+import os
+try:
+    ##
+    # Arg parse is not installed on older Python distributions.
+    # ovs ships with a version in the directory mentioned below.
+    import argparse
+except ImportError:
+    sys.path.append(os.path.join("@pkgdatadir@", "python"))
+    import argparse
+import logging
+import re
+import unittest
+import copy
+import curses
+import operator
+import subprocess
+import fcntl
+import struct
+import termios
+import datetime
+import threading
+import time
+import socket
+
+
+##
+# The following two definitions provide the necessary netaddr functionality.
+# Python netaddr module is not part of the core installation. Packaging
+# netaddr was involved and seems inappropriate given that only two
+# methods where used.
+def ipv4_to_network(ip_str):
+    """ Calculate the network given a ipv4/mask value.
+    If a mask is not present simply return ip_str.
+    """
+    pack_length = '!HH'
+    try:
+        (ip, mask) = ip_str.split("/")
+    except ValueError:
+        # just an ip address no mask.
+        return ip_str
+
+    ip_p = socket.inet_pton(socket.AF_INET, ip)
+    ip_t = struct.unpack(pack_length, ip_p)
+    mask_t = struct.unpack(pack_length, socket.inet_pton(socket.AF_INET, mask))
+    network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)]
+
+    return socket.inet_ntop(socket.AF_INET,
+                            struct.pack('!HH', network_n[0], network_n[1]))
+
+
+def ipv6_to_network(ip_str):
+    """ Calculate the network given a ipv6/mask value.
+    If a mask is not present simply return ip_str.
+    """
+    pack_length = '!HHHHHHHH'
+    try:
+        (ip, mask) = ip_str.split("/")
+    except ValueError:
+        # just an ip address no mask.
+        return ip_str
+
+    ip_p = socket.inet_pton(socket.AF_INET6, ip)
+    ip_t = struct.unpack(pack_length, ip_p)
+    mask_t = struct.unpack(pack_length,
+                           socket.inet_pton(socket.AF_INET6, mask))
+    network_n = [ii & jj for (ii, jj) in zip(ip_t, mask_t)]
+
+    return socket.inet_ntop(socket.AF_INET6,
+                            struct.pack(pack_length,
+                                        network_n[0], network_n[1],
+                                        network_n[2], network_n[3],
+                                        network_n[4], network_n[5],
+                                        network_n[6], network_n[7]))
+
+
+##
+# columns displayed
+##
+class Columns:
+    """ Holds column specific content.
+    Titles needs to be less than 8 characters.
+    """
+    VALUE_WIDTH = 9
+    FIELDS = "fields"
+    PACKETS = "packets"
+    COUNT = "count"
+    BYTES = "bytes"
+    AVERAGE = "average"
+
+    def __init__(self):
+        pass
+
+    @staticmethod
+    def assoc_list(obj):
+        """ Return a associated list. """
+        return [(Columns.FIELDS, repr(obj)),
+                (Columns.PACKETS, obj.packets),
+                (Columns.BYTES, obj.bytes),
+                (Columns.COUNT, obj.count),
+                (Columns.AVERAGE, obj.average),
+                ]
+
+
+def element_eth_get(field_type, element, stats_dict):
+    """ Extract eth frame src and dst from a dump-flow element."""
+    fmt = "%s(src=%s,dst=%s)"
+
+    element = fmt % (field_type, element["src"], element["dst"])
+    return SumData(field_type, element, stats_dict["packets"],
+                   stats_dict["bytes"], element)
+
+
+def element_ipv4_get(field_type, element, stats_dict):
+    """ Extract src and dst from a dump-flow element."""
+    fmt = "%s(src=%s,dst=%s)"
+    element_show = fmt % (field_type, element["src"], element["dst"])
+
+    element_key = fmt % (field_type, ipv4_to_network(element["src"]),
+                         ipv4_to_network(element["dst"]))
+
+    return SumData(field_type, element_show, stats_dict["packets"],
+                       stats_dict["bytes"], element_key)
+
+
+def element_tunnel_get(field_type, element, stats_dict):
+    """ Extract src and dst from a tunnel."""
+    return element_ipv4_get(field_type, element, stats_dict)
+
+
+def element_ipv6_get(field_type, element, stats_dict):
+    """ Extract src and dst from a dump-flow element."""
+
+    fmt = "%s(src=%s,dst=%s)"
+    element_show = fmt % (field_type, element["src"], element["dst"])
+
+    element_key = fmt % (field_type, ipv6_to_network(element["src"]),
+                         ipv6_to_network(element["dst"]))
+
+    return SumData(field_type, element_show, stats_dict["packets"],
+                       stats_dict["bytes"], element_key)
+
+
+def element_dst_port_get(field_type, element, stats_dict):
+    """ Extract src and dst from a dump-flow element."""
+    element_key = "%s(dst=%s)" % (field_type, element["dst"])
+    return SumData(field_type, element_key, stats_dict["packets"],
+                   stats_dict["bytes"], element_key)
+
+
+def element_passthrough_get(field_type, element, stats_dict):
+    """ Extract src and dst from a dump-flow element."""
+    element_key = "%s(%s)" % (field_type, element)
+    return SumData(field_type, element_key,
+                   stats_dict["packets"], stats_dict["bytes"], element_key)
+
+
+# pylint: disable-msg=R0903
+class OutputFormat:
+    """ Holds field_type and function to extract element value. """
+    def __init__(self, field_type, generator):
+        self.field_type = field_type
+        self.generator = generator
+
+OUTPUT_FORMAT = [
+    OutputFormat("eth", element_eth_get),
+    OutputFormat("ipv4", element_ipv4_get),
+    OutputFormat("ipv6", element_ipv6_get),
+    OutputFormat("tunnel", element_tunnel_get),
+    OutputFormat("udp", element_dst_port_get),
+    OutputFormat("tcp", element_dst_port_get),
+    OutputFormat("eth_type", element_passthrough_get),
+    OutputFormat("in_port", element_passthrough_get)
+    ]
+
+
+ELEMENT_KEY = {
+    "udp": "udp.dst",
+    "tcp": "tcp.dst"
+    }
+
+
+def top_input_get(args):
+    """ Return subprocess stdout."""
+    cmd = []
+    if (args.host):
+        cmd += ["ssh", args.host]
+    cmd += ["ovs-dpctl", "dump-flows"]
+
+    return subprocess.Popen(cmd, stderr=subprocess.STDOUT,
+                            stdout=subprocess.PIPE).stdout
+
+
+def args_get():
+    """ read program parameters handle any necessary validation of input. """
+
+    parser = argparse.ArgumentParser(
+                          formatter_class=argparse.RawDescriptionHelpFormatter,
+                          description=__doc__)
+    ##
+    # None is a special value indicating to read flows from stdin.
+    # This handles the case
+    #   ovs-dpctl dump-flows | ovs-dpctl-flows.py
+    parser.add_argument("-v", "--version", version="@VERSION@",
+                        action="version", help="show version")
+    parser.add_argument("-f", "--flow-file", dest="flowFiles", default=None,
+                        action="append",
+                        help="file containing flows from ovs-dpctl dump-flow")
+    parser.add_argument("-V", "--verbose", dest="verbose",
+                        default=logging.CRITICAL,
+                        action="store_const", const=logging.DEBUG,
+                        help="enable debug level verbosity")
+    parser.add_argument("-s", "--script", dest="top", action="store_false",
+                        help="Run from a script (no user interface)")
+    parser.add_argument("--host", dest="host",
+                        help="Specify a user@host for retrieving flows see"
+                             "Accessing Remote Hosts for more information")
+
+    parser.add_argument("-a", "--accumulate", dest="accumulate",
+                        action="store_true", default=False,
+                        help="Accumulate dump-flow content")
+    parser.add_argument("--accumulate-decay", dest="accumulateDecay",
+                        default=5.0 * 60, type=float,
+                        help="Decay old accumulated flows. "
+                        "The default is 5 minutes. "
+                        "A value of 0 disables decay.")
+    parser.add_argument("-d", "--delay", dest="delay", type=int,
+                        default=1000,
+                        help="Delay in milliseconds to collect dump-flow "
+                             "content (sample rate).")
+
+    args = parser.parse_args()
+
+    logging.basicConfig(level=args.verbose)
+
+    return args
+
+###
+# Code to parse a single line in dump-flow
+###
+# key(values)
+FIELDS_CMPND = re.compile("([\w]+)\((.+)\)")
+# key:value
+FIELDS_CMPND_ELEMENT = re.compile("([\w:]+)=([/\.\w:]+)")
+FIELDS_ELEMENT = re.compile("([\w]+):([-\.\w]+)")
+
+
+def flow_line_iter(line):
+    """ iterate over flow dump elements.
+    return tuples of (true, element) or (false, remaining element)
+    """
+    # splits by , except for when in a (). Actions element was not
+    # split properly but we don't need it.
+    rc = []
+
+    element = ""
+    paren_count = 0
+
+    for ch in line:
+        if (ch == '('):
+            paren_count += 1
+        elif (ch == ')'):
+            paren_count -= 1
+
+        if (ch == ' '):
+            # ignore white space.
+            continue
+        elif ((ch == ',') and (paren_count == 0)):
+            rc.append(element)
+            element = ""
+        else:
+            element += ch
+
+    if (paren_count):
+        raise ValueError(line)
+    else:
+        if (len(element) > 0):
+            rc.append(element)
+    return rc
+
+
+def flow_line_compound_parse(compound):
+    """ Parse compound element
+    for example
+    src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03
+    which is in
+    eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)
+    """
+    result = {}
+    for element in flow_line_iter(compound):
+        match = FIELDS_CMPND_ELEMENT.search(element)
+        if (match):
+            key = match.group(1)
+            value = match.group(2)
+            result[key] = value
+
+        match = FIELDS_CMPND.search(element)
+        if (match):
+            key = match.group(1)
+            value = match.group(2)
+            result[key] = flow_line_compound_parse(value)
+            continue
+
+    if (len(result.keys()) == 0):
+        return compound
+    return result
+
+
+def flow_line_split(line):
+    """ Convert a flow dump line into ([fields], [stats], actions) tuple.
+    Where fields and stats are lists.
+    This function relies on a the following ovs-dpctl dump-flow
+    output characteristics:
+    1. The dumpe flow line consists of a list of frame fields, list of stats
+       and action.
+    2. list of frame fields, each stat and action field are delimited by ', '.
+    3. That all other non stat field are not delimited by ', '.
+
+    """
+
+    results = re.split(', ', line)
+
+    (field, stats, action) = (results[0], results[1:-1], results[-1])
+
+    fields = flow_line_iter(field)
+    return (fields, stats, action)
+
+
+def elements_to_dict(elements):
+    """ Convert line to a hierarchy of dictionaries. """
+    result = {}
+    for element in elements:
+        match = FIELDS_CMPND.search(element)
+        if (match):
+            key = match.group(1)
+            value = match.group(2)
+            result[key] = flow_line_compound_parse(value)
+            continue
+
+        match = FIELDS_ELEMENT.search(element)
+        if (match):
+            key = match.group(1)
+            value = match.group(2)
+            result[key] = value
+        else:
+            raise ValueError("can't parse >%s<" % element)
+    return result
+
+
+# pylint: disable-msg=R0903
+class SumData(object):
+    """ Interface that all data going into SumDb must implement.
+    Holds the flow field and its corresponding count, total packets,
+    total bytes and calculates average.
+
+    __repr__ is used as key into SumData singleton.
+    __str__ is used as human readable output.
+    """
+
+    def __init__(self, field_type, field, packets, flow_bytes, key):
+        # Count is the number of lines in the dump-flow log.
+        self.field_type = field_type
+        self.field = field
+        self.count = 1
+        self.packets = int(packets)
+        self.bytes = int(flow_bytes)
+        self.key = key
+
+    def decrement(self, decr_packets, decr_bytes, decr_count):
+        """ Decrement content to calculate delta from previous flow sample."""
+        self.packets -= decr_packets
+        self.bytes -= decr_bytes
+        self.count -= decr_count
+
+    def __iadd__(self, other):
+        """ Add two objects. """
+
+        if (self.key != other.key):
+            raise ValueError("adding two unrelated types")
+
+        self.count += other.count
+        self.packets += other.packets
+        self.bytes += other.bytes
+        return self
+
+    def __isub__(self, other):
+        """ Decrement two objects. """
+
+        if (self.key != other.key):
+            raise ValueError("adding two unrelated types")
+
+        self.count -= other.count
+        self.packets -= other.packets
+        self.bytes -= other.bytes
+        return self
+
+    def __getattr__(self, name):
+        """ Handle average. """
+        if (name == "average"):
+            if (self.packets == 0):
+                return float(0.0)
+            else:
+                return float(self.bytes) / float(self.packets)
+        raise AttributeError(name)
+
+    def __str__(self):
+        """ Used for debugging. """
+        return "%s %s %s %s" % (self.field, self.count,
+                                   self.packets, self.bytes)
+
+    def __repr__(self):
+        """ Used as key in the FlowDB table. """
+        return self.key
+
+
+def flow_aggregate(fields_dict, stats_dict):
+    """ Search for content in a line.
+    Passed the flow port of the dump-flows plus the current stats consisting
+    of packets, bytes, etc
+    """
+    result = []
+
+    for output_format in OUTPUT_FORMAT:
+        field = fields_dict.get(output_format.field_type, None)
+        if (field):
+            obj = output_format.generator(output_format.field_type,
+                                          field, stats_dict)
+            result.append(obj)
+
+    return result
+
+
+def flows_read(ihdl, flow_db):
+    """ read flow content from ihdl and insert into flow_db. """
+
+    done = False
+    while (not done):
+        line = ihdl.readline()
+        if (len(line) == 0):
+            # end of input
+            break
+
+        try:
+            flow_db.flow_line_add(line)
+        except ValueError, arg:
+            logging.error(arg)
+
+    return flow_db
+
+
+def get_terminal_size():
+    """
+    return column width and height of the terminal
+    """
+    for fd_io in [0, 1, 2]:
+        try:
+            result = struct.unpack('hh',
+                                   fcntl.ioctl(fd_io, termios.TIOCGWINSZ,
+                                               '1234'))
+        except IOError:
+            result = None
+            continue
+
+    if (result is None or result == (0, 0)):
+        # Maybe we can't get the width. In that case assume (25, 80)
+        result = (25, 80)
+
+    return result
+
+##
+# Content derived from:
+# http://getpython3.com/diveintopython3/your-first-python-program.html#divingin
+##
+SUFFIXES = {1000: ['KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
+            1024: ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']}
+
+
+def approximate_size(size, a_kilobyte_is_1024_bytes=True):
+    """Convert a file size to human-readable form.
+
+    Keyword arguments:
+    size -- file size in bytes
+    a_kilobyte_is_1024_bytes -- if True (default), use multiples of 1024
+                                    if False, use multiples of 1000
+
+    Returns: string
+
+    """
+    size = float(size)
+    if size < 0:
+        raise ValueError('number must be non-negative')
+
+    if (a_kilobyte_is_1024_bytes):
+        multiple = 1024
+    else:
+        multiple = 1000
+    for suffix in SUFFIXES[multiple]:
+        size /= multiple
+        if size < multiple:
+            return "%.1f %s" % (size, suffix)
+
+    raise ValueError('number too large')
+
+
+##
+# End copied content
+##
+class ColMeta:
+    """ Concepts about columns. """
+    def __init__(self, sortable, width):
+        self.sortable = sortable
+        self.width = width
+
+
+class RowMeta:
+    """ How to render rows. """
+    def __init__(self, label, fmt):
+        self.label = label
+        self.fmt = fmt
+
+
+def fmt_packet(obj, width):
+    """ Provide a string for packets that is appropriate for output."""
+    return str(obj.packets).rjust(width)
+
+
+def fmt_count(obj, width):
+    """ Provide a string for average that is appropriate for output."""
+    return str(obj.count).rjust(width)
+
+
+def fmt_avg(obj, width):
+    """ Provide a string for average that is appropriate for output."""
+    return str(int(obj.average)).rjust(width)
+
+
+def fmt_field(obj, width):
+    """ truncate really long flow and insert ellipses to help make it
+    clear.
+    """
+
+    ellipses = " ... "
+    value = obj.field
+    if (len(obj.field) > width):
+        value = value[:(width - len(ellipses))] + ellipses
+    return value.ljust(width)
+
+
+def fmt_bytes(obj, width):
+    """ Provide a string for average that is appropriate for output."""
+    if (len(str(obj.bytes)) <= width):
+        value = str(obj.bytes)
+    else:
+        value = approximate_size(obj.bytes)
+    return value.rjust(width)
+
+
+def title_center(value, width):
+    """ Center a column title."""
+    return value.upper().center(width)
+
+
+def title_rjust(value, width):
+    """ Right justify a column title. """
+    return value.upper().rjust(width)
+
+
+def column_picker(order, obj):
+    """ return the column as specified by order. """
+    if (order == 1):
+        return obj.count
+    elif (order == 2):
+        return obj.packets
+    elif (order == 3):
+        return obj.bytes
+    elif (order == 4):
+        return obj.average
+    else:
+        raise ValueError("order outside of range %s" % order)
+
+
+class Render:
+    """ Renders flow data. """
+    def __init__(self, console_width):
+        """ Calculate column widths taking into account changes in format."""
+
+        self._start_time = datetime.datetime.now()
+
+        self._cols = [ColMeta(False, 0),
+                      ColMeta(True, Columns.VALUE_WIDTH),
+                      ColMeta(True, Columns.VALUE_WIDTH),
+                      ColMeta(True, Columns.VALUE_WIDTH),
+                      ColMeta(True, Columns.VALUE_WIDTH)]
+        self._console_width = console_width
+        self.console_width_set(console_width)
+
+        # Order in this array dictate the order of the columns.
+        # The 0 width for the first entry is a place holder. This is
+        # dynamically calculated. The first column is special. We need a
+        # way to indicate which field are presented.
+        self._descs = [RowMeta("", title_rjust),
+                       RowMeta("", title_rjust),
+                       RowMeta("", title_rjust),
+                       RowMeta("", title_rjust),
+                       RowMeta("", title_rjust)]
+        self._column_sort_select = 0
+        self.column_select_event()
+
+        self._titles = [
+            RowMeta(Columns.FIELDS, title_center),
+            RowMeta(Columns.COUNT, title_rjust),
+            RowMeta(Columns.PACKETS, title_rjust),
+            RowMeta(Columns.BYTES, title_rjust),
+            RowMeta(Columns.AVERAGE, title_rjust)
+        ]
+
+        self._datas = [
+            RowMeta(None, fmt_field),
+            RowMeta(None, fmt_count),
+            RowMeta(None, fmt_packet),
+            RowMeta(None, fmt_bytes),
+            RowMeta(None, fmt_avg)
+            ]
+
+        ##
+        # _field_types hold which fields are displayed in the field
+        # column, with the keyword all implying all fields.
+        ##
+        self._field_types = ["all"] + [ii.field_type for ii in OUTPUT_FORMAT]
+
+        ##
+        # The default is to show all field types.
+        ##
+        self._field_type_select = -1
+        self.field_type_toggle()
+
+    def _field_type_select_get(self):
+        """ Return which field type to display. """
+        return self._field_types[self._field_type_select]
+
+    def field_type_toggle(self):
+        """ toggle which field types to show. """
+        self._field_type_select += 1
+        if (self._field_type_select >= len(self._field_types)):
+            self._field_type_select = 0
+        value = Columns.FIELDS + " (%s)" % self._field_type_select_get()
+        self._titles[0].label = value
+
+    def column_select_event(self):
+        """ Handles column select toggle. """
+
+        self._descs[self._column_sort_select].label = ""
+        for _ in range(len(self._cols)):
+            self._column_sort_select += 1
+            if (self._column_sort_select >= len(self._cols)):
+                self._column_sort_select = 0
+
+            # Now look for the next sortable column
+            if (self._cols[self._column_sort_select].sortable):
+                break
+        self._descs[self._column_sort_select].label = "DESC"
+
+    def console_width_set(self, console_width):
+        """ Adjust the output given the new console_width. """
+        self._console_width = console_width
+
+        spaces = len(self._cols) - 1
+        ##
+        # Calculating column width can be tedious but important. The
+        # flow field value can be long. The goal here is to dedicate
+        # fixed column space for packets, bytes, average and counts. Give the
+        # remaining space to the flow column. When numbers get large
+        # transition output to output generated by approximate_size which
+        # limits output to ###.# XiB in other words 9 characters.
+        ##
+        # At this point, we know the maximum length values. We may
+        # truncate the flow column to get everything to fit.
+        self._cols[0].width = 0
+        values_max_length = sum([ii.width for ii in self._cols]) + spaces
+        flow_max_length = console_width - values_max_length
+        self._cols[0].width = flow_max_length
+
+    def format(self, flow_db):
+        """ shows flows based on --script parameter."""
+
+        rc = []
+        ##
+        # Top output consists of
+        # Title
+        # Column title (2 rows)
+        # data
+        # statistics and status
+
+        ##
+        # Title
+        ##
+        rc.append("Flow Summary".center(self._console_width))
+
+        stats = " Total: %(flow_total)s  errors: %(flow_errors)s " % \
+                  flow_db.flow_stats_get()
+        accumulate = flow_db.accumulate_get()
+        if (accumulate):
+            stats += "Accumulate: on "
+        else:
+            stats += "Accumulate: off "
+
+        duration = datetime.datetime.now() - self._start_time
+        stats += "Duration: %s " % str(duration)
+        rc.append(stats.ljust(self._console_width))
+
+        ##
+        # 2 rows for columns.
+        ##
+        # Indicate which column is in descending order.
+        rc.append(" ".join([ii.fmt(ii.label, col.width)
+                            for (ii, col) in zip(self._descs, self._cols)]))
+
+        rc.append(" ".join([ii.fmt(ii.label, col.width)
+                         for (ii, col) in zip(self._titles, self._cols)]))
+
+        ##
+        # Data.
+        ##
+        for dd in flow_db.field_values_in_order(self._field_type_select_get(),
+                                                self._column_sort_select):
+            rc.append(" ".join([ii.fmt(dd, col.width)
+                                for (ii, col) in zip(self._datas,
+                                                     self._cols)]))
+
+        return rc
+
+
+def curses_screen_begin():
+    """ begin curses screen control. """
+    stdscr = curses.initscr()
+    curses.cbreak()
+    curses.noecho()
+    stdscr.keypad(1)
+    return stdscr
+
+
+def curses_screen_end(stdscr):
+    """ end curses screen control. """
+    curses.nocbreak()
+    stdscr.keypad(0)
+    curses.echo()
+    curses.endwin()
+
+
+class FlowDB:
+    """ Implements live vs accumulate mode.
+
+    Flows are stored as key value pairs. The key consists of the content
+    prior to stat fields. The value portion consists of stats in a dictionary
+    form.
+
+    @ \todo future add filtering here.
+    """
+    def __init__(self, accumulate):
+        self._accumulate = accumulate
+        self._error_count = 0
+        # Values are (stats, last update time.)
+        # The last update time is used for aging.
+        self._flow_lock = threading.Lock()
+        # This dictionary holds individual flows.
+        self._flows = {}
+        # This dictionary holds aggregate of flow fields.
+        self._fields = {}
+
+    def accumulate_get(self):
+        """ Return the current accumulate state. """
+        return self._accumulate
+
+    def accumulate_toggle(self):
+        """ toggle accumulate flow behavior. """
+        self._accumulate = not self._accumulate
+
+    def begin(self):
+        """ Indicate the beginning of processing flow content.
+        if accumulate is false clear current set of flows. """
+
+        if (not self._accumulate):
+            self._flow_lock.acquire()
+            try:
+                self._flows.clear()
+            finally:
+                self._flow_lock.release()
+            self._fields.clear()
+
+    def flow_line_add(self, line):
+        """ Split a line from a ovs-dpctl dump-flow into key and stats.
+        The order of the content in the flow should be:
+        - flow content
+        - stats for the flow
+        - actions
+
+        This method also assumes that the dump flow output does not
+        change order of fields of the same flow.
+        """
+
+        line = line.rstrip("\n")
+        (fields, stats, _) = flow_line_split(line)
+
+        try:
+            fields_dict = elements_to_dict(fields)
+
+            if (len(fields_dict) == 0):
+                raise ValueError("flow fields are missing %s", line)
+
+            stats_dict = elements_to_dict(stats)
+            if (len(stats_dict) == 0):
+                raise ValueError("statistics are missing %s.", line)
+
+            ##
+            # In accumulate mode, the Flow database can reach 10,000's of
+            # persistent flows. The interaction of the script with this many
+            # flows is too slow. Instead, delta are sent to the flow_db
+            # database allow incremental changes to be done in O(m) time
+            # where m is the current flow list, instead of iterating over
+            # all flows in O(n) time where n is the entire history of flows.
+            key = ",".join(fields)
+
+            self._flow_lock.acquire()
+            try:
+                (stats_old_dict, _) = self._flows.get(key, (None, None))
+            finally:
+                self._flow_lock.release()
+
+            self.flow_event(fields_dict, stats_old_dict, stats_dict)
+
+        except ValueError, arg:
+            logging.error(arg)
+            self._error_count += 1
+            raise
+
+        self._flow_lock.acquire()
+        try:
+            self._flows[key] = (stats_dict, datetime.datetime.now())
+        finally:
+            self._flow_lock.release()
+
+    def decay(self, decayTimeInSeconds):
+        """ Decay content. """
+        now = datetime.datetime.now()
+        for (key, value) in self._flows.items():
+            (stats_dict, updateTime) = value
+            delta = now - updateTime
+
+            if (delta.seconds > decayTimeInSeconds):
+                self._flow_lock.acquire()
+                try:
+                    del self._flows[key]
+
+                    fields_dict = elements_to_dict(flow_line_iter(key))
+                    matches = flow_aggregate(fields_dict, stats_dict)
+                    for match in matches:
+                        self.field_dec(match)
+
+                finally:
+                    self._flow_lock.release()
+
+    def flow_stats_get(self):
+        """ Return statistics in a form of a dictionary. """
+        rc = None
+        self._flow_lock.acquire()
+        try:
+            rc = {"flow_total": len(self._flows),
+                  "flow_errors": self._error_count}
+        finally:
+            self._flow_lock.release()
+        return rc
+
+    def field_types_get(self):
+        """ Return the set of types stored in the singleton. """
+        types = set((ii.field_type for ii in self._fields.values()))
+        return types
+
+    def field_add(self, data):
+        """ Collect dump-flow data to sum number of times item appears. """
+        current = self._fields.get(repr(data), None)
+        if (current is None):
+            current = copy.copy(data)
+        else:
+            current += data
+        self._fields[repr(current)] = current
+
+    def field_dec(self, data):
+        """ Collect dump-flow data to sum number of times item appears. """
+        current = self._fields.get(repr(data), None)
+        if (current is None):
+            raise ValueError("decrementing field missing %s" % repr(data))
+
+        current -= data
+        self._fields[repr(current)] = current
+        if (current.count == 0):
+            del self._fields[repr(current)]
+
+    def field_values_in_order(self, field_type_select, column_order):
+        """ Return a list of items in order maximum first. """
+        values = self._fields.values()
+        if (field_type_select != "all"):
+            # If a field type other than "all" then reduce the list.
+            values = [ii for ii in values
+                      if (ii.field_type == field_type_select)]
+        values = [(column_picker(column_order, ii), ii) for ii in values]
+        values.sort(key=operator.itemgetter(0))
+        values.reverse()
+        values = [ii[1] for ii in values]
+        return values
+
+    def flow_event(self, fields_dict, stats_old_dict, stats_new_dict):
+        """ Receives new flow information. """
+
+        # In order to avoid processing every flow at every sample
+        # period, changes in flow packet count is used to determine the
+        # delta in the flow statistics. This delta is used in the call
+        # to self.decrement prior to self.field_add
+
+        if (stats_old_dict is None):
+            # This is a new flow
+            matches = flow_aggregate(fields_dict, stats_new_dict)
+            for match in matches:
+                self.field_add(match)
+        else:
+            old_packets = int(stats_old_dict.get("packets", 0))
+            new_packets = int(stats_new_dict.get("packets", 0))
+            if (old_packets == new_packets):
+                # ignore. same data.
+                pass
+            else:
+                old_bytes = stats_old_dict.get("bytes", 0)
+                # old_packets != new_packets
+                # if old_packets > new_packets then we end up decrementing
+                # packets and bytes.
+                matches = flow_aggregate(fields_dict, stats_new_dict)
+                for match in matches:
+                    match.decrement(int(old_packets), int(old_bytes), 1)
+                    self.field_add(match)
+
+
+class DecayThread(threading.Thread):
+    """ Periodically call flow database to see if any flows are old. """
+    def __init__(self, flow_db, interval):
+        """ Start decay thread. """
+        threading.Thread.__init__(self)
+
+        self._interval = max(1, interval)
+        self._min_interval = min(1, interval / 10)
+        self._flow_db = flow_db
+        self._event = threading.Event()
+        self._running = True
+
+        self.daemon = True
+
+    def run(self):
+        """ Worker thread which handles decaying accumulated flows. """
+
+        while(self._running):
+            self._event.wait(self._min_interval)
+            if (self._running):
+                self._flow_db.decay(self._interval)
+
+    def stop(self):
+        """ Stop thread. """
+        self._running = False
+        self._event.set()
+        ##
+        # Give the calling thread time to terminate but not too long.
+        # this thread is a daemon so the application will terminate if
+        # we timeout during the join. This is just a cleaner way to
+        # release resources.
+        self.join(2.0)
+
+
+def flow_top_command(stdscr, render, flow_db):
+    """ Handle input while in top mode. """
+    ch = stdscr.getch()
+    ##
+    # Any character will restart sampling.
+    if (ch == ord('h')):
+        # halt output.
+        ch = stdscr.getch()
+        while (ch == -1):
+            ch = stdscr.getch()
+
+    if (ch == ord('s')):
+        # toggle which column sorts data in descending order.
+        render.column_select_event()
+    elif (ch == ord('a')):
+        flow_db.accumulate_toggle()
+    elif (ch == ord('f')):
+        render.field_type_toggle()
+    elif (ch == ord(' ')):
+        # resample
+        pass
+
+    return ch
+
+
+def decay_timer_start(flow_db, accumulateDecay):
+    """ If accumulateDecay greater than zero then start timer. """
+    if (accumulateDecay > 0):
+        decay_timer = DecayThread(flow_db, accumulateDecay)
+        decay_timer.start()
+        return decay_timer
+    else:
+        return None
+
+
+def flows_top(args):
+    """ handles top like behavior when --script is not specified. """
+
+    flow_db = FlowDB(args.accumulate)
+    render = Render(0)
+
+    decay_timer = decay_timer_start(flow_db, args.accumulateDecay)
+    lines = []
+
+    try:
+        stdscr = curses_screen_begin()
+        try:
+            ch = 'X'
+            #stdscr.nodelay(1)
+            stdscr.timeout(args.delay)
+
+            while (ch != ord('q')):
+                flow_db.begin()
+
+                try:
+                    ihdl = top_input_get(args)
+                    try:
+                        flows_read(ihdl, flow_db)
+                    finally:
+                        ihdl.close()
+                except OSError, arg:
+                    logging.critical(arg)
+                    break
+
+                (console_height, console_width) = stdscr.getmaxyx()
+                render.console_width_set(console_width)
+
+                output_height = console_height - 1
+                line_count = range(output_height)
+                line_output = render.format(flow_db)
+                lines = zip(line_count, line_output[:output_height])
+
+                stdscr.erase()
+                for (count, line) in lines:
+                    stdscr.addstr(count, 0, line[:console_width])
+                stdscr.refresh()
+
+                ch = flow_top_command(stdscr, render, flow_db)
+
+        finally:
+            curses_screen_end(stdscr)
+    except KeyboardInterrupt:
+        pass
+    if (decay_timer):
+        decay_timer.stop()
+
+    # repeat output
+    for (count, line) in lines:
+        print line
+
+
+def flows_script(args):
+    """ handles --script option. """
+
+    flow_db = FlowDB(args.accumulate)
+    flow_db.begin()
+
+    if (args.flowFiles is None):
+        logging.info("reading flows from stdin")
+        ihdl = os.fdopen(sys.stdin.fileno(), 'r', 0)
+        try:
+            flow_db = flows_read(ihdl, flow_db)
+        finally:
+            ihdl.close()
+    else:
+        for flowFile in args.flowFiles:
+            logging.info("reading flows from %s", flowFile)
+            ihdl = open(flowFile, "r")
+            try:
+                flow_db = flows_read(ihdl, flow_db)
+            finally:
+                ihdl.close()
+
+    (_, console_width) = get_terminal_size()
+    render = Render(console_width)
+
+    for line in render.format(flow_db):
+        print line
+
+
+def main():
+    """ Return 0 on success or 1 on failure.
+
+    Algorithm
+    There are four stages to the process ovs-dpctl dump-flow content.
+    1. Retrieve current input
+    2. store in FlowDB and maintain history
+    3. Iterate over FlowDB and aggregating stats for each flow field
+    4. present data.
+
+    Retrieving current input is currently trivial, the ovs-dpctl dump-flow
+    is called. Future version will have more elaborate means for collecting
+    dump-flow content. FlowDB returns all data as in the form of a hierarchical
+    dictionary. Input will vary.
+
+    In the case of accumulate mode, flows are not purged from the FlowDB
+    manager. Instead at the very least, merely the latest statistics are
+    kept. In the case, of live output the FlowDB is purged prior to sampling
+    data.
+
+    Aggregating results requires identify flow fields to aggregate out
+    of the flow and summing stats.
+
+    """
+    args = args_get()
+
+    try:
+        if (args.top):
+            flows_top(args)
+        else:
+            flows_script(args)
+    except KeyboardInterrupt:
+        return 1
+    return 0
+
+if __name__ == '__main__':
+    sys.exit(main())
+elif __name__ == 'ovs-dpctl-top':
+    # pylint: disable-msg=R0915
+
+    ##
+    # Test case beyond this point.
+    # pylint: disable-msg=R0904
+    class TestsuiteFlowParse(unittest.TestCase):
+        """
+        parse flow into hierarchy of dictionaries.
+        """
+        def test_flow_parse(self):
+            """ test_flow_parse. """
+            line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\
+                   "dst=33:33:00:01:00:03),eth_type(0x86dd),"\
+                   "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\
+                   "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\
+                   "udp(src=61252,dst=5355), packets:1, bytes:92, "\
+                   "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\
+                   "38,41,44,47,50,53,56,59,62,65"
+
+            (fields, stats, _) = flow_line_split(line)
+            flow_dict = elements_to_dict(fields + stats)
+            self.assertEqual(flow_dict["eth"]["src"], "00:50:56:b4:4e:f8")
+            self.assertEqual(flow_dict["eth"]["dst"], "33:33:00:01:00:03")
+            self.assertEqual(flow_dict["ipv6"]["src"],
+                             "fe80::55bf:fe42:bc96:2812")
+            self.assertEqual(flow_dict["ipv6"]["dst"], "ff02::1:3")
+            self.assertEqual(flow_dict["packets"], "1")
+            self.assertEqual(flow_dict["bytes"], "92")
+
+            line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\
+                   "dst=33:33:00:01:00:03),eth_type(0x86dd),"\
+                   "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\
+                   "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\
+                   "udp(src=61252,dst=5355), packets:1, bytes:92, "\
+                   "used:-0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\
+                   "38,41,44,47,50,53,56,59,62,65"
+
+            (fields, stats, _) = flow_line_split(line)
+            flow_dict = elements_to_dict(fields + stats)
+            self.assertEqual(flow_dict["used"], "-0.703s")
+            self.assertEqual(flow_dict["packets"], "1")
+            self.assertEqual(flow_dict["bytes"], "92")
+
+        def test_flow_sum(self):
+            """ test_flow_sum. """
+            line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\
+                   "dst=33:33:00:01:00:03),eth_type(0x86dd),"\
+                   "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\
+                   "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\
+                   "udp(src=61252,dst=5355), packets:2, bytes:92, "\
+                   "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\
+                   "38,41,44,47,50,53,56,59,62,65"
+
+            (fields, stats, _) = flow_line_split(line)
+            stats_dict = elements_to_dict(stats)
+            fields_dict = elements_to_dict(fields)
+            ##
+            # Test simple case of one line.
+            flow_db = FlowDB(False)
+            matches = flow_aggregate(fields_dict, stats_dict)
+            for match in matches:
+                flow_db.field_add(match)
+
+            flow_types = flow_db.field_types_get()
+            expected_flow_types = ["eth", "eth_type", "udp", "in_port", "ipv6"]
+            self.assert_(len(flow_types) == len(expected_flow_types))
+            for flow_type in flow_types:
+                self.assertTrue(flow_type in expected_flow_types)
+
+            for flow_type in flow_types:
+                sum_value = flow_db.field_values_in_order("all", 1)
+                self.assert_(len(sum_value) == 5)
+                self.assert_(sum_value[0].packets == 2)
+                self.assert_(sum_value[0].count == 1)
+                self.assert_(sum_value[0].bytes == 92)
+
+            ##
+            # Add line again just to see counts go up.
+            matches = flow_aggregate(fields_dict, stats_dict)
+            for match in matches:
+                flow_db.field_add(match)
+
+            flow_types = flow_db.field_types_get()
+            self.assert_(len(flow_types) == len(expected_flow_types))
+            for flow_type in flow_types:
+                self.assertTrue(flow_type in expected_flow_types)
+
+            for flow_type in flow_types:
+                sum_value = flow_db.field_values_in_order("all", 1)
+                self.assert_(len(sum_value) == 5)
+                self.assert_(sum_value[0].packets == 4)
+                self.assert_(sum_value[0].count == 2)
+                self.assert_(sum_value[0].bytes == 2 * 92)
+
+        def test_assoc_list(self):
+            """ test_assoc_list. """
+            line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\
+                   "dst=33:33:00:01:00:03),eth_type(0x86dd),"\
+                   "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\
+                   "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\
+                   "udp(src=61252,dst=5355), packets:2, bytes:92, "\
+                   "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\
+                   "38,41,44,47,50,53,56,59,62,65"
+
+            valid_flows = [
+                'eth_type(0x86dd)',
+                'udp(dst=5355)',
+                'in_port(4)',
+                'ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3)',
+                'eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)'
+                ]
+
+            (fields, stats, _) = flow_line_split(line)
+            stats_dict = elements_to_dict(stats)
+            fields_dict = elements_to_dict(fields)
+
+            ##
+            # Test simple case of one line.
+            flow_db = FlowDB(False)
+            matches = flow_aggregate(fields_dict, stats_dict)
+            for match in matches:
+                flow_db.field_add(match)
+
+            for sum_value in flow_db.field_values_in_order("all", 1):
+                assoc_list = Columns.assoc_list(sum_value)
+                for item in assoc_list:
+                    if (item[0] == "fields"):
+                        self.assertTrue(item[1] in valid_flows)
+                    elif (item[0] == "packets"):
+                        self.assertTrue(item[1] == 2)
+                    elif (item[0] == "count"):
+                        self.assertTrue(item[1] == 1)
+                    elif (item[0] == "average"):
+                        self.assertTrue(item[1] == 46.0)
+                    elif (item[0] == "bytes"):
+                        self.assertTrue(item[1] == 92)
+                    else:
+                        raise ValueError("unknown %s", item[0])
+
+        def test_human_format(self):
+            """ test_assoc_list. """
+
+            self.assertEqual(approximate_size(0.0), "0.0 KiB")
+            self.assertEqual(approximate_size(1024), "1.0 KiB")
+            self.assertEqual(approximate_size(1024 * 1024), "1.0 MiB")
+            self.assertEqual(approximate_size((1024 * 1024) + 100000),
+                             "1.1 MiB")
+            value = (1024 * 1024 * 1024) + 100000000
+            self.assertEqual(approximate_size(value), "1.1 GiB")
+
+        def test_flow_line_split(self):
+            """ Splitting a flow line is not trivial.
+            There is no clear delimiter. Comma is used liberally."""
+            expected_fields = ["in_port(4)",
+                            "eth(src=00:50:56:b4:4e:f8,dst=33:33:00:01:00:03)",
+                            "eth_type(0x86dd)",
+                           "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"
+                           "label=0,proto=17,tclass=0,hlimit=1,frag=no)",
+                           "udp(src=61252,dst=5355)"]
+            expected_stats = ["packets:2", "bytes:92", "used:0.703s"]
+            expected_actions = "actions:3,8,11,14,17,20,23,26,29,32,35," \
+                               "38,41,44,47,50,53,56,59,62,65"
+
+            line = "in_port(4),eth(src=00:50:56:b4:4e:f8,"\
+                   "dst=33:33:00:01:00:03),eth_type(0x86dd),"\
+                   "ipv6(src=fe80::55bf:fe42:bc96:2812,dst=ff02::1:3,"\
+                   "label=0,proto=17,tclass=0,hlimit=1,frag=no),"\
+                   "udp(src=61252,dst=5355), packets:2, bytes:92, "\
+                   "used:0.703s, actions:3,8,11,14,17,20,23,26,29,32,35,"\
+                   "38,41,44,47,50,53,56,59,62,65"
+
+            (fields, stats, actions) = flow_line_split(line)
+
+            self.assertEqual(fields, expected_fields)
+            self.assertEqual(stats, expected_stats)
+            self.assertEqual(actions, expected_actions)
+
+        def test_accumulate_decay(self):
+            """ test_accumulate_decay: test accumulated decay. """
+            lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b,"
+                     "dst=ff:ff:ff:ff:ff:ff),"
+                     "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255,"
+                     "tip=10.24.104.230/255.255.255.255,op=1/0xff,"
+                     "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00,"
+                     "tha=00:00:00:00:00:00/00:00:00:00:00:00), "
+                     "packets:1, bytes:120, used:0.004s, actions:1"]
+
+            flow_db = FlowDB(True)
+            flow_db.begin()
+            flow_db.flow_line_add(lines[0])
+
+            # Make sure we decay
+            time.sleep(4)
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1)
+            flow_db.decay(1)
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0)
+
+            flow_db.flow_line_add(lines[0])
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1)
+            flow_db.decay(30)
+            # Should not be deleted.
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1)
+
+            flow_db.flow_line_add(lines[0])
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 1)
+            timer = decay_timer_start(flow_db, 2)
+            time.sleep(10)
+            self.assertEqual(flow_db.flow_stats_get()["flow_total"], 0)
+            timer.stop()
+
+        def test_accumulate(self):
+            """ test_accumulate test that FlowDB supports accumulate. """
+
+            lines = ["in_port(1),eth(src=00:50:56:4f:dc:3b,"
+                     "dst=ff:ff:ff:ff:ff:ff),"
+                     "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255,"
+                     "tip=10.24.104.230/255.255.255.255,op=1/0xff,"
+                     "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00,"
+                     "tha=00:00:00:00:00:00/00:00:00:00:00:00), "
+                     "packets:1, bytes:120, used:0.004s, actions:1",
+                     "in_port(2),"
+                     "eth(src=68:ef:bd:25:ef:c0,dst=33:33:00:00:00:66),"
+                     "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::,"
+                     "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0,"
+                     "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029), "
+                     "packets:2, bytes:5026, used:0.348s, actions:1",
+                     "in_port(1),eth(src=ee:ee:ee:ee:ee:ee,"
+                     "dst=ff:ff:ff:ff:ff:ff),"
+                     "eth_type(0x0806),arp(sip=10.24.105.107/255.255.255.255,"
+                     "tip=10.24.104.230/255.255.255.255,op=1/0xff,"
+                     "sha=00:50:56:4f:dc:3b/00:00:00:00:00:00,"
+                     "tha=00:00:00:00:00:00/00:00:00:00:00:00), packets:2, "
+                     "bytes:240, used:0.004s, actions:1"]
+
+            lines = [
+                "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1",
+                "in_port(2),eth_type(0x0806), packets:2, bytes:126, actions:1",
+                "in_port(1),eth_type(0x0806), packets:2, bytes:240, actions:1",
+                "in_port(1),eth_type(0x0800), packets:1, bytes:120, actions:1",
+                "in_port(1),eth_type(0x0800), packets:2, bytes:240, actions:1",
+                "in_port(1),eth_type(0x0806), packets:1, bytes:120, actions:1",
+                ]
+
+            # Turn on accumulate.
+            flow_db = FlowDB(True)
+            flow_db.begin()
+
+            flow_db.flow_line_add(lines[0])
+
+            # Test one flow exist.
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 1)
+            self.assertEqual(in_ports[0].bytes, 120)
+            self.assertEqual(in_ports[0].count, 1)
+
+            # simulate another sample
+            # Test two different flows exist.
+            flow_db.begin()
+            flow_db.flow_line_add(lines[1])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 1)
+            self.assertEqual(in_ports[0].bytes, 120)
+            self.assertEqual(in_ports[0].count, 1)
+
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 126)
+            self.assertEqual(in_ports[0].count, 1)
+
+            # Test first flow increments packets.
+            flow_db.begin()
+            flow_db.flow_line_add(lines[2])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 240)
+            self.assertEqual(in_ports[0].count, 1)
+
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 126)
+            self.assertEqual(in_ports[0].count, 1)
+
+            # Test third flow but with the same in_port(1) as the first flow.
+            flow_db.begin()
+            flow_db.flow_line_add(lines[3])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 3)
+            self.assertEqual(in_ports[0].bytes, 360)
+            self.assertEqual(in_ports[0].count, 2)
+
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 126)
+            self.assertEqual(in_ports[0].count, 1)
+
+            # Third flow has changes.
+            flow_db.begin()
+            flow_db.flow_line_add(lines[4])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 4)
+            self.assertEqual(in_ports[0].bytes, 480)
+            self.assertEqual(in_ports[0].count, 2)
+
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 126)
+            self.assertEqual(in_ports[0].count, 1)
+
+            # First flow reset.
+            flow_db.begin()
+            flow_db.flow_line_add(lines[5])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 3)
+            self.assertEqual(in_ports[0].bytes, 360)
+            self.assertEqual(in_ports[0].count, 2)
+
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(2)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 2)
+            self.assertEqual(in_ports[0].bytes, 126)
+            self.assertEqual(in_ports[0].count, 1)
+
+        def test_parse_character_errors(self):
+            """ test_parsing errors.
+            The flow parses is purposely loose. Its not designed to validate
+            input. Merely pull out what it can but there are situations
+            that a parse error can be detected.
+            """
+
+            lines = ["complete garbage",
+                     "in_port(2),eth(src=68:ef:bd:25:ef:c0,"
+                     "dst=33:33:00:00:00:66),"
+                     "eth_type(0x86dd),ipv6(src=fe80::6aef:bdff:fe25:efc0/::,"
+                     "dst=ff02::66/::,label=0/0,proto=17/0xff,tclass=0xe0/0,"
+                     "hlimit=255/0,frag=no/0),udp(src=2029,dst=2029),"
+                     "packets:2,bytes:5026,actions:1"]
+
+            flow_db = FlowDB(False)
+            flow_db.begin()
+            for line in lines:
+                try:
+                    flow_db.flow_line_add(line)
+                except ValueError:
+                    # We want an exception. That is how we know we have
+                    # correctly found a simple parsing error. We are not
+                    # looking to validate flow output just catch simple issues.
+                    continue
+                self.assertTrue(False)
+
+        def test_tunnel_parsing(self):
+            """ test_tunnel_parsing test parse flows with tunnel. """
+            lines = [
+                "tunnel(tun_id=0x0,src=192.168.1.1,dst=192.168.1.10,"
+                "tos=0x0,ttl=64,flags(key)),in_port(1),"
+                "eth(src=9e:40:f5:ef:ec:ee,dst=01:23:20:00:00:30),"
+                "eth_type(0x8902), packets:6, bytes:534, used:0.128s, "
+                "actions:userspace(pid=4294962691,slow_path(cfm))"
+                ]
+            flow_db = FlowDB(False)
+            flow_db.begin()
+            flow_db.flow_line_add(lines[0])
+            sum_values = flow_db.field_values_in_order("all", 1)
+            in_ports = [ii for ii in sum_values if (repr(ii) == "in_port(1)")]
+            self.assertEqual(len(in_ports), 1)
+            self.assertEqual(in_ports[0].packets, 6)
+            self.assertEqual(in_ports[0].bytes, 534)
+            self.assertEqual(in_ports[0].count, 1)
+
+        def test_flow_multiple_paren(self):
+            """ test_flow_multiple_paren. """
+            line = "tunnel(tun_id=0x0,src=192.168.1.1,flags(key)),in_port(2)"
+            valid = ["tunnel(tun_id=0x0,src=192.168.1.1,flags(key))",
+                     "in_port(2)"]
+            rc = flow_line_iter(line)
+            self.assertEqual(valid, rc)
+
+        def test_to_network(self):
+            """ test_to_network test ipv4_to_network and ipv6_to_network. """
+            ipv4s = [
+                ("192.168.0.1", "192.168.0.1"),
+                ("192.168.0.1/255.255.255.255", "192.168.0.1"),
+                ("192.168.0.1/255.255.255.0", "192.168.0.0"),
+                ("192.168.0.1/255.255.0.0", "192.168.0.0"),
+                ("192.168.0.1/255.0.0.0", "192.0.0.0"),
+                ("192.168.0.1/0.0.0.0", "0.0.0.0"),
+                ("10.24.106.230/255.255.255.255", "10.24.106.230"),
+                ("10.24.106.230/255.255.255.0", "10.24.106.0"),
+                ("10.24.106.0/255.255.255.0", "10.24.106.0"),
+                ("10.24.106.0/255.255.252.0", "10.24.104.0")
+                ]
+
+            ipv6s = [
+                ("1::192:168:0:1", "1::192:168:0:1"),
+                ("1::192:168:0:1/1::ffff:ffff:ffff:ffff", "1::192:168:0:1"),
+                ("1::192:168:0:1/1::ffff:ffff:ffff:0", "1::192:168:0:0"),
+                ("1::192:168:0:1/1::ffff:ffff:0:0", "1::192:168:0:0"),
+                ("1::192:168:0:1/1::ffff:0:0:0", "1::192:0:0:0"),
+                ("1::192:168:0:1/1::0:0:0:0", "1::"),
+                ("1::192:168:0:1/::", "::")
+                ]
+
+            for (ipv4_test, ipv4_check) in ipv4s:
+                self.assertEqual(ipv4_to_network(ipv4_test), ipv4_check)
+
+            for (ipv6_test, ipv6_check) in ipv6s:
+                self.assertEqual(ipv6_to_network(ipv6_test), ipv6_check)
index 0516d1b..98b47b8 100644 (file)
@@ -60,10 +60,7 @@ static bool zero_statistics;
 /* --may-create: Allow mod-flows command to create a new flow? */
 static bool may_create;
 
-/* -m, --more: Output verbosity.
- *
- * So far only undocumented commands honor this option, so we don't document
- * the option itself. */
+/* -m, --more: Increase output verbosity. */
 static int verbosity;
 
 static const struct command *get_all_commands(void);
@@ -170,14 +167,16 @@ usage(void)
            "  dump-dps                 display names of all datapaths\n"
            "  show                     show basic info on all datapaths\n"
            "  show DP...               show basic info on each DP\n"
-           "  dump-flows DP            display flows in DP\n"
-           "  add-flow DP FLOW ACTIONS add FLOW with ACTIONS to DP\n"
-           "  mod-flow DP FLOW ACTIONS change FLOW actions to ACTIONS in DP\n"
-           "  del-flow DP FLOW         delete FLOW from DP\n"
-           "  del-flows DP             delete all flows from DP\n"
+           "  dump-flows [DP]          display flows in DP\n"
+           "  add-flow [DP] FLOW ACTIONS add FLOW with ACTIONS to DP\n"
+           "  mod-flow [DP] FLOW ACTIONS change FLOW actions to ACTIONS in DP\n"
+           "  del-flow [DP] FLOW         delete FLOW from DP\n"
+           "  del-flows [DP]             delete all flows from DP\n"
            "Each IFACE on add-dp, add-if, and set-if may be followed by\n"
            "comma-separated options.  See ovs-dpctl(8) for syntax, or the\n"
-           "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n",
+           "Interface table in ovs-vswitchd.conf.db(5) for an options list.\n"
+           "For COMMAND dump-flows, add-flow, mod-flow, del-flow and\n"
+           "del-flows, DP is optional if there is only one datapath.\n",
            program_name, program_name);
     vlog_usage();
     printf("\nOptions for show and mod-flow:\n"
@@ -858,7 +857,7 @@ dpctl_del_flow(int argc, char *argv[])
     ofpbuf_init(&mask, 0);
     run(odp_flow_from_string(key_s, NULL, &key, &mask), "parsing flow key");
 
-    dp_name = argc == 2 ? xstrdup(argv[1]) : get_one_dp();
+    dp_name = argc == 3 ? xstrdup(argv[1]) : get_one_dp();
     run(parsed_dpif_open(dp_name, false, &dpif), "opening datapath");
     free(dp_name);
 
@@ -1063,6 +1062,7 @@ dpctl_normalize_actions(int argc, char *argv[])
     ofpbuf_init(&odp_actions, 0);
     run(odp_actions_from_string(argv[2], &port_names, &odp_actions),
         "odp_actions_from_string");
+    simap_destroy(&port_names);
 
     if (verbosity) {
         ds_clear(&s);
index 3141eff..526e12c 100644 (file)
@@ -190,7 +190,7 @@ statistics are printed for all queues on \fIport\fR; if only
 \fIport\fR is omitted, then statistics are printed for \fIqueue\fR on
 every port where it exists.
 .
-.SS "OpenFlow 1.1+ Switch Management Commands"
+.SS "OpenFlow 1.1+ Group Table Commands"
 .
 The following commands work only with switches that support OpenFlow
 1.1 or later.  Because support for OpenFlow 1.1 and later is still
@@ -238,6 +238,42 @@ Send to controller.  (This is how an OpenFlow 1.0 switch always
 handles packets that do not match any flow in the last table.)
 .RE
 .
+.SS "OpenFlow 1.3+ Switch Meter Table Commands"
+.
+These commands manage the meter table in an OpenFlow switch.  In each
+case, \fImeter\fR specifies a meter entry in the format described in
+\fBMeter Syntax\fR, below.
+.
+.PP
+OpenFlow 1.3 introduced support for meters, so these commands only
+work with switches that support OpenFlow 1.3 or later.  The caveats
+described for groups in the previous section also apply to meters.
+.
+.IP "\fBadd\-meter \fIswitch meter\fR"
+Add a meter entry to \fIswitch\fR's tables. The \fImeter\fR syntax is
+described in section \fBMeter Syntax\fR, below.
+.
+.IP "\fBmod\-meter \fIswitch meter\fR"
+Modify an existing meter.
+.
+.IP "\fBdel\-meters \fIswitch\fR"
+.IQ "\fBdel\-meter \fIswitch\fR [\fImeter\fR]"
+Delete entries from \fIswitch\fR's meter table.  \fImeter\fR can specify
+a single meter with syntax \fBmeter=\fIid\fR, or all meters with syntax
+\fBmeter=all\fR.
+.
+.IP "\fBdump\-meters \fIswitch\fR"
+.IQ "\fBdump\-meter \fIswitch\fR [\fImeter\fR]"
+Print meter configuration.  \fImeter\fR can specify a single meter with
+syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR.
+.
+.IP "\fBmeter\-stats \fIswitch\fR [\fImeter\fR]"
+Print meter statistics.  \fImeter\fR can specify a single meter with
+syntax \fBmeter=\fIid\fR, or all meters with syntax \fBmeter=all\fR.
+.
+.IP "\fBmeter\-features \fIswitch\fR"
+Print meter features.
+.
 .SS "OpenFlow Switch Flow Table Commands"
 .
 These commands manage the flow table in an OpenFlow switch.  In each
@@ -1322,6 +1358,12 @@ field will be replaced with the corresponding bit from \fIvalue\fR. Both
 \fIvalue\fR and \fImask\fR are 64-bit values that are decimal by default; use
 a \fB0x\fR prefix to specify them in hexadecimal.
 .
+.IP \fBmeter\fR:\fImeter_id\fR
+Apply the \fImeter_id\fR before any other actions. If a meter band rate is
+exceeded, the packet may be dropped, or modified, depending on the meter
+band type. See the description of the \fBMeter Table Commands\fR, above,
+for more details.
+.
 .IP \fBgoto_table\fR:\fItable\fR
 Indicates the next table in the process pipeline.
 .
@@ -1580,6 +1622,62 @@ This or the \fBwatch_port\fR field is required
 for groups whose \fBtype\fR is \fBff\fR or \fBfast_failover\fR.
 .RE
 .
+.SS "Meter Syntax"
+.PP
+The meter table commands accept an argument that describes a meter.
+Such meter descriptions comprise a series \fIfield\fB=\fIvalue\fR
+assignments, separated by commas or white space.
+(Embedding spaces into a group description normally requires
+quoting to prevent the shell from breaking the description into
+multiple arguments.). Unless noted otherwise only the last instance
+of each field is honoured.
+.PP
+.IP \fBmeter=\fIid\fR
+The integer meter id of the meter.
+When this field is specified in \fBdel-meter\fR, \fBdump-meter\fR, or
+\fBmeter-stats\fR, the keyword "all" may be used to designate all meters.
+.
+This field is required, exept for \fBmeter-stats\fR, which dumps all stats
+when this field is not specified.
+
+.IP \fBkbps\fR
+.IQ \fBpktps\fR
+The unit for the meter band rate parameters, either kilobits per second, or
+packets per second, respectively.  One of these must be specified.  The burst
+size unit corresponds to the rate unit by dropping the "per second", i.e.,
+burst is in units of kilobits or packets, respectively.
+
+.IP \fBburst\fR
+Specify burst size for all bands, or none of them, if this flag is not given.
+
+.IP \fBstats\fR
+Collect meter and band statistics.
+
+.IP \fBbands\fR=\fIband_parameters\fR
+The \fBadd-meter\fR and \fBmod-meter\fR commands require at least one
+band specification. Bands must appear after all other fields.
+.RS
+.IP \fBtype=\fItype\fR
+The type of the meter band.  This keyword starts a new band specification.
+Each band specifies a rate above which the band is to take some action. The
+action depends on the band type.  If multiple bands' rate is exceeded, then
+the band with the highest rate among the exceeded bands is selected.
+The following keywords designate the allowed
+meter band types:
+.RS
+.IP \fBdrop\fR
+Drop packets exceeding the band's rate limit.
+.RE
+.
+.IP "The other \fIband_parameters\fR are:"
+.IP \fBrate=\fIvalue\fR
+The relative rate limit for this band, in kilobits per second or packets per
+second, depending on the meter flags defined above.
+.IP \fBburst_size=\fIport\fR
+The maximum burst allowed for the band.  If unspecified, the switch is free to
+select some reasonable value depending on it's configuration.
+.RE
+.
 .SH OPTIONS
 .TP
 \fB\-\-strict\fR
index a5a5c02..c2cc1f6 100644 (file)
@@ -309,6 +309,14 @@ usage(void)
            "  dump-group-features SWITCH  print group features\n"
            "  dump-groups SWITCH          print group description\n"
            "  dump-group-stats SWITCH [GROUP]  print group statistics\n"
+           "  add-meter SWITCH METER      add meter described by METER\n"
+           "  mod-meter SWITCH METER      modify specific METER\n"
+           "  del-meter SWITCH METER      delete METER\n"
+           "  del-meters SWITCH           delete all meters\n"
+           "  dump-meter SWITCH METER     print METER configuration\n"
+           "  dump-meters SWITCH          print all meter configuration\n"
+           "  meter-stats SWITCH [METER]  print meter statistics\n"
+           "  meter-features SWITCH       print meter features\n"
            "\nFor OpenFlow switches and controllers:\n"
            "  probe TARGET                probe whether TARGET is up\n"
            "  ping TARGET [N]             latency of N-byte echos\n"
@@ -1113,13 +1121,12 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
 static void
 ofctl_flow_mod(int argc, char *argv[], uint16_t command)
 {
-    enum ofputil_protocol usable_protocols;
-
     if (argc > 2 && !strcmp(argv[2], "-")) {
         ofctl_flow_mod_file(argc, argv, command);
     } else {
         struct ofputil_flow_mod fm;
         char *error;
+        enum ofputil_protocol usable_protocols;
 
         error = parse_ofp_flow_mod_str(&fm, argc > 2 ? argv[2] : "", command,
                                        &usable_protocols);
@@ -2471,6 +2478,102 @@ ofctl_diff_flows(int argc OVS_UNUSED, char *argv[])
         exit(2);
     }
 }
+
+static void
+ofctl_meter_mod__(const char *bridge, const char *str, int command)
+{
+    struct ofputil_meter_mod mm;
+    struct vconn *vconn;
+    enum ofputil_protocol protocol;
+    enum ofputil_protocol usable_protocols;
+    enum ofp_version version;
+
+    if (str) {
+        char *error;
+        error = parse_ofp_meter_mod_str(&mm, str, command, &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
+    } else {
+        usable_protocols = OFPUTIL_P_OF13_UP;
+        mm.command = command;
+        mm.meter.meter_id = OFPM13_ALL;
+    }
+
+    protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
+    version = ofputil_protocol_to_ofp_version(protocol);
+    transact_noreply(vconn, ofputil_encode_meter_mod(version, &mm));
+    vconn_close(vconn);
+}
+
+static void
+ofctl_meter_request__(const char *bridge, const char *str,
+                      enum ofputil_meter_request_type type)
+{
+    struct ofputil_meter_mod mm;
+    struct vconn *vconn;
+    enum ofputil_protocol usable_protocols;
+    enum ofputil_protocol protocol;
+    enum ofp_version version;
+
+    if (str) {
+        char *error;
+        error = parse_ofp_meter_mod_str(&mm, str, -1, &usable_protocols);
+        if (error) {
+            ovs_fatal(0, "%s", error);
+        }
+    } else {
+        usable_protocols = OFPUTIL_P_OF13_UP;
+        mm.meter.meter_id = OFPM13_ALL;
+    }
+
+    protocol = open_vconn_for_flow_mod(bridge, &vconn, usable_protocols);
+    version = ofputil_protocol_to_ofp_version(protocol);
+    transact_noreply(vconn, ofputil_encode_meter_request(version,
+                                                         type,
+                                                         mm.meter.meter_id));
+    vconn_close(vconn);
+}
+
+
+static void
+ofctl_add_meter(int argc OVS_UNUSED, char *argv[])
+{
+    ofctl_meter_mod__(argv[1], argv[2], OFPMC13_ADD);
+}
+
+static void
+ofctl_mod_meter(int argc OVS_UNUSED, char *argv[])
+{
+    ofctl_meter_mod__(argv[1], argv[2], OFPMC13_MODIFY);
+}
+
+static void
+ofctl_del_meters(int argc, char *argv[])
+{
+    ofctl_meter_mod__(argv[1], argc > 2 ? argv[2] : NULL, OFPMC13_DELETE);
+}
+
+static void
+ofctl_dump_meters(int argc, char *argv[])
+{
+    ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
+                          OFPUTIL_METER_CONFIG);
+}
+
+static void
+ofctl_meter_stats(int argc, char *argv[])
+{
+    ofctl_meter_request__(argv[1], argc > 2 ? argv[2] : NULL,
+                          OFPUTIL_METER_STATS);
+}
+
+static void
+ofctl_meter_features(int argc OVS_UNUSED, char *argv[])
+{
+    ofctl_meter_request__(argv[1], NULL, OFPUTIL_METER_FEATURES);
+}
+
 \f
 /* Undocumented commands for unit testing. */
 
@@ -3186,6 +3289,14 @@ static const struct command all_commands[] = {
     { "del-flows", 1, 2, ofctl_del_flows },
     { "replace-flows", 2, 2, ofctl_replace_flows },
     { "diff-flows", 2, 2, ofctl_diff_flows },
+    { "add-meter", 2, 2, ofctl_add_meter },
+    { "mod-meter", 2, 2, ofctl_mod_meter },
+    { "del-meter", 2, 2, ofctl_del_meters },
+    { "del-meters", 1, 1, ofctl_del_meters },
+    { "dump-meter", 2, 2, ofctl_dump_meters },
+    { "dump-meters", 1, 1, ofctl_dump_meters },
+    { "meter-stats", 1, 2, ofctl_meter_stats },
+    { "meter-features", 1, 1, ofctl_meter_features },
     { "packet-out", 4, INT_MAX, ofctl_packet_out },
     { "dump-ports", 1, 2, ofctl_dump_ports },
     { "dump-ports-desc", 1, 1, ofctl_dump_ports_desc },
index 4b42ec7..5fd5b3b 100644 (file)
           <ul>
             <li>
               To ensure that ovs-vswitchd has enough time to pull statistics
-              from the datapath, the minimum
-              <ref column="other_config" key="cfm_interval"/> is 500ms.
+              from the datapath, the fault detection interval is set to
+              3.5 * MAX(<ref column="other_config" key="cfm_interval"/>, 500)
+              ms.
             </li>
 
             <li>
index 4d3b8fa..87efd88 100644 (file)
@@ -426,6 +426,7 @@ exit 0
 /usr/sbin/ovsdb-server
 /usr/bin/ovs-appctl
 /usr/bin/ovs-dpctl
+/usr/bin/ovs-dpctl-top
 /usr/bin/ovs-ofctl
 /usr/bin/ovs-parse-backtrace
 /usr/bin/ovs-pcap
@@ -443,6 +444,7 @@ exit 0
 /usr/share/man/man8/ovs-bugtool.8.gz
 /usr/share/man/man8/ovs-ctl.8.gz
 /usr/share/man/man8/ovs-dpctl.8.gz
+/usr/share/man/man8/ovs-dpctl-top.8.gz
 /usr/share/man/man8/ovs-ofctl.8.gz
 /usr/share/man/man8/ovs-parse-backtrace.8.gz
 /usr/share/man/man1/ovs-pcap.1.gz